hound 0 23 марта, 2015 Опубликовано 23 марта, 2015 · Жалоба Добрый день. Есть GSM модем, управление, которого производится AT командами. Общий вид "общения" с модемом: 1. Отправляется команда 2. Ответ модема на команду (может отсутствовать) 3. Строка "OK", что мол команда выполнена. Соответственно есть команды, которые возвращают значение состояния модема, например: AT+CREG? +CREG: 0, 1 OK А есть, которые просто выдают строчку ОК, что команда принята и все, например: AT OK Собственно и нужно отслеживать каждый ответ модема и разбирать его, сейчас я сделал что-то подобное. Функция отправки простой AT команды модему: uint8_t modem_check_at(void) { uint32_t flag, rc; uart3_ex_send_string("AT\r\n"); rc = tn_event_wait(&modem_event, MODEM_SAY_OK, TN_EVENT_WCOND_OR, &flag, 300); if (rc != TERR_NO_ERR) { dbg_send("AT fail\r\n"); return ACTION_FAIL; } dbg_send("AT ok\r\n"); tn_event_clear(&modem_event, ~(MODEM_SAY_OK)); return ACTION_OK; } А в самом прерывании, если от модема пришла строка содержащее слово ОК, то выставляется флаг MODEM_SAY_OK: if (strncmp(modem_buffer, "OK", 2) == 0) { tn_event_iset(&modem_event, MODEM_SAY_OK); return; } С простыми командами так проходит, но сложность возникает с теми командами, в которых нужно разбирать ответ модема. Например команда: AT+CREG? После отправки модему она возвращает строку с определенными параметрами и после уже слово ОК, что команда выполнена. Поэтому простой проверкой на слово ОК не обойтись, нужно разбирать, то что ответил модем. Сделал таким образом: все строчки, которые пришли от модема складываются в очередь, которую уже должна обработать функция, которая отправляла данную команду модему, опять же пример: uint8_t modem_creg(void) { uint32_t rc, flag, number_queue; uart3_ex_send_string("AT+CREG?\r\n"); rc = tn_queue_receive(&queue_modem_buffer, (void**)&number_queue, 300); if (rc != TERR_NO_ERR) { dbg_send("CREG fail!\r\n"); return ACTION_FAIL; } dbg_send("Answer CREG - "); dbg_send(modem_queue_buffer[number_queue]); dbg_send_rn(); } Да момент такой еще, есть один линейный буфер, в который пишутся приходящие символы в прерывании, в том случае когда эту строчку нужно отправить через очередь, содержимое этого линейного буфера копируется в другой буфер, который представляем из себя "массив буферов" и собственно позицию в этом массиве и передается через очередь. Но в таком случае в очередь может отправится ЭХО модема и вместо ответа "+CREG: 0,2" в функции увидим "AT+CREG?", как вариант прогонять очередь до тех пор пока не попадется нужный нам ответ: do { rc = tn_queue_receive_polling(&queue_modem_buffer, (void**)&number_queue); if (strncmp(modem_queue_buffer[number_queue], "+CREG", 5) == 0) { return number_queue; } } while (rc == TERR_NO_ERR); Но это кажется сильно натянутым решением... Эхо от модема отключать не желательно,хотя это отчасти и решило бы проблему, но опять же не полностью. И кроме однострочных команд, модем может выдать много строчные команды. В общем, у кого-нибудь был опыт работы с АТ командами, кто как реализовал? Заранее благодарен за любой совет/пинок в нужную/правильную сторону. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба Moderator: К вопросам операционных систем тема относится чуть менее, чем никак, посему - перенес. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
controller_m30 1 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба Делал в двух вариантах: без распознавания чисел, и с распознаванием. В этом случае, вроде бы, можно обойтись без распознавания чисел (если надо, то приведу и распознаванием). Без распознавания. Сначала составляем таблицу всех нужных нам ответов модема, при этом к числам в ответе относимся как к части текста: DB8 "OK" ' 1 DB8 "ERROR" ' 2 DB8 "RDY" ' 3 DB8 "+CFUN: 1" ' 4 DB8 "+CFUN: 0" ' 5 DB8 "Call Ready" ' 6 DB8 "+CPIN: READY" ' 7 DB8 "+CPIN: NOT INSERTED" ' 8 DB8 "+CREG 0,1" ' 9 DB8 "+CREG 0,0" ' 10 DB8 "SMS Ready" ' 11 и так далее Принятые из порта текстовые ответы сразу-же "прочёсываются" по данной таблице на совпадение, и если оно есть - № совпавшей позиции записываем в очередь. Основная программа, таким образом, будет выбирать из очереди только коды принятых сообщений, и как-то на них реагировать. Например ответ модема: +CREG 0,1 OK будет помещён в очередь как последовательность байтов: 9, 1 А ответ: +CREG 0,0 OK в очереди будет: 10, 1 Ещё для примера - текст при включении модема: RDY +CFUN: 1 +CPIN: READY Call Ready SMS Ready в очереди будет выглядеть так: 3, 4, 7, 6, 11 А всё что не распозналось (эхо и какие-то ненужные ответы) - в очереди будет отсутствовать вообще. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hound 0 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба А если сделать так: перед отправкой команды модему в глобальную перемену заносим начало ответа на команду, в случае с командой AT+CREG? это будет "+CREG", а в прерывании, вернее в функции обработчике буфера в прерывании будем сравнивать полученный ответ от модема с этой переменной. Тогда не нужно будет прочесывать каждый раз таблицу... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
controller_m30 1 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба Можно и так - это лишь вопрос удобства написания конкретной программы :laughing: Мне нужно было рулить ещё нескольким внешними устройствами, а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды. Это очень долго (модем может ещё и зависнуть). А терять данные от других внешних устройств не хотелось. Потому приём данных от COM-порта был перепоручен обработчику прерываний UART, который в завершении приёма очередной строки сверялся с таблицей, и выкладывал в глобальную переменную код этой строки. Сама таблица была 30-40 строчек, и её "прочёсывание" после ответа модема - основную программу особо не тормозило. А уже основная программа, в промежутках между обращениями к остальным устройствам, парсила глобальную переменную на неравенство "0", и только в этом случае отвлекалась на обработку ответа. Если у вас программа линейная и других задач для процессора нет - то вполне можно загрузить его ожиданием приёма данных от модема на все 100%. Почему бы и нет :rolleyes: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hound 0 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба Ну кроме работы с модемом нужно повешать еще и другие задачи, поэтому и использую операционку: один таск под модем, второй под работу с ГПС и т.д. каждый таск под свое устройство. а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды Не совсем понял, а почему может составлять секунды? Модем висит на одном ЮАРТе, другие девайсы на другом и т.п под каждый юарт свой обработчик прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
lotor 0 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба А как Вы будете обрабатывать unsolicited messages? Не совсем понял, а почему может составлять секунды? Вероятно, имелось в виду, что gsm модуль чисто физически может отвечать с задержкой. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hound 0 24 марта, 2015 Опубликовано 24 марта, 2015 (изменено) · Жалоба А как Вы будете обрабатывать unsolicited messages? В функции обработчике буфера от модема (в прерывании) до того как отправить буфер очередью проверяются такие данные от модема: 1. OK - выставляется флаг, который ожидает задача после отправки команды модему, если за N-ое кол-во мсек после отправки команды флаг не выставился - значит модем не ответил и дальше уже по обстоятельству (перазагрузка модема и т.п) 2. > - готов отправить данные серверу. 3. SEND OK - аналогично с флагом ОК только этот флаг ожидается после отправки данных "на сервер". 4. +RECEIVE: - получили данные "с сервера" и записываем приходящие данные в прерывании не в буфер модема, а в буфер входящих сообщений. И уже если эти 4 строчки не совпали смотрим отправлять ли очередь или нет, опять же в зависимости от того на какую команду ответ мы ждем. К сообщению приложил модуль, который на текущий момент у меня получился, пока еще без подключения к серверу и без полноценного разбора ответов. modem.rar Изменено 24 марта, 2015 пользователем hound Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
controller_m30 1 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба А, вот какой обработчик. Это совсем другое дело! А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные. Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+CREG: 0,1","OK". То, что отмечено как "\r" и "\n" - это непечатные символы <cr> (0x0d) и <lf>(0x0a) соответственно. Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hound 0 24 марта, 2015 Опубликовано 24 марта, 2015 · Жалоба А, вот какой обработчик. Это совсем другое дело! А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные. Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+CREG: 0,1","OK". То, что отмечено как "\r" и "\n" - это непечатные символы <cr> (0x0d) и <lf>(0x0a) соответственно. Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения? По концу строки <0xOD><0xOA> В прерывании: if (ch == 0x0D) return; if (ch == 0x0A) { modem_line_buffer.data[modem_line_buffer.pos] = 0x00; modem_line_buffer.pos = 0; modem_process_buffer(); return; } Дальше уже в функции modem_process_buffer() идет обработка сообщения void modem_process_buffer(void) { if (strlen(modem_line_buffer.data) < 1) return; if (strncmp(modem_line_buffer.data, "OK", 2) == 0) { tn_event_iset(&modem_event, MODEM_SAY_OK); return; } if (strncmp(modem_line_buffer.data, "SEND OK", 7) == 0) { tn_event_iset(&modem_event, MODEM_SEND_OK); return; } if (strncmp(modem_line_buffer.data, "+RECEIVE", 7) == 0) { uint16_t length; length = get_modem_token_int(modem_line_buffer.data, 1); modem_status.read_flag = TRUE; modem_receive_buffer_reset(&modem_receive_buffer, length); return; } if (strncmp(modem_line_buffer.data, wait_queue.data, wait_queue.length) == 0) { modem_send_queue(); } } Где, wait_queue.data, wait_queue.lengt это строка и ее длина для анализа ответа от модема, т.е отправили мы команду AT+CREG? и от нее нам нужно получить ответ вида +CREG: 2 Вот значение "+CREG" мы и записывает wait_queue.data. И уже кроме этого ответа ничего в очередь не попадает, а если что-то пришло от сервера, то в прерывании мы схватим сообщение "+RECEIVE", которое в очередь не попадет уж точно. Как-то так. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Radik_1983 0 30 марта, 2015 Опубликовано 30 марта, 2015 · Жалоба В прерывании разбор строк лучше не делать. Прерывание должно отработать максимально быстро и выйти. В какой-то момент могут возникать "глюки" - или не успел отработать или потерял символ. Имхо лучше запоминать позиции концевиков 0x0D 0x0A, а разбор делать в основной программе. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться