Jump to content

    
Sign in to follow this  
hound

GSM модем + TNKernel

Recommended Posts

Добрый день. Есть 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);

 

Но это кажется сильно натянутым решением...

Эхо от модема отключать не желательно,хотя это отчасти и решило бы проблему, но опять же не полностью.

И кроме однострочных команд, модем может выдать много строчные команды.

 

В общем, у кого-нибудь был опыт работы с АТ командами, кто как реализовал? Заранее благодарен за любой совет/пинок в нужную/правильную сторону.

Share this post


Link to post
Share on other sites

Делал в двух вариантах: без распознавания чисел, и с распознаванием. В этом случае, вроде бы, можно обойтись без распознавания чисел (если надо, то приведу и распознаванием).

 

Без распознавания. Сначала составляем таблицу всех нужных нам ответов модема, при этом к числам в ответе относимся как к части текста:

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

 

А всё что не распозналось (эхо и какие-то ненужные ответы) - в очереди будет отсутствовать вообще.

Share this post


Link to post
Share on other sites

А если сделать так:

перед отправкой команды модему в глобальную перемену заносим начало ответа на команду, в случае с командой AT+CREG?

это будет "+CREG", а в прерывании, вернее в функции обработчике буфера в прерывании будем сравнивать полученный ответ от модема с этой переменной. Тогда не нужно будет прочесывать каждый раз таблицу...

Share this post


Link to post
Share on other sites

Можно и так - это лишь вопрос удобства написания конкретной программы :laughing:

 

Мне нужно было рулить ещё нескольким внешними устройствами, а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды. Это очень долго (модем может ещё и зависнуть). А терять данные от других внешних устройств не хотелось.

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

 

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

 

Если у вас программа линейная и других задач для процессора нет - то вполне можно загрузить его ожиданием приёма данных от модема на все 100%. Почему бы и нет :rolleyes:

Share this post


Link to post
Share on other sites

Ну кроме работы с модемом нужно повешать еще и другие задачи, поэтому и использую операционку:

один таск под модем,

второй под работу с ГПС и т.д. каждый таск под свое устройство.

а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секунды

 

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

Share this post


Link to post
Share on other sites

А как Вы будете обрабатывать unsolicited messages?

 

Не совсем понял, а почему может составлять секунды?

Вероятно, имелось в виду, что gsm модуль чисто физически может отвечать с задержкой.

Share this post


Link to post
Share on other sites
А как Вы будете обрабатывать unsolicited messages?

 

В функции обработчике буфера от модема (в прерывании) до того как отправить буфер очередью проверяются такие данные от модема:

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

 

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

 

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

 

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

 

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

 

К сообщению приложил модуль, который на текущий момент у меня получился, пока еще без подключения к серверу и без полноценного разбора ответов.

modem.rar

Edited by hound

Share this post


Link to post
Share on other sites

А, вот какой обработчик. Это совсем другое дело! :biggrin:

А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные.

 

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

d5c376936842.jpg

Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+CREG: 0,1" и "OK"? Как обработчик отделяет сообщения друг от друга? По тайм-ауту, или по <cr>, <lf> вначале и конце сообщения?

Share this post


Link to post
Share on other sites
А, вот какой обработчик. Это совсем другое дело! :biggrin:

А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные.

 

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

d5c376936842.jpg

Как видно, это сплошной поток данных. Сможет ли Ваш обработчик разделить строку на "+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", которое в очередь не попадет уж точно.

Как-то так.

Share this post


Link to post
Share on other sites

В прерывании разбор строк лучше не делать. Прерывание должно отработать максимально быстро и выйти. В какой-то момент могут возникать "глюки" - или не успел отработать или потерял символ. Имхо лучше запоминать позиции концевиков 0x0D 0x0A, а разбор делать в основной программе.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this