Если кто обратил внимание, предделители у таймеров Пики имеют тип float. То есть, вроде как можно подстраивать частоту плавно :) Давно меня занимала эта фича, только не на чем было проверить. А тут наконец приехал мне с Али логиканализатор на 100 МГц. Настроил PWM, макс значение счета 5 - соответственно частота должна получится 133 / 5 = 26.6 МГц. Установил предделитель 1.064, чтобы, значит, получить на выходе ровно 25 МГц
26.6 / 1.064 = 25 МГц
Чуда не произошло. Как я и предполагал, частота не настраивается плавно. 25 МГц из 26 делается примерно так - десяток импульсов на частоте 26.6 и один на частоте 20. Потом снова десяток на 26....
Ну так millis() в AVR так же работает. И 55-мс прерывания в DOS-сессии Windows - тоже.
25 МГц - это значит, 25000000 импульсов в секунду. Разве нет?
улыбнул значит )))
Да, есть проблема, объединил скетчи сканера wifi и двухтонального генератора и,
(выход синхронизации работает как и положено на 225 герц
а вот выход частоты 1800 пропускает импульсы, попробую перевести на другой пин...
делители float бывают только в сказках... из-за высокой частоты тактирования скорее всего на низких генерируемых частотах выходная частота будет весьма близка с требуемой...есть вероятность...
признавайтесь, кто SPI исследовал?
Не могу разобраться с установкой скорости в библиотеке Adafruit - не устанавливается.
SPI тянет от hardwarespi.h, там она по умолчанию 4 мегагерца, но при применении библиотеки Adafruit_ST7735 она явно сильно ниже
По осциллограмме ниже видно, что обмен идёт в 16 битном режиме на скорости 50 килогерц?
Откуда это берётся???
Для w5500 ставил 1 МГц, не работало, пришлось прицепить логанализатор, реально spi 1мгц было. Большую частоту не пробовал.
И при чем тут скорость передачи данных? Смотрим частоту clk пина spi, по нему и судим о скорости.
Ну, если я правильно вижу, в ячейке 5 мксек, два импульса в ячейке, аккурат 4мгц, не? Upd: не, Или 200 ns это ширина квадратика?
частота по символу H, что такое D пока не знаю (может время нарастания-спада сигнала), особо осциллограф не изучал, выставил исследование SPI и смотрю, обрабатывает не быстро иногда висит висит но так на экран ничего и не выведет, короче это не ригол )))
4 мегагерца это 250 наносекунд, 1 мегагерц - 1 микросекунда это у меня въелось )))
Работа с модемом SIM800L
Инициализация + чтение SMS в PDU формате, в том числе составных.
Пока только в 7и битной кодировке (латиница), кириллица позже будет.
#ifndef SIM800_PICO_H
#define SIM800_PICO_H
/// modem SIM800L UART
#define UART_ID uart0
#define BAUD_RATE 9600
#define DATA_BITS 8
#define STOP_BITS 1
#define PARITY UART_PARITY_NONE
// We are using pins 12 and 13, but see the GPIO function select table in the
// datasheet for information on which other pins can be used.
#define UART_TX_PIN 12
#define UART_RX_PIN 13
#define pinResetModem 23 // пин сброса модема
#define max_size_modem_rx_buf 2048 // приемный буфер данных от модема
#define period_wait_start_modem 15000UL // время ожидания модема после включения
#define period_wait_registration 120000UL // время ожидания регистрации модема в сети оператора
#define period_read_sms 10000UL // как часто с модема читать СМС
enum tModemGlobalStatus {modemStBegin = 0, modemStInit, modemStReg, modemStReboot, modemStReady};
void modemInit(void); // инициализируем пины
void modemReset(void); // сброс модема
void modemMainLoop(void);
union t_TP_MTIandCo {
struct {
unsigned TP_MTI:2;
unsigned TP_MMS:1;
unsigned res1:1;
unsigned res2:1;
unsigned TP_SRI:1;
unsigned TP_UDHI:1;
unsigned TP_RP:1;
};
unsigned char TP_MTI_Value;
};
#endif /* SIM800_PICO_H */
#include <stdio.h>
#include "pico/stdlib.h"
#include "sim800pico.h"
#include "millpico.h"
#include <string.h>
#include <ctype.h>
tModemGlobalStatus currentGlobalStatusModem = modemStBegin;
unsigned char modemRXbuf[max_size_modem_rx_buf];
unsigned short modemPosBuf;
unsigned char modemStepWork;
unsigned long modemTimerWork = 0UL;
unsigned char operatorName[16];
unsigned char operatorCSQ;
signed short findPosSubstringFromBuf(unsigned char * inStr) {
char * pStr = strstr((const char *) modemRXbuf, (const char *) inStr);
if (pStr) return (inStr - ((unsigned char*)pStr));
else return -1;
}
bool findOKRNfromBuf(void) {
if (findPosSubstringFromBuf((unsigned char *)"OK\r\n") >= 0) return true;
else return false;
}
void initModemUART(void) { // sim800 uart init
// Set up our UART with the required speed.
uart_init(UART_ID, BAUD_RATE);
// Set the TX and RX pins by using the function select on the GPIO
// Set datasheet for more information on function select
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
// Use some the various UART functions to send out data
// In a default system, printf will also output via the default UART
// Send out a character without any conversions
//uart_putc_raw(UART_ID, 'A');
// Send out a character but do CR/LF conversions
//uart_putc(UART_ID, 'B');
// Send out a string, with CR/LF conversions
//uart_puts(UART_ID, " Hello, UART!\n");
// Actually, we want a different speed
// The call will return the actual baud rate selected, which will be as close as
// possible to that requested
int __unused actual = uart_set_baudrate(UART_ID, BAUD_RATE);
// Set UART flow control CTS/RTS, we don't want these, so turn them off
uart_set_hw_flow(UART_ID, true, true);
// Set our data format
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
// Turn off FIFO's - we want to do this character by character
uart_set_fifo_enabled(UART_ID, true);
}
void modemInit(void) { // инициализируем пины и все остальное
gpio_init(pinResetModem); // sim800 reset
gpio_set_dir(pinResetModem, GPIO_IN); // sim800 reset
gpio_put(pinResetModem, 0); // sim800 reset
initModemUART(); // sim800 uart init
}
void modemReset(void) { // сброс модема
gpio_set_dir(pinResetModem, GPIO_OUT);
sleep_ms(115);
gpio_set_dir(pinResetModem, GPIO_IN);
}
void modemClearRXbuf(void) {
modemPosBuf = 0;
memset(modemRXbuf, '\0', max_size_modem_rx_buf);
//modemRXbuf[modemPosBuf] = '\0';
}
void modemSendCMDandShowAndTimerStep(unsigned char * inStr) {
uart_puts(UART_ID, (const char*)inStr);
printf("%s\n", inStr);
modemTimerWork = millis(); // сбросим таймер
++modemStepWork; // На след шаг
}
bool processCREG(void) { // парсинг ответа модема на команду AT+CREG
char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CREG: ");
if (p1) {
if (p1 >= ((const char *) modemRXbuf)) {
char * p2 = strchr((const char *) modemRXbuf, ',');
if (p2) {
if (p2 > p1) {
unsigned char br = *(p2+1);
if ((br == '1') || (br == '5')) return true;
}
}
}
}
return false;
}
void processCOPS(void) { // парсинг ответа модема на команду AT+COPS
char * p1 = strstr((const char *) modemRXbuf, (const char *) "+COPS: ");
if (p1) {
if (p1 >= ((const char *) modemRXbuf)) {
char * p2 = strchr((const char *) modemRXbuf, ',');
if (p2) {
char * p3 = strchr((p2+1), ',');
if (p3) {
if ((p3>p2) && ((*(p3+1)) == '"')) {
char * p4 = strchr((p3+2), '"');
if ((p4) && (p4>(p3+2))) {
unsigned char lenP = p4-p3-2;
for (unsigned char i=0; i<lenP; ++i) operatorName[i] = *(p3+2+i);
operatorName[lenP] = '\0';
}
}
}
}
}
}
}
void processCSQ(void) { // парсинг ответа модема на команду AT+CSQ
char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CSQ: ");
if (p1) {
if (p1 >= ((const char *) modemRXbuf)) {
if (isdigit(*(p1+6))) {
unsigned char tC = (*(p1+6)) - '0';
if (isdigit(*(p1+7))) {
operatorCSQ = tC*10+((*(p1+7)) - '0');
} else {
operatorCSQ = tC;
}
}
}
}
}
bool modemCMDstReg(bool inLine) {
static unsigned long timerRegistration;
static bool goodCREG;
switch (modemStepWork) {
case 0: {
timerRegistration = millis();
goodCREG = false;
++modemStepWork;
break;
}
case 1: {
modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CREG?\r"); // проверяем зарегестрировался ли модем в сети
break;
}
case 2: {
if ((millis() - timerRegistration) >= period_wait_registration) return false;
if ((millis() - modemTimerWork) >= 5000UL) { // больше 5 сек нет ответа
modemTimerWork = millis(); // сбросим таймер
modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации
} else {
if (inLine) { // если пришла строка
if (processCREG()) goodCREG = true; // парсим строку - если зарегестрировались - ставим флаг
if (findOKRNfromBuf()) { // ищем ответ ОК
if (goodCREG) { // если успешно зарегестрировались
modemSendCMDandShowAndTimerStep((unsigned char *)"AT+COPS?\r"); // получить режим подключения к оператору и имя оператора
operatorName[0] = 0; // очищаем имя оператора
} else { // если так и не смогли зарегестрироваться
modemTimerWork = millis(); // сбросим таймер
modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации
}
}
}
}
break;
}
case 3: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine) { // если пришла строка
processCOPS();
if (findOKRNfromBuf()) {
modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSQ\r"); // запрос качества связи
operatorCSQ = 0;
}
}
break;
}
case 4: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine) { // если пришла строка
processCSQ();
if (findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMGF=0\r"); // SMS в PDU формате
}
break;
}
case 5: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) { // успешно закончили начальную инициализацию модема
currentGlobalStatusModem = modemStReady;
modemStepWork = 0;
}
break;
}
case 25: {
if ((millis() - modemTimerWork) >= 5000UL) modemStepWork = 1; // прождали и опять вернулись на запрос регистрации
break;
}
default: {}
}
return true;
}
bool modemCMDstInit(bool inLine) {
switch (modemStepWork) {
case 0: { // ждем пока модем прочухается
if ((millis() - modemTimerWork) >= period_wait_start_modem) ++modemStepWork; // На след шаг
break;
}
case 1: {
modemSendCMDandShowAndTimerStep((unsigned char *)"ATZ\r"); // проверка связи с модемом AT / сброс настроек на default - ATZ
break;
}
case 2: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATE0\r"); // отключение(ATE0) ЭХО, т е модем в обратку не дублирует
break;
}
case 3: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATV1\r"); // развернуты подробный ответ от модема
break;
}
case 4: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMEE=2\r"); // вывод подробных описаний ошибок
break;
}
case 5: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CLIP=0\r"); // отключение АОН
break;
}
case 6: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATS0=0\r"); // модем не берет трубку при входящем звонке
break;
}
case 7: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSCLK=0\r"); // отключение работы энергосберегающего режима через пин DTR
break;
}
case 8: {
if ((millis() - modemTimerWork) >= 5000UL) return false;
if (inLine && findOKRNfromBuf()) { // закончилась начальная инициализация
modemStepWork = 0;
currentGlobalStatusModem = modemStReg; // переходим в режим регистрации оператора
}
break;
}
default: {}
}
return true;
}
enum tSMSmode {smsStNone = 0, smsStGetList, smsStProcessing};
unsigned char smsGetByteFromTwoChars(char * inChars) {
unsigned char c1 = *inChars;
unsigned char outByte;
if (isdigit(c1)) outByte = c1 - '0'; else outByte = c1 - 55;
outByte *= 16;
c1 = *(inChars + 1);
if (isdigit(c1)) outByte += c1 - '0'; else outByte += c1 - 55;
return outByte;
}
unsigned char smsTextMsg7bit(char * begStr, char * endStr, char * outBuf, unsigned char msgLen) {
unsigned char outCharPos = 0; // позиция в выходном массиве
char * currCharPos = begStr; // указатель на символ откуда берем байт
unsigned char prevDataByte = 0; // предыдущий входящий байт
while ((currCharPos < endStr) && (outCharPos < msgLen)) {
unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт
currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных
unsigned char idxCurrByte = outCharPos % 8; // индекс сдвига входящего байта 0...7
if (idxCurrByte) {
unsigned char lowBits = prevDataByte >> (8 - idxCurrByte);
*(outBuf+outCharPos) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits;
++outCharPos;
if (idxCurrByte == 7) {
*(outBuf+outCharPos) = inDataByte;
++outCharPos;
}
} else {
*(outBuf+outCharPos) = inDataByte & 0x7F;
++outCharPos;
}
prevDataByte = inDataByte; // сохраним для след байта данных
}
return outCharPos;
}
unsigned char smsTextMsg7bitWithOffset(char * begStr, char * endStr, char * outBuf, unsigned char msgLen, unsigned short inOffset) {
char * currCharPos = begStr; // указатель на символ откуда берем байт
unsigned char changeOutOffset = 0; // байт сдвига выходного буфера
if (inOffset) { // если первый байт сдвигается
*(outBuf) = smsGetByteFromTwoChars(currCharPos) >> inOffset; // кладем в выходной буфер со сдвигом
currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных
changeOutOffset = 1; // будем на 1 символ больше класть в выходной буфер
}
unsigned char outCharPos = 0; // позиция в выходном массиве
unsigned char prevDataByte = 0; // предыдущий входящий байт
unsigned char idxCurrByte = 0; // индекс сдвига входящего байта 0...6
while ((currCharPos < endStr) && ((outCharPos+changeOutOffset) < msgLen)) {
unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт
currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных
if (idxCurrByte) {
unsigned char lowBits = prevDataByte >> (8 - idxCurrByte);
*(outBuf+outCharPos+changeOutOffset) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits;
++outCharPos;
if (idxCurrByte == 6) {
*(outBuf+outCharPos+changeOutOffset) = inDataByte >> 1;
++outCharPos;
}
} else {
*(outBuf+outCharPos+changeOutOffset) = inDataByte & 0x7F;
++outCharPos;
}
prevDataByte = inDataByte; // сохраним для след байта данных
++idxCurrByte;
if (idxCurrByte >= 7) idxCurrByte = 0; // индекс по входящим байтам 0...6
}
return outCharPos+changeOutOffset;
}
// http://hardisoft.ru/soft/samodelkin-soft/poluchenie-i-dekodirovanie-sms-soobshhenij-v-formate-pdu/
void modemSMSprocessingLineText(void) {
static unsigned char smsDataIndex; // индекс смс
static unsigned char smsDataLenField; // длина данных из заголовка
char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CMGL: ");
if ((p1) && (p1 >= ((const char *) modemRXbuf))) {
// вытаскиваем индекс СМСки
if (isdigit(*(p1+7))) {
smsDataIndex = (*(p1+7)) - '0';
if (isdigit(*(p1+8))) {
smsDataIndex = smsDataIndex * 10 +(*(p1+8)) - '0';
}
printf("smsDataIndex %d\r\n", smsDataIndex);
// вытаскиваем длину поля данных
p1 = strchr((const char *) (p1 + 7), ',');
if (p1) {
char * p2 = strchr((const char *) (p1 + 1), ',');
if (p2) {
p1 = strchr((const char *) (p2 + 1), ',');
if ((p1) && (p1>p2) && (isdigit(*(p1+1))) && (isdigit(*(p1+2)))) {
smsDataLenField = ((*(p1+1)) - '0') * 10 + ((*(p1+2)) - '0');
if (isdigit(*(p1+3))) {
smsDataLenField *= 10;
smsDataLenField += (*(p1+3)) - '0';
}
printf("smsDataLenField %d\r\n", smsDataLenField);
return; // строка успешно разпозналась - выходим
}
}
}
}
} else if ((smsDataIndex) && (smsDataLenField)) { // след строка уже будет опираться на предыдущие обработанные данные
char * smsPcurrentCharPos = (char*) modemRXbuf; // начало строки с данными
char * smsPendPosLine = strchr(smsPcurrentCharPos, '\r'); // последний символ строки с данными + 1
unsigned short smsDataCharsLenLine = smsPendPosLine - smsPcurrentCharPos; // общая длина строки с данными
if ((smsDataCharsLenLine % 2) == 0) { // количество символов должно быть четным
if ((smsDataCharsLenLine / 2) >= smsDataLenField) { // есть ли заголовок TP-SCA (номер СМС центра)
smsPcurrentCharPos += ((smsDataCharsLenLine/2) - smsDataLenField) * 2; // сдвигаем указатель строки начала данных вправо на длину TP-SCA
t_TP_MTIandCo TP_MTIandCo;
TP_MTIandCo.TP_MTI_Value = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("TP-MTI %d\r\n", TP_MTIandCo.TP_MTI);
printf("TP-MMS %d\r\n", TP_MTIandCo.TP_MMS);
printf("TP-SRI %d\r\n", TP_MTIandCo.TP_SRI);
printf("TP-UDHI %d\r\n", TP_MTIandCo.TP_UDHI);
printf("TP-RP %d\r\n", TP_MTIandCo.TP_RP);
smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт
// адрес отправителя TP-OA
unsigned char lenTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // длина строки отправителя
smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт
unsigned char typeTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // формат строки отправителя
smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт
unsigned char smsSender[16]; // отправитель
switch (typeTP_OA) {
case 0x91: { // международный формат номера
unsigned char senderPos = 0;
while (senderPos < lenTP_OA) {
unsigned char ch1 = * (smsPcurrentCharPos + 1); // первый символ / перевернутая позиция
if (senderPos < lenTP_OA) {
smsSender[senderPos] = ch1; ++senderPos;
}
ch1 = * smsPcurrentCharPos; // второй символ // перевернутая позиция
if (senderPos < lenTP_OA) {
smsSender[senderPos] = ch1; ++senderPos;
}
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
}
smsSender[senderPos] = '\0'; // конец строки
break;
}
default: { // тут остальные форматы номера,которые еще не запрограммированы, например текстовый отправитель, тупо сдвигаем позицию на нужгное кол.байт
unsigned char senderPos = 0;
while (senderPos < lenTP_OA) {
if (senderPos < lenTP_OA) {
smsSender[senderPos] = * smsPcurrentCharPos; ++senderPos;
}
if (senderPos < lenTP_OA) {
smsSender[senderPos] = * (smsPcurrentCharPos + 1); ++senderPos;
}
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
}
smsSender[senderPos] = '\0'; // конец строки
}
}
printf("Sender %s\r\n", smsSender);
// TP-PID (Protocol identifier) – Идентификатор протокола. // не используем - пропускаем
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// TP-DCS (Data coding scheme) – Схема кодирования данных.
// 00 — 7-битная сжатая кодировка, 08 — UCS2, 10 и 18 — то же, только сообщение класса 0, т.е. Flash.
unsigned char TP_DCS = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("TP-DCS %02x\r\n", TP_DCS);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// TP-SCTS (The service centre time stamp) – Штамп времени сервисного центра. Не используем.
smsPcurrentCharPos +=14; // указатель сдвигаем на 7 байт
// TP-UDL (User Data Length) — длина пользовательских данных, включая User Data header, если он есть. Если SMS закодирована 7-битной кодировкой, то данное поле указывает количество символов в сообщении. Если кодировка UCS2 – то поле указывает количество байт в сообщении.
unsigned char TP_UDL = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("TP-UDL %d\r\n", TP_UDL);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// TP-UD (User Data) – Поле пользовательских данных. Здесь находится сам текст сообщения. В случае, если SMS длинная, т.е. разбита на несколько сообщений, данное поле начинается с заголовка TP-UDH. На наличие этого заголовка указывает бит TP-UDHI поля TP-MTI & Co.
unsigned char smsMessage[161]; // текст одного сообщения, по стандарту максимум 160 байт + терминатор
smsMessage[0] = '\0';
if (TP_MTIandCo.TP_UDHI == 0) { // сообщение из одного блока
if ((TP_DCS == 0x00) || (TP_DCS == 0x10)) { // 7и битная кодировка
smsMessage[smsTextMsg7bit(smsPcurrentCharPos, smsPendPosLine, (char *) smsMessage, TP_UDL)] = '\0';
} else if ((TP_DCS == 0x08) || (TP_DCS == 0x18)) { // кодировка UCS2
}
} else { // многоблочное сообщение
unsigned char UDHL = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("UDHL %02x\r\n", UDHL);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// UDH
// IEI Для отправки длинных сообщений всегда равен либо 0х00, либо 0х08. Если 0х00 – то IED1 занимает 1 байт, если 0х08 – то IED1 занимает 2 байта.
unsigned char IEI = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("IEI %02x\r\n", IEI);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// IEDL Длина данных информационного элемента – 3 или 4 байта, в зависимости от длины поля IED1
unsigned char IEDL = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("IEDL %02x\r\n", IEDL);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// IED1 Номер-ссылка. Должен быть одинаков для всех частей сообщения
unsigned short IED1;
if (IEI == 0x00) {
IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
} else {
IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos) * 256;
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
IED1 += smsGetByteFromTwoChars(smsPcurrentCharPos);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
}
printf("IED1 %02x\r\n", IED1);
// IED2 Количество частей в сообщении
unsigned char IED2 = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("IED2 %02x\r\n", IED2);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
// IED3 Порядковый номер части сообщения
unsigned char IED3 = smsGetByteFromTwoChars(smsPcurrentCharPos);
printf("IED3 %02x\r\n", IED3);
smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт
if ((TP_DCS == 0x00) || (TP_DCS == 0x10)) { // 7и битная кодировка
// считаем смещение септета
static unsigned short offset7bitFirstChar = 0;
unsigned short lenBytesWithUDH = TP_UDL*7/8; // длина поля UD с заголовком UDH
unsigned short lenBitsWOUDH = (lenBytesWithUDH - UDHL - 1) * 8; // длина поля с сообщением в битах уже без заголовка
unsigned short count7bitChars = lenBitsWOUDH / 7; // количество целиковых 7и битных символов
if ((IED3 == 1) && (IED2 > 1)) { // считаем для первой части сообщения, для остальных частей он аналогичный
offset7bitFirstChar = lenBitsWOUDH - (count7bitChars * 7); // filler 0-6 бит Наполнитель, который выравнивает text по границе септета
}
smsMessage[smsTextMsg7bitWithOffset(smsPcurrentCharPos, smsPendPosLine, (char *) smsMessage, count7bitChars, offset7bitFirstChar)] = '\0';
} else if ((TP_DCS == 0x08) || (TP_DCS == 0x18)) { // кодировка UCS2
}
}
printf("%s\r\n", smsMessage);
}
}
}
smsDataIndex = 0; // если не распозналась и не обработалась СМСка // или успешно обработалось - обнуляем все что сделали
}
void modemCMDworkSMS(bool inLine) {
static bool firstReadAllSMS = true;
static unsigned long smsReqListTimer = 0;
static unsigned long smsTimerWork;
static tSMSmode smsCurrentMode = smsStNone;
switch (smsCurrentMode) {
case smsStGetList: {
if (firstReadAllSMS) {
uart_puts(UART_ID, (const char*)"AT+CMGL=4,0\r");
printf("%s\n", "AT+CMGL=4,0\r\n");
firstReadAllSMS = false;
} else {
return;
uart_puts(UART_ID, (const char*)"AT+CMGL=0,0\r");
printf("%s\n", "AT+CMGL=0,0\r\n");
}
smsTimerWork = millis();
smsCurrentMode = smsStProcessing;
break;
}
case smsStProcessing: {
if ((millis() - smsTimerWork) >= 20000UL) {
smsReqListTimer = millis();
smsCurrentMode = smsStNone;
} else if (inLine) {
if (findOKRNfromBuf()) {
smsReqListTimer = millis();
smsCurrentMode = smsStNone;
} else {
modemSMSprocessingLineText();
}
}
break;
}
default: { // smsStNone
if ((millis() - smsReqListTimer) >= period_read_sms) smsCurrentMode = smsStGetList;
}
}
}
void modemMainLoop(void) {
switch (currentGlobalStatusModem) {
case modemStBegin: {
modemClearRXbuf();
modemStepWork = 0;
modemTimerWork = millis();
currentGlobalStatusModem = modemStInit;
break;
}
case modemStReboot: {
modemReset();
currentGlobalStatusModem = modemStBegin;
break;
}
default: { // main work by modem
unsigned char br = 0; // переменная куда падает входящий байт из модема
bool flLine = false; // влаг что пришла целиковая строка
while (uart_is_readable(UART_ID)) { // если что то пришло из модема
br = uart_getc(UART_ID); // читаем пришедший байт
if (br) { // если он больше нуля
modemRXbuf[modemPosBuf] = br; // кладем байт в буфер
++modemPosBuf; // сдвигаем указатель на следующую позицию
if (modemPosBuf >= max_size_modem_rx_buf) {
modemPosBuf = 0; // начнем с начала буфера при переполнении
flLine = true; // флаг что конец строки
break; // выходим из цикла
}
if ((br == '\r') || (br == '\n') || ((br >= ' ') && (br < 0x7F))) { // если символ печатный или конец строки
printf("%c", br); // выводим как есть
} else { // если не печатный
printf("x%02x", br); // выводим его шестнадцатиричное представление в лог
}
if (((br == '\n') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\r')) ||
((br == '\r') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\n'))) { // пришел конец строки
flLine = true; // флаг что конец строки
modemRXbuf[modemPosBuf] = '\0'; // прописываем терминатор/окончание строки
break; // выходим из цикла
}
}
}
// начинаем запуск всех обработчиков данных
switch (currentGlobalStatusModem) {
case modemStInit: {
if (!modemCMDstInit(flLine)) currentGlobalStatusModem = modemStReboot;
break;
}
case modemStReg: {
if (!modemCMDstReg(flLine)) currentGlobalStatusModem = modemStReboot;
break;
}
case modemStReady: {
modemCMDworkSMS(flLine);
break;
}
default: {}
}
// остальные обработчики
// если обработали целиковую строку
if (flLine) modemClearRXbuf(); // обнуляем массив
// end main work
}
}
tight_loop_contents();
}
cmsis это стандарт arm, он есть везде и он отвратительный.
почему это ? если более менее знаком с даташитом устройства для которого пишешь , то cmsis вполне себе понятен интуитивно .или вы другое имеете ввиду ?
Привет, подскажите что я делаю не так компилирую программу в main.uf2, перезагружаю с зажатым boot, закидываю на появившийся диск программу, она запускается и работает, но если перезагрузить плату программа больше не запускается. Только если опять закидывать через boot.
Нет, это неправильно, так быть не должно.
Где то в инете лежит что то типа clean.uf2, программа, которая чистить флэш и прочее в МК. Попробуйте его. Или другую прогу скомпилировать, понять так же себя МК ведёт или нет.
Нет, это неправильно, так быть не должно. Где то в инете лежит что то типа clean.uf2, программа, которая чистить флэш и прочее в МК. Попробуйте его. Или другую прогу скомпилировать, понять так же себя МК ведёт или нет.
Так не будет работать, т.к. используются не один бит (с помощью побитовых операций сдвига и логического сложения формируется управляющее слово из 24 бит).
Возьмите плату и светодиод и попробуйте. У меня она есть со встроенным светодиодом, код примера протестирован , ошибки исправлены.
В ошибке даже стрелочка нарисована, откуда берет.
хм,
оно?
ок, спасибо, пошел искать путь к этим переменным....
Интересный модуль...и по цене и по возможностям...
Самое приятное, с моей точки зрения:простота работы с периферией по сравнению с stm32 cmsis. Sdk хорОш!
SPI работает с полтычка, это точно, по PWM библиотека тоже простая...реальная замена ардуино нано и по цене и по простоте использования
простота = она же беднота.... Режимов таймера с гулькин нос
Для каждой задачи нужен свой МК, нужны таймеры - берём stm32.
отож!!! STM32WB55VG c памятью побольше )))
Чтобы было информативно, закину определения для PWM
Если кто обратил внимание, предделители у таймеров Пики имеют тип float. То есть, вроде как можно подстраивать частоту плавно :) Давно меня занимала эта фича, только не на чем было проверить. А тут наконец приехал мне с Али логиканализатор на 100 МГц. Настроил PWM, макс значение счета 5 - соответственно частота должна получится 133 / 5 = 26.6 МГц. Установил предделитель 1.064, чтобы, значит, получить на выходе ровно 25 МГц
26.6 / 1.064 = 25 МГц
Чуда не произошло. Как я и предполагал, частота не настраивается плавно. 25 МГц из 26 делается примерно так - десяток импульсов на частоте 26.6 и один на частоте 20. Потом снова десяток на 26....
Может я что не так настроил - попробуйте сами.
Ну так millis() в AVR так же работает. И 55-мс прерывания в DOS-сессии Windows - тоже.
25 МГц - это значит, 25000000 импульсов в секунду. Разве нет?
Ну так millis() в AVR так же работает. И 55-мс прерывания в DOS-сессии Windows - тоже.
25 МГц - это значит, 25000000 импульсов в секунду. Разве нет?
улыбнул значит )))
Да, есть проблема, объединил скетчи сканера wifi и двухтонального генератора и,
(выход синхронизации работает как и положено на 225 герц
а вот выход частоты 1800 пропускает импульсы, попробую перевести на другой пин...
ничего не дало, на другом пине также пропускает
делители float бывают только в сказках... из-за высокой частоты тактирования скорее всего на низких генерируемых частотах выходная частота будет весьма близка с требуемой...есть вероятность...
делители float бывают только в сказках...
читаем даташит и описание SDK внимательно....
25 МГц - это значит, 25000000 импульсов в секунду. Разве нет?
это как посмотреть :)
Можно сказать и так - "частота 25 МГц это означает что импульсы идут через каждые 1/ 25 000 000 секунды".... и тут уже будут нюансы
делители float бывают только в сказках...
читаем даташит и описание SDK внимательно....
я жеж безграмотный, поговорить куда ни шло )))
Если оставить только два PWM то всё работает как надо и 1800 и 2250 ...
Ребята, если чё, так Вы работаете с цифровой техникой, не забыли?
Ребята, если чё, так Вы работаете с цифровой техникой, не забыли?
ясный пень аналоговнет
Ребята, если чё, так Вы работаете с цифровой техникой, не забыли?
а вдруг? :)
25 МГц - это значит, 25000000 импульсов в секунду. Разве нет?
это как посмотреть :)
Можно сказать и так - "частота 25 МГц это означает что импульсы идут через каждые 1/ 25 000 000 секунды".... и тут уже будут нюансы
И, кстати, Петрович выше справедливо заметил, что мы имеем дело с цифрой, а потому 25000000 импульсов в секунду и никаких дополнительных условий.
признавайтесь, кто SPI исследовал?

