Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: GSM модем + TNKernel
Форум разработчиков электроники ELECTRONIX.ru > Интерфейсы > Форумы по интерфейсам > Сотовая связь и ее приложения
hound
Добрый день. Есть 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
Moderator: К вопросам операционных систем тема относится чуть менее, чем никак, посему - перенес.
controller_m30
Делал в двух вариантах: без распознавания чисел, и с распознаванием. В этом случае, вроде бы, можно обойтись без распознавания чисел (если надо, то приведу и распознаванием).

Без распознавания. Сначала составляем таблицу всех нужных нам ответов модема, при этом к числам в ответе относимся как к части текста:
Код
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
А если сделать так:
перед отправкой команды модему в глобальную перемену заносим начало ответа на команду, в случае с командой AT+CREG?
это будет "+CREG", а в прерывании, вернее в функции обработчике буфера в прерывании будем сравнивать полученный ответ от модема с этой переменной. Тогда не нужно будет прочесывать каждый раз таблицу...
controller_m30
Можно и так - это лишь вопрос удобства написания конкретной программы laughing.gif

Мне нужно было рулить ещё нескольким внешними устройствами, а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды. Это очень долго (модем может ещё и зависнуть). А терять данные от других внешних устройств не хотелось.
Потому приём данных от COM-порта был перепоручен обработчику прерываний UART, который в завершении приёма очередной строки сверялся с таблицей, и выкладывал в глобальную переменную код этой строки. Сама таблица была 30-40 строчек, и её "прочёсывание" после ответа модема - основную программу особо не тормозило.

А уже основная программа, в промежутках между обращениями к остальным устройствам, парсила глобальную переменную на неравенство "0", и только в этом случае отвлекалась на обработку ответа.

Если у вас программа линейная и других задач для процессора нет - то вполне можно загрузить его ожиданием приёма данных от модема на все 100%. Почему бы и нет rolleyes.gif
hound
Ну кроме работы с модемом нужно повешать еще и другие задачи, поэтому и использую операционку:
один таск под модем,
второй под работу с ГПС и т.д. каждый таск под свое устройство.
Цитата
а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды


Не совсем понял, а почему может составлять секунды? Модем висит на одном ЮАРТе, другие девайсы на другом и т.п под каждый юарт свой обработчик прерывания.
Lotor
А как Вы будете обрабатывать unsolicited messages?

Цитата(hound @ Mar 24 2015, 17:33) *
Не совсем понял, а почему может составлять секунды?

Вероятно, имелось в виду, что gsm модуль чисто физически может отвечать с задержкой.
hound
Цитата(Lotor @ Mar 24 2015, 19:14) *
А как Вы будете обрабатывать unsolicited messages?


В функции обработчике буфера от модема (в прерывании) до того как отправить буфер очередью проверяются такие данные от модема:
1. OK - выставляется флаг, который ожидает задача после отправки команды модему, если за N-ое кол-во мсек после отправки команды флаг не выставился - значит модем не ответил и дальше уже по обстоятельству (перазагрузка модема и т.п)

2. > - готов отправить данные серверу.

3. SEND OK - аналогично с флагом ОК только этот флаг ожидается после отправки данных "на сервер".

4. +RECEIVE: - получили данные "с сервера" и записываем приходящие данные в прерывании не в буфер модема, а в буфер входящих сообщений.

И уже если эти 4 строчки не совпали смотрим отправлять ли очередь или нет, опять же в зависимости от того на какую команду ответ мы ждем.

К сообщению приложил модуль, который на текущий момент у меня получился, пока еще без подключения к серверу и без полноценного разбора ответов.
controller_m30
А, вот какой обработчик. Это совсем другое дело! biggrin.gif
А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные.

Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+CREG: 0,1","OK". То, что отмечено как "\r" и "\n" - это непечатные символы <cr> (0x0d) и <lf>(0x0a) соответственно.

Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения?
hound
Цитата(controller_m30 @ Mar 24 2015, 20:53) *
А, вот какой обработчик. Это совсем другое дело! biggrin.gif
А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные.

Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+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", которое в очередь не попадет уж точно.
Как-то так.
RadikX
В прерывании разбор строк лучше не делать. Прерывание должно отработать максимально быстро и выйти. В какой-то момент могут возникать "глюки" - или не успел отработать или потерял символ. Имхо лучше запоминать позиции концевиков 0x0D 0x0A, а разбор делать в основной программе.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.