Не могу разобраться с установкой скорости в библиотеке Adafruit - не устанавливается.
SPI тянет от hardwarespi.h, там она по умолчанию 4 мегагерца, но при применении библиотеки Adafruit_ST7735 она явно сильно ниже
По осциллограмме ниже видно, что обмен идёт в 16 битном режиме на скорости 50 килогерц?
Откуда это берётся???
Для w5500 ставил 1 МГц, не работало, пришлось прицепить логанализатор, реально spi 1мгц было. Большую частоту не пробовал.
И при чем тут скорость передачи данных? Смотрим частоту clk пина spi, по нему и судим о скорости.
CLK на осциллограмме зелёный луч
Ну, если я правильно вижу, в ячейке 5 мксек, два импульса в ячейке, аккурат 4мгц, не?
Upd: не,
Или 200 ns это ширина квадратика?
частота по символу H, что такое D пока не знаю (может время нарастания-спада сигнала), особо осциллограф не изучал, выставил исследование SPI и смотрю, обрабатывает не быстро иногда висит висит но так на экран ничего и не выведет, короче это не ригол )))
4 мегагерца это 250 наносекунд, 1 мегагерц - 1 микросекунда это у меня въелось )))
Получается период (грубо) 2.5 мкс, значит частота 400 кГц, библиотека врет, используем sdk и прямое общение с устройством, и наступит счастье)
так не бывает, где-то однозначно должно это устанавливаться, пока не нашёл где...
Работа с модемом SIM800L
Инициализация + чтение SMS в PDU формате, в том числе составных.
Пока только в 7и битной кодировке (латиница), кириллица позже будет.
#ifndef SIM800_PICO_H #define SIM800_PICO_H /// modem SIM800L UART #define UART_ID uart0 #define BAUD_RATE 9600 #define DATA_BITS 8 #define STOP_BITS 1 #define PARITY UART_PARITY_NONE // We are using pins 12 and 13, but see the GPIO function select table in the // datasheet for information on which other pins can be used. #define UART_TX_PIN 12 #define UART_RX_PIN 13 #define pinResetModem 23 // пин сброса модема #define max_size_modem_rx_buf 2048 // приемный буфер данных от модема #define period_wait_start_modem 15000UL // время ожидания модема после включения #define period_wait_registration 120000UL // время ожидания регистрации модема в сети оператора #define period_read_sms 10000UL // как часто с модема читать СМС enum tModemGlobalStatus {modemStBegin = 0, modemStInit, modemStReg, modemStReboot, modemStReady}; void modemInit(void); // инициализируем пины void modemReset(void); // сброс модема void modemMainLoop(void); union t_TP_MTIandCo { struct { unsigned TP_MTI:2; unsigned TP_MMS:1; unsigned res1:1; unsigned res2:1; unsigned TP_SRI:1; unsigned TP_UDHI:1; unsigned TP_RP:1; }; unsigned char TP_MTI_Value; }; #endif /* SIM800_PICO_H */ #include <stdio.h> #include "pico/stdlib.h" #include "sim800pico.h" #include "millpico.h" #include <string.h> #include <ctype.h> tModemGlobalStatus currentGlobalStatusModem = modemStBegin; unsigned char modemRXbuf[max_size_modem_rx_buf]; unsigned short modemPosBuf; unsigned char modemStepWork; unsigned long modemTimerWork = 0UL; unsigned char operatorName[16]; unsigned char operatorCSQ; signed short findPosSubstringFromBuf(unsigned char * inStr) { char * pStr = strstr((const char *) modemRXbuf, (const char *) inStr); if (pStr) return (inStr - ((unsigned char*)pStr)); else return -1; } bool findOKRNfromBuf(void) { if (findPosSubstringFromBuf((unsigned char *)"OK\r\n") >= 0) return true; else return false; } void initModemUART(void) { // sim800 uart init // Set up our UART with the required speed. uart_init(UART_ID, BAUD_RATE); // Set the TX and RX pins by using the function select on the GPIO // Set datasheet for more information on function select gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); // Use some the various UART functions to send out data // In a default system, printf will also output via the default UART // Send out a character without any conversions //uart_putc_raw(UART_ID, 'A'); // Send out a character but do CR/LF conversions //uart_putc(UART_ID, 'B'); // Send out a string, with CR/LF conversions //uart_puts(UART_ID, " Hello, UART!\n"); // Actually, we want a different speed // The call will return the actual baud rate selected, which will be as close as // possible to that requested int __unused actual = uart_set_baudrate(UART_ID, BAUD_RATE); // Set UART flow control CTS/RTS, we don't want these, so turn them off uart_set_hw_flow(UART_ID, true, true); // Set our data format uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY); // Turn off FIFO's - we want to do this character by character uart_set_fifo_enabled(UART_ID, true); } void modemInit(void) { // инициализируем пины и все остальное gpio_init(pinResetModem); // sim800 reset gpio_set_dir(pinResetModem, GPIO_IN); // sim800 reset gpio_put(pinResetModem, 0); // sim800 reset initModemUART(); // sim800 uart init } void modemReset(void) { // сброс модема gpio_set_dir(pinResetModem, GPIO_OUT); sleep_ms(115); gpio_set_dir(pinResetModem, GPIO_IN); } void modemClearRXbuf(void) { modemPosBuf = 0; memset(modemRXbuf, '\0', max_size_modem_rx_buf); //modemRXbuf[modemPosBuf] = '\0'; } void modemSendCMDandShowAndTimerStep(unsigned char * inStr) { uart_puts(UART_ID, (const char*)inStr); printf("%s\n", inStr); modemTimerWork = millis(); // сбросим таймер ++modemStepWork; // На след шаг } bool processCREG(void) { // парсинг ответа модема на команду AT+CREG char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CREG: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { char * p2 = strchr((const char *) modemRXbuf, ','); if (p2) { if (p2 > p1) { unsigned char br = *(p2+1); if ((br == '1') || (br == '5')) return true; } } } } return false; } void processCOPS(void) { // парсинг ответа модема на команду AT+COPS char * p1 = strstr((const char *) modemRXbuf, (const char *) "+COPS: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { char * p2 = strchr((const char *) modemRXbuf, ','); if (p2) { char * p3 = strchr((p2+1), ','); if (p3) { if ((p3>p2) && ((*(p3+1)) == '"')) { char * p4 = strchr((p3+2), '"'); if ((p4) && (p4>(p3+2))) { unsigned char lenP = p4-p3-2; for (unsigned char i=0; i<lenP; ++i) operatorName[i] = *(p3+2+i); operatorName[lenP] = '\0'; } } } } } } } void processCSQ(void) { // парсинг ответа модема на команду AT+CSQ char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CSQ: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { if (isdigit(*(p1+6))) { unsigned char tC = (*(p1+6)) - '0'; if (isdigit(*(p1+7))) { operatorCSQ = tC*10+((*(p1+7)) - '0'); } else { operatorCSQ = tC; } } } } } bool modemCMDstReg(bool inLine) { static unsigned long timerRegistration; static bool goodCREG; switch (modemStepWork) { case 0: { timerRegistration = millis(); goodCREG = false; ++modemStepWork; break; } case 1: { modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CREG?\r"); // проверяем зарегестрировался ли модем в сети break; } case 2: { if ((millis() - timerRegistration) >= period_wait_registration) return false; if ((millis() - modemTimerWork) >= 5000UL) { // больше 5 сек нет ответа modemTimerWork = millis(); // сбросим таймер modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации } else { if (inLine) { // если пришла строка if (processCREG()) goodCREG = true; // парсим строку - если зарегестрировались - ставим флаг if (findOKRNfromBuf()) { // ищем ответ ОК if (goodCREG) { // если успешно зарегестрировались modemSendCMDandShowAndTimerStep((unsigned char *)"AT+COPS?\r"); // получить режим подключения к оператору и имя оператора operatorName[0] = 0; // очищаем имя оператора } else { // если так и не смогли зарегестрироваться modemTimerWork = millis(); // сбросим таймер modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации } } } } break; } case 3: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine) { // если пришла строка processCOPS(); if (findOKRNfromBuf()) { modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSQ\r"); // запрос качества связи operatorCSQ = 0; } } break; } case 4: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine) { // если пришла строка processCSQ(); if (findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMGF=0\r"); // SMS в PDU формате } break; } case 5: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) { // успешно закончили начальную инициализацию модема currentGlobalStatusModem = modemStReady; modemStepWork = 0; } break; } case 25: { if ((millis() - modemTimerWork) >= 5000UL) modemStepWork = 1; // прождали и опять вернулись на запрос регистрации break; } default: {} } return true; } bool modemCMDstInit(bool inLine) { switch (modemStepWork) { case 0: { // ждем пока модем прочухается if ((millis() - modemTimerWork) >= period_wait_start_modem) ++modemStepWork; // На след шаг break; } case 1: { modemSendCMDandShowAndTimerStep((unsigned char *)"ATZ\r"); // проверка связи с модемом AT / сброс настроек на default - ATZ break; } case 2: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATE0\r"); // отключение(ATE0) ЭХО, т е модем в обратку не дублирует break; } case 3: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATV1\r"); // развернуты подробный ответ от модема break; } case 4: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMEE=2\r"); // вывод подробных описаний ошибок break; } case 5: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CLIP=0\r"); // отключение АОН break; } case 6: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATS0=0\r"); // модем не берет трубку при входящем звонке break; } case 7: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSCLK=0\r"); // отключение работы энергосберегающего режима через пин DTR break; } case 8: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) { // закончилась начальная инициализация modemStepWork = 0; currentGlobalStatusModem = modemStReg; // переходим в режим регистрации оператора } break; } default: {} } return true; } enum tSMSmode {smsStNone = 0, smsStGetList, smsStProcessing}; unsigned char smsGetByteFromTwoChars(char * inChars) { unsigned char c1 = *inChars; unsigned char outByte; if (isdigit(c1)) outByte = c1 - '0'; else outByte = c1 - 55; outByte *= 16; c1 = *(inChars + 1); if (isdigit(c1)) outByte += c1 - '0'; else outByte += c1 - 55; return outByte; } unsigned char smsTextMsg7bit(char * begStr, char * endStr, char * outBuf, unsigned char msgLen) { unsigned char outCharPos = 0; // позиция в выходном массиве char * currCharPos = begStr; // указатель на символ откуда берем байт unsigned char prevDataByte = 0; // предыдущий входящий байт while ((currCharPos < endStr) && (outCharPos < msgLen)) { unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных unsigned char idxCurrByte = outCharPos % 8; // индекс сдвига входящего байта 0...7 if (idxCurrByte) { unsigned char lowBits = prevDataByte >> (8 - idxCurrByte); *(outBuf+outCharPos) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits; ++outCharPos; if (idxCurrByte == 7) { *(outBuf+outCharPos) = inDataByte; ++outCharPos; } } else { *(outBuf+outCharPos) = inDataByte & 0x7F; ++outCharPos; } prevDataByte = inDataByte; // сохраним для след байта данных } return outCharPos; } unsigned char smsTextMsg7bitWithOffset(char * begStr, char * endStr, char * outBuf, unsigned char msgLen, unsigned short inOffset) { char * currCharPos = begStr; // указатель на символ откуда берем байт unsigned char changeOutOffset = 0; // байт сдвига выходного буфера if (inOffset) { // если первый байт сдвигается *(outBuf) = smsGetByteFromTwoChars(currCharPos) >> inOffset; // кладем в выходной буфер со сдвигом currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных changeOutOffset = 1; // будем на 1 символ больше класть в выходной буфер } unsigned char outCharPos = 0; // позиция в выходном массиве unsigned char prevDataByte = 0; // предыдущий входящий байт unsigned char idxCurrByte = 0; // индекс сдвига входящего байта 0...6 while ((currCharPos < endStr) && ((outCharPos+changeOutOffset) < msgLen)) { unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных if (idxCurrByte) { unsigned char lowBits = prevDataByte >> (8 - idxCurrByte); *(outBuf+outCharPos+changeOutOffset) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits; ++outCharPos; if (idxCurrByte == 6) { *(outBuf+outCharPos+changeOutOffset) = inDataByte >> 1; ++outCharPos; } } else { *(outBuf+outCharPos+changeOutOffset) = inDataByte & 0x7F; ++outCharPos; } prevDataByte = inDataByte; // сохраним для след байта данных ++idxCurrByte; if (idxCurrByte >= 7) idxCurrByte = 0; // индекс по входящим байтам 0...6 } return outCharPos+changeOutOffset; } // http://hardisoft.ru/soft/samodelkin-soft/poluchenie-i-dekodirovanie-sms-soobshhenij-v-formate-pdu/ void modemSMSprocessingLineText(void) { static unsigned char smsDataIndex; // индекс смс static unsigned char smsDataLenField; // длина данных из заголовка char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CMGL: "); if ((p1) && (p1 >= ((const char *) modemRXbuf))) { // вытаскиваем индекс СМСки if (isdigit(*(p1+7))) { smsDataIndex = (*(p1+7)) - '0'; if (isdigit(*(p1+8))) { smsDataIndex = smsDataIndex * 10 +(*(p1+8)) - '0'; } printf("smsDataIndex %d\r\n", smsDataIndex); // вытаскиваем длину поля данных p1 = strchr((const char *) (p1 + 7), ','); if (p1) { char * p2 = strchr((const char *) (p1 + 1), ','); if (p2) { p1 = strchr((const char *) (p2 + 1), ','); if ((p1) && (p1>p2) && (isdigit(*(p1+1))) && (isdigit(*(p1+2)))) { smsDataLenField = ((*(p1+1)) - '0') * 10 + ((*(p1+2)) - '0'); if (isdigit(*(p1+3))) { smsDataLenField *= 10; smsDataLenField += (*(p1+3)) - '0'; } printf("smsDataLenField %d\r\n", smsDataLenField); return; // строка успешно разпозналась - выходим } } } } } else if ((smsDataIndex) && (smsDataLenField)) { // след строка уже будет опираться на предыдущие обработанные данные char * smsPcurrentCharPos = (char*) modemRXbuf; // начало строки с данными char * smsPendPosLine = strchr(smsPcurrentCharPos, '\r'); // последний символ строки с данными + 1 unsigned short smsDataCharsLenLine = smsPendPosLine - smsPcurrentCharPos; // общая длина строки с данными if ((smsDataCharsLenLine % 2) == 0) { // количество символов должно быть четным if ((smsDataCharsLenLine / 2) >= smsDataLenField) { // есть ли заголовок TP-SCA (номер СМС центра) smsPcurrentCharPos += ((smsDataCharsLenLine/2) - smsDataLenField) * 2; // сдвигаем указатель строки начала данных вправо на длину TP-SCA t_TP_MTIandCo TP_MTIandCo; TP_MTIandCo.TP_MTI_Value = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("TP-MTI %d\r\n", TP_MTIandCo.TP_MTI); printf("TP-MMS %d\r\n", TP_MTIandCo.TP_MMS); printf("TP-SRI %d\r\n", TP_MTIandCo.TP_SRI); printf("TP-UDHI %d\r\n", TP_MTIandCo.TP_UDHI); printf("TP-RP %d\r\n", TP_MTIandCo.TP_RP); smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт // адрес отправителя TP-OA unsigned char lenTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // длина строки отправителя smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт unsigned char typeTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // формат строки отправителя smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт unsigned char smsSender[16]; // отправитель switch (typeTP_OA) { case 0x91: { // международный формат номера unsigned char senderPos = 0; while (senderPos < lenTP_OA) { unsigned char ch1 = * (smsPcurrentCharPos + 1); // первый символ / перевернутая позиция if (senderPos < lenTP_OA) { smsSender[senderPos] = ch1; ++senderPos; } ch1 = * smsPcurrentCharPos; // второй символ // перевернутая позиция if (senderPos < lenTP_OA) { smsSender[senderPos] = ch1; ++senderPos; } smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } smsSender[senderPos] = '\0'; // конец строки break; } default: { // тут остальные форматы номера,которые еще не запрограммированы, например текстовый отправитель, тупо сдвигаем позицию на нужгное кол.байт unsigned char senderPos = 0; while (senderPos < lenTP_OA) { if (senderPos < lenTP_OA) { smsSender[senderPos] = * smsPcurrentCharPos; ++senderPos; } if (senderPos < lenTP_OA) { smsSender[senderPos] = * (smsPcurrentCharPos + 1); ++senderPos; } smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } smsSender[senderPos] = '\0'; // конец строки } } printf("Sender %s\r\n", smsSender); // TP-PID (Protocol identifier) – Идентификатор протокола. // не используем - пропускаем smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-DCS (Data coding scheme) – Схема кодирования данных. // 00 — 7-битная сжатая кодировка, 08 — UCS2, 10 и 18 — то же, только сообщение класса 0, т.е. Flash. unsigned char TP_DCS = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("TP-DCS %02x\r\n", TP_DCS); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-SCTS (The service centre time stamp) – Штамп времени сервисного центра. Не используем. smsPcurrentCharPos +=14; // указатель сдвигаем на 7 байт // TP-UDL (User Data Length) — длина пользовательских данных, включая User Data header, если он есть. Если SMS закодирована 7-битной кодировкой, то данное поле указывает количество символов в сообщении. Если кодировка UCS2 – то поле указывает количество байт в сообщении. unsigned char TP_UDL = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("TP-UDL %d\r\n", TP_UDL); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-UD (User Data) – Поле пользовательских данных. Здесь находится сам текст сообщения. В случае, если SMS длинная, т.е. разбита на несколько сообщений, данное поле начинается с заголовка TP-UDH. На наличие этого заголовка указывает бит TP-UDHI поля TP-MTI & Co. unsigned char smsMessage[161]; // текст одного сообщения, по стандарту максимум 160 байт + терминатор smsMessage[0] = '\0'; if (TP_MTIandCo.TP_UDHI == 0) { // сообщение из одного блока if ((TP_DCS == 0x00) || (TP_DCS == 0x10)) { // 7и битная кодировка smsMessage[smsTextMsg7bit(smsPcurrentCharPos, smsPendPosLine, (char *) smsMessage, TP_UDL)] = '\0'; } else if ((TP_DCS == 0x08) || (TP_DCS == 0x18)) { // кодировка UCS2 } } else { // многоблочное сообщение unsigned char UDHL = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("UDHL %02x\r\n", UDHL); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // UDH // IEI Для отправки длинных сообщений всегда равен либо 0х00, либо 0х08. Если 0х00 – то IED1 занимает 1 байт, если 0х08 – то IED1 занимает 2 байта. unsigned char IEI = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("IEI %02x\r\n", IEI); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IEDL Длина данных информационного элемента – 3 или 4 байта, в зависимости от длины поля IED1 unsigned char IEDL = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("IEDL %02x\r\n", IEDL); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IED1 Номер-ссылка. Должен быть одинаков для всех частей сообщения unsigned short IED1; if (IEI == 0x00) { IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } else { IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos) * 256; smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт IED1 += smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } printf("IED1 %02x\r\n", IED1); // IED2 Количество частей в сообщении unsigned char IED2 = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("IED2 %02x\r\n", IED2); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IED3 Порядковый номер части сообщения unsigned char IED3 = smsGetByteFromTwoChars(smsPcurrentCharPos); printf("IED3 %02x\r\n", IED3); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт if ((TP_DCS == 0x00) || (TP_DCS == 0x10)) { // 7и битная кодировка // считаем смещение септета static unsigned short offset7bitFirstChar = 0; unsigned short lenBytesWithUDH = TP_UDL*7/8; // длина поля UD с заголовком UDH unsigned short lenBitsWOUDH = (lenBytesWithUDH - UDHL - 1) * 8; // длина поля с сообщением в битах уже без заголовка unsigned short count7bitChars = lenBitsWOUDH / 7; // количество целиковых 7и битных символов if ((IED3 == 1) && (IED2 > 1)) { // считаем для первой части сообщения, для остальных частей он аналогичный offset7bitFirstChar = lenBitsWOUDH - (count7bitChars * 7); // filler 0-6 бит Наполнитель, который выравнивает text по границе септета } smsMessage[smsTextMsg7bitWithOffset(smsPcurrentCharPos, smsPendPosLine, (char *) smsMessage, count7bitChars, offset7bitFirstChar)] = '\0'; } else if ((TP_DCS == 0x08) || (TP_DCS == 0x18)) { // кодировка UCS2 } } printf("%s\r\n", smsMessage); } } } smsDataIndex = 0; // если не распозналась и не обработалась СМСка // или успешно обработалось - обнуляем все что сделали } void modemCMDworkSMS(bool inLine) { static bool firstReadAllSMS = true; static unsigned long smsReqListTimer = 0; static unsigned long smsTimerWork; static tSMSmode smsCurrentMode = smsStNone; switch (smsCurrentMode) { case smsStGetList: { if (firstReadAllSMS) { uart_puts(UART_ID, (const char*)"AT+CMGL=4,0\r"); printf("%s\n", "AT+CMGL=4,0\r\n"); firstReadAllSMS = false; } else { return; uart_puts(UART_ID, (const char*)"AT+CMGL=0,0\r"); printf("%s\n", "AT+CMGL=0,0\r\n"); } smsTimerWork = millis(); smsCurrentMode = smsStProcessing; break; } case smsStProcessing: { if ((millis() - smsTimerWork) >= 20000UL) { smsReqListTimer = millis(); smsCurrentMode = smsStNone; } else if (inLine) { if (findOKRNfromBuf()) { smsReqListTimer = millis(); smsCurrentMode = smsStNone; } else { modemSMSprocessingLineText(); } } break; } default: { // smsStNone if ((millis() - smsReqListTimer) >= period_read_sms) smsCurrentMode = smsStGetList; } } } void modemMainLoop(void) { switch (currentGlobalStatusModem) { case modemStBegin: { modemClearRXbuf(); modemStepWork = 0; modemTimerWork = millis(); currentGlobalStatusModem = modemStInit; break; } case modemStReboot: { modemReset(); currentGlobalStatusModem = modemStBegin; break; } default: { // main work by modem unsigned char br = 0; // переменная куда падает входящий байт из модема bool flLine = false; // влаг что пришла целиковая строка while (uart_is_readable(UART_ID)) { // если что то пришло из модема br = uart_getc(UART_ID); // читаем пришедший байт if (br) { // если он больше нуля modemRXbuf[modemPosBuf] = br; // кладем байт в буфер ++modemPosBuf; // сдвигаем указатель на следующую позицию if (modemPosBuf >= max_size_modem_rx_buf) { modemPosBuf = 0; // начнем с начала буфера при переполнении flLine = true; // флаг что конец строки break; // выходим из цикла } if ((br == '\r') || (br == '\n') || ((br >= ' ') && (br < 0x7F))) { // если символ печатный или конец строки printf("%c", br); // выводим как есть } else { // если не печатный printf("x%02x", br); // выводим его шестнадцатиричное представление в лог } if (((br == '\n') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\r')) || ((br == '\r') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\n'))) { // пришел конец строки flLine = true; // флаг что конец строки modemRXbuf[modemPosBuf] = '\0'; // прописываем терминатор/окончание строки break; // выходим из цикла } } } // начинаем запуск всех обработчиков данных switch (currentGlobalStatusModem) { case modemStInit: { if (!modemCMDstInit(flLine)) currentGlobalStatusModem = modemStReboot; break; } case modemStReg: { if (!modemCMDstReg(flLine)) currentGlobalStatusModem = modemStReboot; break; } case modemStReady: { modemCMDworkSMS(flLine); break; } default: {} } // остальные обработчики // если обработали целиковую строку if (flLine) modemClearRXbuf(); // обнуляем массив // end main work } } tight_loop_contents(); }-
Update:
обновленный код - расшифровка в том числе русских СМС
#ifndef SIM800_PICO_H #define SIM800_PICO_H /// modem SIM800L UART #define UART_ID uart0 #define BAUD_RATE 9600 #define DATA_BITS 8 #define STOP_BITS 1 #define PARITY UART_PARITY_NONE // We are using pins 12 and 13, but see the GPIO function select table in the // datasheet for information on which other pins can be used. #define UART_TX_PIN 12 #define UART_RX_PIN 13 #define pinResetModem 23 // пин сброса модема //#define SHOW_DATASMS_LOG // вывести в лог всех данных об СМС #define max_size_modem_rx_buf 2048 // приемный буфер данных от модема #define period_wait_start_modem 15000UL // время ожидания модема после включения #define period_wait_registration 120000UL // время ожидания регистрации модема в сети оператора #define period_read_sms 10000UL // как часто с модема читать СМС #define max_count_read_sms 20 // максимальное количество СМСок в массиве // частей СМС enum tModemGlobalStatus {modemStBegin = 0, modemStInit, modemStReg, modemStReboot, modemStReady}; void modemInit(void); // инициализируем пины void modemReset(void); // сброс модема void modemMainLoop(void); enum t_SMSprocessingStatus {smsPrEmpty = 0, smsPrHeader, smsPrReady}; union t_TP_MTIandCo { struct { unsigned TP_MTI:2; unsigned TP_MMS:1; unsigned res1:1; unsigned res2:1; unsigned TP_SRI:1; unsigned TP_UDHI:1; unsigned TP_RP:1; }; unsigned char TP_MTI_Value; }; struct t_OneSMS { t_SMSprocessingStatus smsStatus; unsigned char smsDataIndex; // индекс смс unsigned char smsDataLenField; // длина данных из заголовка t_TP_MTIandCo TP_MTIandCo; unsigned char smsSender[16]; // отправитель unsigned char TP_DCS; // TP-DCS (Data coding scheme) – Схема кодирования данных. // 00 — 7-битная сжатая кодировка, 08 — UCS2, 10 и 18 — то же, только сообщение класса 0, т.е. Flash. unsigned char TP_UDL; // TP-UDL (User Data Length) — длина пользовательских данных, включая User Data header, если он есть. Если SMS закодирована 7-битной кодировкой, то данное поле указывает количество символов в сообщении. Если кодировка UCS2 – то поле указывает количество байт в сообщении. // TP-UD (User Data) – Поле пользовательских данных. Здесь находится сам текст сообщения. В случае, если SMS длинная, т.е. разбита на несколько сообщений, данное поле начинается с заголовка TP-UDH. На наличие этого заголовка указывает бит TP-UDHI поля TP-MTI & Co. unsigned char smsMessage[161]; // текст одного сообщения, по стандарту максимум 160 байт + терминатор unsigned char UDHL; // длина заголовка в многоблочном сообщении // UDH unsigned char IEI; // IEI Для отправки длинных сообщений всегда равен либо 0х00, либо 0х08. Если 0х00 – то IED1 занимает 1 байт, если 0х08 – то IED1 занимает 2 байта. unsigned char IEDL; // IEDL Длина данных информационного элемента – 3 или 4 байта, в зависимости от длины поля IED1 unsigned short IED1; // IED1 Номер-ссылка. Должен быть одинаков для всех частей сообщения unsigned char IED2; // IED2 Количество частей в сообщении unsigned char IED3; // IED3 Порядковый номер части сообщения }; #endif /* SIM800_PICO_H */ #include <stdio.h> #include "pico/stdlib.h" #include "sim800pico.h" #include "millpico.h" #include <string.h> #include <ctype.h> tModemGlobalStatus currentGlobalStatusModem = modemStBegin; unsigned char modemRXbuf[max_size_modem_rx_buf]; unsigned short modemPosBuf; unsigned char modemStepWork; unsigned long modemTimerWork = 0UL; unsigned char operatorName[16]; unsigned char operatorCSQ; t_OneSMS bufReadSMS[max_count_read_sms]; signed short findPosSubstringFromBuf(unsigned char * inStr) { char * pStr = strstr((const char *) modemRXbuf, (const char *) inStr); if (pStr) return (inStr - ((unsigned char*)pStr)); else return -1; } bool findOKRNfromBuf(void) { if (findPosSubstringFromBuf((unsigned char *)"OK\r\n") >= 0) return true; else return false; } void initModemUART(void) { // sim800 uart init // Set up our UART with the required speed. uart_init(UART_ID, BAUD_RATE); // Set the TX and RX pins by using the function select on the GPIO // Set datasheet for more information on function select gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); // Use some the various UART functions to send out data // In a default system, printf will also output via the default UART // Send out a character without any conversions //uart_putc_raw(UART_ID, 'A'); // Send out a character but do CR/LF conversions //uart_putc(UART_ID, 'B'); // Send out a string, with CR/LF conversions //uart_puts(UART_ID, " Hello, UART!\n"); // Actually, we want a different speed // The call will return the actual baud rate selected, which will be as close as // possible to that requested int __unused actual = uart_set_baudrate(UART_ID, BAUD_RATE); // Set UART flow control CTS/RTS, we don't want these, so turn them off uart_set_hw_flow(UART_ID, true, true); // Set our data format uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY); // Turn off FIFO's - we want to do this character by character uart_set_fifo_enabled(UART_ID, true); } void modemInit(void) { // инициализируем пины и все остальное gpio_init(pinResetModem); // sim800 reset gpio_set_dir(pinResetModem, GPIO_IN); // sim800 reset gpio_put(pinResetModem, 0); // sim800 reset initModemUART(); // sim800 uart init } void modemReset(void) { // сброс модема gpio_set_dir(pinResetModem, GPIO_OUT); sleep_ms(115); gpio_set_dir(pinResetModem, GPIO_IN); } void modemClearRXbuf(void) { modemPosBuf = 0; memset(modemRXbuf, '\0', max_size_modem_rx_buf); //modemRXbuf[modemPosBuf] = '\0'; } void modemSendCMDandShowAndTimerStep(unsigned char * inStr) { uart_puts(UART_ID, (const char*)inStr); printf("%s\n", inStr); modemTimerWork = millis(); // сбросим таймер ++modemStepWork; // На след шаг } bool processCREG(void) { // парсинг ответа модема на команду AT+CREG char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CREG: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { char * p2 = strchr((const char *) modemRXbuf, ','); if (p2) { if (p2 > p1) { unsigned char br = *(p2+1); if ((br == '1') || (br == '5')) return true; } } } } return false; } void processCOPS(void) { // парсинг ответа модема на команду AT+COPS char * p1 = strstr((const char *) modemRXbuf, (const char *) "+COPS: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { char * p2 = strchr((const char *) modemRXbuf, ','); if (p2) { char * p3 = strchr((p2+1), ','); if (p3) { if ((p3>p2) && ((*(p3+1)) == '"')) { char * p4 = strchr((p3+2), '"'); if ((p4) && (p4>(p3+2))) { unsigned char lenP = p4-p3-2; for (unsigned char i=0; i<lenP; ++i) operatorName[i] = *(p3+2+i); operatorName[lenP] = '\0'; } } } } } } } void processCSQ(void) { // парсинг ответа модема на команду AT+CSQ char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CSQ: "); if (p1) { if (p1 >= ((const char *) modemRXbuf)) { if (isdigit(*(p1+6))) { unsigned char tC = (*(p1+6)) - '0'; if (isdigit(*(p1+7))) { operatorCSQ = tC*10+((*(p1+7)) - '0'); } else { operatorCSQ = tC; } } } } } bool modemCMDstReg(bool inLine) { static unsigned long timerRegistration; static bool goodCREG; switch (modemStepWork) { case 0: { timerRegistration = millis(); goodCREG = false; ++modemStepWork; break; } case 1: { modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CREG?\r"); // проверяем зарегестрировался ли модем в сети break; } case 2: { if ((millis() - timerRegistration) >= period_wait_registration) return false; if ((millis() - modemTimerWork) >= 5000UL) { // больше 5 сек нет ответа modemTimerWork = millis(); // сбросим таймер modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации } else { if (inLine) { // если пришла строка if (processCREG()) goodCREG = true; // парсим строку - если зарегестрировались - ставим флаг if (findOKRNfromBuf()) { // ищем ответ ОК if (goodCREG) { // если успешно зарегестрировались modemSendCMDandShowAndTimerStep((unsigned char *)"AT+COPS?\r"); // получить режим подключения к оператору и имя оператора operatorName[0] = 0; // очищаем имя оператора } else { // если так и не смогли зарегестрироваться modemTimerWork = millis(); // сбросим таймер modemStepWork = 25; // перейдем на паузу 5 секунд, что бы потом опять запросить статус регистрации } } } } break; } case 3: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine) { // если пришла строка processCOPS(); if (findOKRNfromBuf()) { modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSQ\r"); // запрос качества связи operatorCSQ = 0; } } break; } case 4: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine) { // если пришла строка processCSQ(); if (findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMGF=0\r"); // SMS в PDU формате } break; } case 5: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) { // успешно закончили начальную инициализацию модема currentGlobalStatusModem = modemStReady; modemStepWork = 0; } break; } case 25: { if ((millis() - modemTimerWork) >= 5000UL) modemStepWork = 1; // прождали и опять вернулись на запрос регистрации break; } default: {} } return true; } bool modemCMDstInit(bool inLine) { switch (modemStepWork) { case 0: { // ждем пока модем прочухается if ((millis() - modemTimerWork) >= period_wait_start_modem) ++modemStepWork; // На след шаг break; } case 1: { modemSendCMDandShowAndTimerStep((unsigned char *)"ATZ\r"); // проверка связи с модемом AT / сброс настроек на default - ATZ break; } case 2: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATE0\r"); // отключение(ATE0) ЭХО, т е модем в обратку не дублирует break; } case 3: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATV1\r"); // развернуты подробный ответ от модема break; } case 4: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CMEE=2\r"); // вывод подробных описаний ошибок break; } case 5: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CLIP=0\r"); // отключение АОН break; } case 6: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"ATS0=0\r"); // модем не берет трубку при входящем звонке break; } case 7: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) modemSendCMDandShowAndTimerStep((unsigned char *)"AT+CSCLK=0\r"); // отключение работы энергосберегающего режима через пин DTR break; } case 8: { if ((millis() - modemTimerWork) >= 5000UL) return false; if (inLine && findOKRNfromBuf()) { // закончилась начальная инициализация modemStepWork = 0; currentGlobalStatusModem = modemStReg; // переходим в режим регистрации оператора } break; } default: {} } return true; } enum tSMSmode {smsStNone = 0, smsStGetList, smsStProcessing}; unsigned char smsGetByteFromTwoChars(char * inChars) { unsigned char c1 = *inChars; unsigned char outByte; if (isdigit(c1)) outByte = c1 - '0'; else outByte = c1 - 55; outByte *= 16; c1 = *(inChars + 1); if (isdigit(c1)) outByte += c1 - '0'; else outByte += c1 - 55; return outByte; } unsigned char smsTextMsg7bit(char * begStr, char * endStr, char * outBuf, unsigned char msgLen) { unsigned char outCharPos = 0; // позиция в выходном массиве char * currCharPos = begStr; // указатель на символ откуда берем байт unsigned char prevDataByte = 0; // предыдущий входящий байт while ((currCharPos < endStr) && (outCharPos < msgLen)) { unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных unsigned char idxCurrByte = outCharPos % 8; // индекс сдвига входящего байта 0...7 if (idxCurrByte) { unsigned char lowBits = prevDataByte >> (8 - idxCurrByte); *(outBuf+outCharPos) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits; ++outCharPos; if (idxCurrByte == 7) { *(outBuf+outCharPos) = inDataByte; ++outCharPos; } } else { *(outBuf+outCharPos) = inDataByte & 0x7F; ++outCharPos; } prevDataByte = inDataByte; // сохраним для след байта данных } return outCharPos; } unsigned char smsTextMsg7bitWithOffset(char * begStr, char * endStr, char * outBuf, unsigned char msgLen, unsigned short inOffset) { char * currCharPos = begStr; // указатель на символ откуда берем байт unsigned char changeOutOffset = 0; // байт сдвига выходного буфера if (inOffset) { // если первый байт сдвигается *(outBuf) = smsGetByteFromTwoChars(currCharPos) >> inOffset; // кладем в выходной буфер со сдвигом currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных changeOutOffset = 1; // будем на 1 символ больше класть в выходной буфер } unsigned char outCharPos = 0; // позиция в выходном массиве unsigned char prevDataByte = 0; // предыдущий входящий байт unsigned char idxCurrByte = 0; // индекс сдвига входящего байта 0...6 while ((currCharPos < endStr) && ((outCharPos+changeOutOffset) < msgLen)) { unsigned char inDataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных if (idxCurrByte) { unsigned char lowBits = prevDataByte >> (8 - idxCurrByte); *(outBuf+outCharPos+changeOutOffset) = ((inDataByte << idxCurrByte) & 0x7F) | lowBits; ++outCharPos; if (idxCurrByte == 6) { *(outBuf+outCharPos+changeOutOffset) = inDataByte >> 1; ++outCharPos; } } else { *(outBuf+outCharPos+changeOutOffset) = inDataByte & 0x7F; ++outCharPos; } prevDataByte = inDataByte; // сохраним для след байта данных ++idxCurrByte; if (idxCurrByte >= 7) idxCurrByte = 0; // индекс по входящим байтам 0...6 } return outCharPos+changeOutOffset; } unsigned char smsTextMsgUCS2(char * begStr, char * endStr, char * outBuf, unsigned char msgLen) { unsigned char outCharPos = 0; // позиция в выходном массиве char * currCharPos = begStr; // указатель на символ откуда берем байт while ((currCharPos < endStr) && (outCharPos < msgLen)) { unsigned char in1DataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных unsigned char in2DataByte = smsGetByteFromTwoChars(currCharPos); // входной байт currCharPos += 2; // сразу сдвинем указатель на два символа вправо = 1 байт данных if (!in1DataByte) { // ASCII *(outBuf+outCharPos) = in2DataByte; ++outCharPos; } else if (in1DataByte == 0x04) { // Unicode RUS if (in2DataByte == 0x01) { *(outBuf+outCharPos) = 0xD0; *(outBuf+outCharPos+1) = 0x81; } else if (in2DataByte == 0x51) { *(outBuf+outCharPos) = 0xD1; *(outBuf+outCharPos+1) = 0x91; } else if ((in2DataByte >= 0x10) && (in2DataByte <= 0x3F)) { *(outBuf+outCharPos) = 0xD0; *(outBuf+outCharPos+1) = 0x90 + (in2DataByte - 0x10); } else if ((in2DataByte >= 0x40) && (in2DataByte <= 0x4F)) { *(outBuf+outCharPos) = 0xD1; *(outBuf+outCharPos+1) = 0x80 + (in2DataByte - 0x40); } else { // other *(outBuf+outCharPos) = in1DataByte; *(outBuf+outCharPos+1) = in2DataByte; } outCharPos += 2; } else { // other *(outBuf+outCharPos) = in1DataByte; *(outBuf+outCharPos+1) = in2DataByte; outCharPos += 2; } } return outCharPos; } // http://hardisoft.ru/soft/samodelkin-soft/poluchenie-i-dekodirovanie-sms-... void modemSMSprocessingLineText(void) { static unsigned char smsDataIndex; // индекс смс static unsigned char smsDataLenField; // длина данных из заголовка static unsigned char smsCurrentIndex; // текущий слот в буфере для обработки char * p1 = strstr((const char *) modemRXbuf, (const char *) "+CMGL: "); if ((p1) && (p1 >= ((const char *) modemRXbuf))) { // вытаскиваем индекс СМСки if (isdigit(*(p1+7))) { smsDataIndex = (*(p1+7)) - '0'; if (isdigit(*(p1+8))) { smsDataIndex = smsDataIndex * 10 +(*(p1+8)) - '0'; } // вытаскиваем длину поля данных p1 = strchr((const char *) (p1 + 7), ','); if (p1) { char * p2 = strchr((const char *) (p1 + 1), ','); if (p2) { p1 = strchr((const char *) (p2 + 1), ','); if ((p1) && (p1>p2) && (isdigit(*(p1+1))) && (isdigit(*(p1+2)))) { smsDataLenField = ((*(p1+1)) - '0') * 10 + ((*(p1+2)) - '0'); if (isdigit(*(p1+3))) { smsDataLenField *= 10; smsDataLenField += (*(p1+3)) - '0'; } // поиск в массиве СМС пустого слота bool foundIdx = false; // флаг что нашелся индекс for (unsigned char i=0; i<max_count_read_sms; ++i) { if (bufReadSMS[i].smsStatus == smsPrEmpty) { foundIdx = true; smsCurrentIndex = i; break; } } if (foundIdx) { bufReadSMS[smsCurrentIndex].smsStatus = smsPrHeader; bufReadSMS[smsCurrentIndex].smsDataIndex = smsDataIndex; bufReadSMS[smsCurrentIndex].smsDataLenField = smsDataLenField; } else { smsDataIndex = 0; // обнулим данные, что бы далее обработка не пошла smsDataLenField = 0; // обнулим данные, что бы далее обработка не пошла } // ---- return; // строка успешно разпозналась - выходим } } } } } else if ((smsDataIndex) && (smsDataLenField) && (bufReadSMS[smsCurrentIndex].smsStatus = smsPrHeader)) { // след строка уже будет опираться на предыдущие обработанные данные char * smsPcurrentCharPos = (char*) modemRXbuf; // начало строки с данными char * smsPendPosLine = strchr(smsPcurrentCharPos, '\r'); // последний символ строки с данными + 1 unsigned short smsDataCharsLenLine = smsPendPosLine - smsPcurrentCharPos; // общая длина строки с данными if ((smsDataCharsLenLine % 2) == 0) { // количество символов должно быть четным if ((smsDataCharsLenLine / 2) >= smsDataLenField) { // есть ли заголовок TP-SCA (номер СМС центра) smsPcurrentCharPos += ((smsDataCharsLenLine/2) - smsDataLenField) * 2; // сдвигаем указатель строки начала данных вправо на длину TP-SCA bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_MTI_Value = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт // адрес отправителя TP-OA unsigned char lenTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // длина строки отправителя smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт unsigned char typeTP_OA = smsGetByteFromTwoChars(smsPcurrentCharPos); // формат строки отправителя smsPcurrentCharPos += 2; // указатель сдвигаем на два символа вправо = 1 байт switch (typeTP_OA) { case 0x91: { // международный формат номера unsigned char senderPos = 0; while (senderPos < lenTP_OA) { unsigned char ch1 = * (smsPcurrentCharPos + 1); // первый символ / перевернутая позиция if (senderPos < lenTP_OA) { bufReadSMS[smsCurrentIndex].smsSender[senderPos] = ch1; ++senderPos; } ch1 = * smsPcurrentCharPos; // второй символ // перевернутая позиция if (senderPos < lenTP_OA) { bufReadSMS[smsCurrentIndex].smsSender[senderPos] = ch1; ++senderPos; } smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } bufReadSMS[smsCurrentIndex].smsSender[senderPos] = '\0'; // конец строки break; } default: { // тут остальные форматы номера,которые еще не запрограммированы, например текстовый отправитель, тупо сдвигаем позицию на нужгное кол.байт unsigned char senderPos = 0; while (senderPos < lenTP_OA) { if (senderPos < lenTP_OA) { bufReadSMS[smsCurrentIndex].smsSender[senderPos] = * smsPcurrentCharPos; ++senderPos; } if (senderPos < lenTP_OA) { bufReadSMS[smsCurrentIndex].smsSender[senderPos] = * (smsPcurrentCharPos + 1); ++senderPos; } smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } bufReadSMS[smsCurrentIndex].smsSender[senderPos] = '\0'; // конец строки } } // TP-PID (Protocol identifier) – Идентификатор протокола. // не используем - пропускаем smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-DCS (Data coding scheme) – Схема кодирования данных. // 00 — 7-битная сжатая кодировка, 08 — UCS2, 10 и 18 — то же, только сообщение класса 0, т.е. Flash. bufReadSMS[smsCurrentIndex].TP_DCS = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-SCTS (The service centre time stamp) – Штамп времени сервисного центра. Не используем. smsPcurrentCharPos +=14; // указатель сдвигаем на 7 байт // TP-UDL (User Data Length) — длина пользовательских данных, включая User Data header, если он есть. Если SMS закодирована 7-битной кодировкой, то данное поле указывает количество символов в сообщении. Если кодировка UCS2 – то поле указывает количество байт в сообщении. bufReadSMS[smsCurrentIndex].TP_UDL = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // TP-UD (User Data) – Поле пользовательских данных. Здесь находится сам текст сообщения. В случае, если SMS длинная, т.е. разбита на несколько сообщений, данное поле начинается с заголовка TP-UDH. На наличие этого заголовка указывает бит TP-UDHI поля TP-MTI & Co. bufReadSMS[smsCurrentIndex].smsMessage[0] = '\0'; if (bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_UDHI == 0) { // сообщение из одного блока if ((bufReadSMS[smsCurrentIndex].TP_DCS == 0x00) || (bufReadSMS[smsCurrentIndex].TP_DCS == 0x10)) { // 7и битная кодировка bufReadSMS[smsCurrentIndex].smsMessage[smsTextMsg7bit(smsPcurrentCharPos, smsPendPosLine, (char *) bufReadSMS[smsCurrentIndex].smsMessage, bufReadSMS[smsCurrentIndex].TP_UDL)] = '\0'; } else if ((bufReadSMS[smsCurrentIndex].TP_DCS == 0x08) || (bufReadSMS[smsCurrentIndex].TP_DCS == 0x18)) { // кодировка UCS2 bufReadSMS[smsCurrentIndex].smsMessage[smsTextMsgUCS2(smsPcurrentCharPos, smsPendPosLine, (char *) bufReadSMS[smsCurrentIndex].smsMessage, bufReadSMS[smsCurrentIndex].TP_UDL)] = '\0'; } } else { // многоблочное сообщение bufReadSMS[smsCurrentIndex].UDHL = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // UDH // IEI Для отправки длинных сообщений всегда равен либо 0х00, либо 0х08. Если 0х00 – то IED1 занимает 1 байт, если 0х08 – то IED1 занимает 2 байта. bufReadSMS[smsCurrentIndex].IEI = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IEDL Длина данных информационного элемента – 3 или 4 байта, в зависимости от длины поля IED1 bufReadSMS[smsCurrentIndex].IEDL = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IED1 Номер-ссылка. Должен быть одинаков для всех частей сообщения if (bufReadSMS[smsCurrentIndex].IEI == 0x00) { bufReadSMS[smsCurrentIndex].IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } else { bufReadSMS[smsCurrentIndex].IED1 = smsGetByteFromTwoChars(smsPcurrentCharPos) * 256; smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт bufReadSMS[smsCurrentIndex].IED1 += smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт } // IED2 Количество частей в сообщении bufReadSMS[smsCurrentIndex].IED2 = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт // IED3 Порядковый номер части сообщения bufReadSMS[smsCurrentIndex].IED3 = smsGetByteFromTwoChars(smsPcurrentCharPos); smsPcurrentCharPos +=2; // указатель сдвигаем на два символа вправо = 1 байт if ((bufReadSMS[smsCurrentIndex].TP_DCS == 0x00) || (bufReadSMS[smsCurrentIndex].TP_DCS == 0x10)) { // 7и битная кодировка // считаем смещение септета static unsigned short offset7bitFirstChar = 0; unsigned short lenBytesWithUDH = bufReadSMS[smsCurrentIndex].TP_UDL*7/8; // длина поля UD с заголовком UDH unsigned short lenBitsWOUDH = (lenBytesWithUDH - bufReadSMS[smsCurrentIndex].UDHL - 1) * 8; // длина поля с сообщением в битах уже без заголовка unsigned short count7bitChars = lenBitsWOUDH / 7; // количество целиковых 7и битных символов if ((bufReadSMS[smsCurrentIndex].IED3 == 1) && (bufReadSMS[smsCurrentIndex].IED2 > 1)) { // считаем для первой части сообщения, для остальных частей он аналогичный offset7bitFirstChar = lenBitsWOUDH - (count7bitChars * 7); // filler 0-6 бит Наполнитель, который выравнивает text по границе септета } bufReadSMS[smsCurrentIndex].smsMessage[smsTextMsg7bitWithOffset(smsPcurrentCharPos, smsPendPosLine, (char *) bufReadSMS[smsCurrentIndex].smsMessage, count7bitChars, offset7bitFirstChar)] = '\0'; } else if ((bufReadSMS[smsCurrentIndex].TP_DCS == 0x08) || (bufReadSMS[smsCurrentIndex].TP_DCS == 0x18)) { // кодировка UCS2 bufReadSMS[smsCurrentIndex].smsMessage[smsTextMsgUCS2(smsPcurrentCharPos, smsPendPosLine, (char *) bufReadSMS[smsCurrentIndex].smsMessage, bufReadSMS[smsCurrentIndex].TP_UDL)] = '\0'; } } #ifdef SHOW_DATASMS_LOG printf("smsDataIndex %d\r\n", smsDataIndex); printf("smsDataLenField %d\r\n", smsDataLenField); printf("TP-MTI %d\r\n", bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_MTI); printf("TP-MMS %d\r\n", bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_MMS); printf("TP-SRI %d\r\n", bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_SRI); printf("TP-UDHI %d\r\n", bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_UDHI); printf("TP-RP %d\r\n", bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_RP); printf("Sender %s\r\n", bufReadSMS[smsCurrentIndex].smsSender); printf("TP-DCS %02x\r\n", bufReadSMS[smsCurrentIndex].TP_DCS); printf("TP-UDL %d\r\n", bufReadSMS[smsCurrentIndex].TP_UDL); if (!bufReadSMS[smsCurrentIndex].TP_MTIandCo.TP_UDHI) { printf("UDHL %02x\r\n", bufReadSMS[smsCurrentIndex].UDHL); printf("IEI %02x\r\n", bufReadSMS[smsCurrentIndex].IEI); printf("IEDL %02x\r\n", bufReadSMS[smsCurrentIndex].IEDL); printf("IED1 %04x\r\n", bufReadSMS[smsCurrentIndex].IED1); printf("IED2 %02x\r\n", bufReadSMS[smsCurrentIndex].IED2); printf("IED3 %02x\r\n", bufReadSMS[smsCurrentIndex].IED3); } printf("Text SMS %d index:\r\n", smsCurrentIndex); printf("%s\r\n", bufReadSMS[smsCurrentIndex].smsMessage); #endif bufReadSMS[smsCurrentIndex].smsStatus = smsPrReady; } } } smsDataIndex = 0; // если не распозналась и не обработалась СМСка // или успешно обработалось - обнуляем все что сделали } void modemCMDworkSMS(bool inLine) { static bool firstReadAllSMS = true; static unsigned long smsReqListTimer = 0; static unsigned long smsTimerWork; static tSMSmode smsCurrentMode = smsStNone; switch (smsCurrentMode) { case smsStGetList: { if (firstReadAllSMS) { uart_puts(UART_ID, (const char*)"AT+CMGL=4,0\r"); printf("%s\n", "AT+CMGL=4,0\r\n"); firstReadAllSMS = false; } else { //return; // если только один раз читать все СМС при старте uart_puts(UART_ID, (const char*)"AT+CMGL=0,0\r"); printf("%s\n", "AT+CMGL=0,0\r\n"); } smsTimerWork = millis(); smsCurrentMode = smsStProcessing; break; } case smsStProcessing: { if ((millis() - smsTimerWork) >= 20000UL) { smsReqListTimer = millis(); smsCurrentMode = smsStNone; } else if (inLine) { if (findOKRNfromBuf()) { smsReqListTimer = millis(); smsCurrentMode = smsStNone; } else { modemSMSprocessingLineText(); } } break; } default: { // smsStNone if ((millis() - smsReqListTimer) >= period_read_sms) smsCurrentMode = smsStGetList; } } } void modemMainLoop(void) { switch (currentGlobalStatusModem) { case modemStBegin: { modemClearRXbuf(); modemStepWork = 0; modemTimerWork = millis(); currentGlobalStatusModem = modemStInit; break; } case modemStReboot: { modemReset(); currentGlobalStatusModem = modemStBegin; break; } default: { // main work by modem unsigned char br = 0; // переменная куда падает входящий байт из модема bool flLine = false; // влаг что пришла целиковая строка while (uart_is_readable(UART_ID)) { // если что то пришло из модема br = uart_getc(UART_ID); // читаем пришедший байт if (br) { // если он больше нуля modemRXbuf[modemPosBuf] = br; // кладем байт в буфер ++modemPosBuf; // сдвигаем указатель на следующую позицию if (modemPosBuf >= max_size_modem_rx_buf) { modemPosBuf = 0; // начнем с начала буфера при переполнении flLine = true; // флаг что конец строки break; // выходим из цикла } if ((br == '\r') || (br == '\n') || ((br >= ' ') && (br < 0x7F))) { // если символ печатный или конец строки printf("%c", br); // выводим как есть } else { // если не печатный printf("x%02x", br); // выводим его шестнадцатиричное представление в лог } if (((br == '\n') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\r')) || ((br == '\r') && (modemPosBuf > 1) && (modemRXbuf[modemPosBuf-2] == '\n'))) { // пришел конец строки flLine = true; // флаг что конец строки modemRXbuf[modemPosBuf] = '\0'; // прописываем терминатор/окончание строки break; // выходим из цикла } } } // начинаем запуск всех обработчиков данных switch (currentGlobalStatusModem) { case modemStInit: { if (!modemCMDstInit(flLine)) currentGlobalStatusModem = modemStReboot; break; } case modemStReg: { if (!modemCMDstReg(flLine)) currentGlobalStatusModem = modemStReboot; break; } case modemStReady: { modemCMDworkSMS(flLine); break; } default: {} } // остальные обработчики // если обработали целиковую строку if (flLine) modemClearRXbuf(); // обнуляем массив // end main work } } tight_loop_contents(); }а у нутрях ядра cmsis присутствует )))
PS а у MBED и CMSIS и HAL ...
cmsis это стандарт arm, он есть везде и он отвратительный.
cmsis это стандарт arm, он есть везде и он отвратительный.
...есть осталось только дерьмо мамонта... зато его много... (из анекдота)
cmsis это стандарт arm, он есть везде и он отвратительный.
[MBED
Мбед отстой... впрочем я им никогда не пользовался Ж)
cmsis это стандарт arm, он есть везде и он отвратительный.
не соглашусь, по факту это определение регистров/прерываний и прочего чипа.
не нравиться CMSIS - пишите напрямую в адреса, данные все есть в даташите.
А кто нибудь пробовал RP2040 подключать к LCD 1602 экрану? Нахожу пока только поврежденные библиотеки.
А кто нибудь пробовал RP2040 подключать к LCD 1602 экрану? Нахожу пока только поврежденные библиотеки.
А что, есть проблемы?
Да, есть проблема. Ни одна библиотека из zip формата не распаковывается, поскольку программа не может найти файл с расширением *.h
Да, есть проблема. Ни одна библиотека из zip формата не распаковывается, поскольку программа не может найти файл с расширением *.h
вот эта и распаковывается и компилируется, подключать дисплей влом (обязан работать)
Привет, подскажите что я делаю не так компилирую программу в main.uf2, перезагружаю с зажатым boot, закидываю на появившийся диск программу, она запускается и работает, но если перезагрузить плату программа больше не запускается. Только если опять закидывать через boot.
Нет, это неправильно, так быть не должно.
Где то в инете лежит что то типа clean.uf2, программа, которая чистить флэш и прочее в МК. Попробуйте его. Или другую прогу скомпилировать, понять так же себя МК ведёт или нет.
я в таком случае заливаю разные версии питона
Я компилирую через wsl (ubuntu ) по win10, если через arduino ide 2.0 то все ок.
а зачем так сложно?
rp2040 sdk под linux прекрасно разворачивается и работает без всяких лишних прокладок.
делители float бывают только в сказках...
с натяжкой в синтезаторах вроде si5351, там правда оно из несколько коэффициентов и отдельный PLL
В коде сообщения #292 ошибка, белый цвет так не получить.
Вместо логического сложения (дизъюнкции, операции «ИЛИ») хитрый кетаец вставил просто сложение "+". Зачем???
Надо так - print("white") for j in range(0, 100): for i in range(NUM_LEDS): ar[i] = j<<16 | j<<8 | j было - ar[i] = j<<16 + j<<8 + jВ коде сообщения #292 ошибка, белый цвет так не получить.
Вместо логического сложения (дизъюнкции, операции «ИЛИ») хитрый кетаец вставил просто сложение "+". Зачем???
Надо так - print("white") for j in range(0, 100): for i in range(NUM_LEDS): ar[i] = j<<16 | j<<8 | j было - ar[i] = j<<16 + j<<8 + jк сожалению в посте код уже не поправить, надо выкладывать новую правленную функцию
В данном случае разницы между + и | нет !
0 +1 = 1
и
0 | 1 = 1
Второй операнд в данном примере всегда 0.
к сожалению в посте код уже не поправить, надо выкладывать новую правленную функцию
Кому надо разберутся.
В данном случае разницы между + и | нет !
0 +1 = 1
и
0 | 1 = 1
Второй операнд в данном примере всегда 0.
Так не будет работать, т.к. используются не один бит (с помощью побитовых операций сдвига и логического сложения формируется управляющее слово из 24 бит).
Возьмите плату и светодиод и попробуйте. У меня она есть со встроенным светодиодом, код примера протестирован , ошибки исправлены.
Вместо логического сложения (дизъюнкции, операции «ИЛИ») хитрый кетаец вставил просто сложение "+". Зачем???
Прежде чем обвинять китайца в хитрости, башкой подумай. Попытайся сложить
0110 0000 0000 +
0000 1100 0000 +
0000 0000 0011 = ?
Потом расскажешь, чо получилось