hound
-
Постов
12 -
Зарегистрирован
-
Посещение
Сообщения, опубликованные hound
-
-
Переход в другую задачу еще не совсем вариант, потому что это в этом "другом режиме работы" нужно девайсу отправить буквально одну команду.
Идеальным вариантом было бы при получении в прерывании уведомления о этом режиме, например, выставлять флаг и запускать сразу следующую итерацию цикла.
Перезапуск основной задачи тоже не вариант, т.к в начале этой задачи идет инициализация этого устройства и его первая перезагрузка, что в "разгаре" работы нельзя делать.
-
Опубликовано · Изменено пользователем hound · Пожаловаться
Bulya, вариант да, но не охота создавать еще другие таски, стараюсь придерживаться принципа: 1-девайс подключенный к МК - 1 таск для него. -
Добрый день, есть таск для работы с некоторым внешним устройством.
Устройство общается с МК по юарту, данные в прерывании складываются в буфер и очередьми отправляется в этот таск.
В таксе есть основной бесконечный цикл, в котором по очереди выполняются разные функции.
Т.е, грубо говоря:
while (1) { func_1(); func_2(); func_3(); func_4(); func_5(); }
Время выполнения каждой функции разное, примерно от 100мс до 1 сек.
Но само устройство может перейти в другой режим работы, для которого нужно будет выполнять уже другие функции. И устройство может перейти в этот режим в любой момент.
Когда устройство переходит в другой режим оно отправляет определенное сообщение МК. Это сообщение отлавливается в прерывании и дальше уже нужно заставить таск выйти из этого цикла и перейти в другой. Каким способ лучше подобное реализовать?
Проверка на наличии сообщения о переходе в другой режим при проверки очереди в каждой функции не вариант, т.к функций в основном цикле может быть намного больше и каждый раз делать проверку кажется лишним.
-
А, вот какой обработчик. Это совсем другое дело!
А то я так понял, что обработчик будет ждать только один ответ (заданный из основной программы), и пропускать все остальные.
Могу в качестве посильной помощи выложить скрин с логического анализатора, как выдаётся из модема ответ "+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", которое в очередь не попадет уж точно.
Как-то так.
-
Опубликовано · Изменено пользователем hound · Пожаловаться
А как Вы будете обрабатывать unsolicited messages?В функции обработчике буфера от модема (в прерывании) до того как отправить буфер очередью проверяются такие данные от модема:
1. OK - выставляется флаг, который ожидает задача после отправки команды модему, если за N-ое кол-во мсек после отправки команды флаг не выставился - значит модем не ответил и дальше уже по обстоятельству (перазагрузка модема и т.п)
2. > - готов отправить данные серверу.
3. SEND OK - аналогично с флагом ОК только этот флаг ожидается после отправки данных "на сервер".
4. +RECEIVE: - получили данные "с сервера" и записываем приходящие данные в прерывании не в буфер модема, а в буфер входящих сообщений.
И уже если эти 4 строчки не совпали смотрим отправлять ли очередь или нет, опять же в зависимости от того на какую команду ответ мы ждем.
К сообщению приложил модуль, который на текущий момент у меня получился, пока еще без подключения к серверу и без полноценного разбора ответов.
-
Ну кроме работы с модемом нужно повешать еще и другие задачи, поэтому и использую операционку:
один таск под модем,
второй под работу с ГПС и т.д. каждый таск под свое устройство.
а в случае с GSM-модемом, пауза между отправкой команды и получением ответа может составлять секундыНе совсем понял, а почему может составлять секунды? Модем висит на одном ЮАРТе, другие девайсы на другом и т.п под каждый юарт свой обработчик прерывания.
-
А если сделать так:
перед отправкой команды модему в глобальную перемену заносим начало ответа на команду, в случае с командой AT+CREG?
это будет "+CREG", а в прерывании, вернее в функции обработчике буфера в прерывании будем сравнивать полученный ответ от модема с этой переменной. Тогда не нужно будет прочесывать каждый раз таблицу...
-
Опубликовано · Изменено пользователем hound · Пожаловаться
Откопал же тему)) надеюсь тут еще кто-то есть и увидит мое сообщение)
Ковыряю модем - Quectel m66, но особой роли не играет, главное те же самые АТ команды.
Использую операционку TNKernel, но тоже особой роли не играет, разве что в этой системе в очереди передается указатель на данные, а не сами данные как, например, во FreeRTOS. К этому чуть позже вернемся)
1. Сделал отдельный таск под работу с модемом.
2. Линейный буфер, куда складываются все приходящие данные с модема, которые потом из прерывания передаются в функцию обработчик (modem_process_buffer).
В функции обработчике (modem_process_buffer) проверяется строка от модема со следующими условиями:
если пришел ответ 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; }
Тут все просто, нужно только дождаться чтобы флаг ОК был выставлен. Сложности начинаются с командами в которых нужно парсить ответ от модема.
Например, команда AT+CREG?
На нее модем отвечает следующим образом:
+CREG: 0, 1 OK
Нужно дождаться именно этого ответа. Сделал следующим образом:
Перед отправкой команды помещаю в некоторые переменные условия по которым мы должны отправлять очередь:
uint8_t modem_creg(void) { uint32_t rc, flag, number_queue; // Используется в прерывании для определения условия отправки очереди strcpy(wait_data, "+CREG"); wait_length = 5; uart3_ex_send_string("AT+CREG?\r\n"); // Ждем ответ ОК от модема, если ОК пришло значит команда успешно выполнена rc = tn_event_wait(&modem_event, MODEM_SAY_OK, TN_EVENT_WCOND_OR, &flag, 300); if (rc != TERR_NO_ERR) { dbg_send("CREG fail\r\n"); return ACTION_FAIL; } tn_event_clear(&modem_event, ~(MODEM_SAY_OK)); // В очереди получаем порядковый номер в массиве для нашего сообщения tn_queue_receive_polling(&queue_modem_buffer, (void**)&number_queue); dbg_send("Answer CREG - "); dbg_send(modem_queue_buffer[number_queue]); dbg_send_rn(); return ACTION_OK; }
И в свою очередь в функции modem_process_buffer (в прерывании):
if (strncmp(modem_line_buffer, wait_data, wait_length) == 0) { modem_send_queue(); }
Либо отправлять в очередь все что пришло и потом уже в функции отправки команды перебирать очереди пока не нашли нужный нам ответ, но так кажется более затратно...
И стоит ли в подобных командах CREG, где нужно разбирать ответ от модема дожидаться именно приходящего ОК сообщения? Или можно просто дождаться уже ответа от модема +CREG и его разбирать, а после флаг ОК просто сбросить...
Ах да, и вот сам таск для модема:
void modem_queue_func(void *par) { dbg_send("Task modem start\r\n"); while (1) { modem_reset(); if (modem_check_at() == ACTION_FAIL) continue; if (modem_qimux() == ACTION_FAIL) continue; while (1) { if (modem_check_at() == ACTION_FAIL) break; if (modem_qistate() == ACTION_FAIL) break; if (modem_cgatt() == ACTION_FAIL) break; if (modem_creg() == ACTION_FAIL) break; } } }
Пока без подключений к серверу, отправки данных и т.п главное пока разобраться с правильностью разбора команды и сообщений от модема.
-
Добрый день. Есть 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);
Но это кажется сильно натянутым решением...
Эхо от модема отключать не желательно,хотя это отчасти и решило бы проблему, но опять же не полностью.
И кроме однострочных команд, модем может выдать много строчные команды.
В общем, у кого-нибудь был опыт работы с АТ командами, кто как реализовал? Заранее благодарен за любой совет/пинок в нужную/правильную сторону.
-
Да, действительно очень и очень глупая ошибка...конечно же проще все делать в одном таске, чем городить новые.
Спасибо за разъяснение.
Но появился следующий вопрос. После того как мы подали на некоторое время сигнал на ножку девайса нам нужно его настроить и если он не отвечает то его снова перезагрузить подачей сигнала на ножку. Как я себе представляю данный код в таком случае:
void task_device(void *par) { while (1) { dbg_send("Reboot device\r\n"); GPIOB->BSRR=GPIO_Pin_1; tn_task_sleep(3000); GPIOB->BRR=GPIO_Pin_1; tn_task_sleep(2000); if (check_device() == ACTION_FAIL) continue; if (change_setting() == ACTION_FAIL) continue; while (1) { rc = tn_queue_receive_polling(&queue_modem_send_data, (void **)&pos); if (rc == TERR_NO_ERR) { device_send_message(); } if (check_device() == ACTION_FAIL) break; tn_task_sleep(1000); } } }
Если девайс нам не отвечает на команды, то мы просто перезапускаем цикл.
А уже во внутреннем цикле идет дальнейшая работа с девайсом, проверяем есть ли сообщения в очереди, если нет, то проверяем не подвис ли девайс, если подвис, то выходит из внутреннего цикла, а во внешнем цикле все по новой - перезагрузка, настройка и т.п
И задача засыпает на 1000 тиков (в данном случае на 1 сек.) чтобы другие задачи могли выполняться.
Использовать очередь с блокировкой задачи не совсем вариант, т.к нам нужно время от времени проверять состояние устройства.
Или же можно реорганизовать работу данного таска?
-
Добрый вечер, начал работать с TNKernel использую версию 2.7.
Есть один вопрос:
нужно определенную функцию выполнять через некоторое время системных тиков, сейчас реализовал через задачи подобным образом:
#define REBOOT_START 0x00 #define REBOOT_UP 0x01 #define REBOOT_END 0x02 void func_reset(void *par) { uint8_t status = REBOOT_START; uart-send_string("Start activate modem"); while (1) { switch(status_reboot) { case REBOOT_START: GPIOB->BSRR=GPIO_Pin_1; status = REBOOT_UP; tn_task_sleep(3000); break; case REBOOT_UP: GPIOB->BRR=GPIO_Pin_1; status = REBOOT_END; tn_task_sleep(1000); break; case REBOOT_END: send_cmd_device(POWER_UP); tn_task_exit(TN_EXIT_TASK); break; } } }
Т.е. создаю задачу без ее запуска при создании, а потом уже в коде программы, там где нужно запустить/перезагрузить девайс я запускаю эту задачу
tn_task_activate(&task_reset);
Но на сколько это оправданно?
Заранее благодарен за ответ.
TNKernel переход в другую часть таска
в Операционные системы
Опубликовано · Пожаловаться
В общем, думаю сделать следующее:
при получении в прерывании сигнала о "другом режиме" выставлять флаг и в начале цикла этот флаг проверять, если он выставлен - выполнить нужное действие и дальше вернуться в "стандартный" режим.
Обмен идет только синхронный, отправили команду устройству - ждем от него ответа, ответ получен отправляем следующую команду и так до бесконечности.
А, например, как лучше поступить с такой задачей:
есть 2 таска,
первый таск отправляет через очередь сообщение второму таску,
во втором таске это сообщение обрабатывается и ответ отправляется обратно первому таску.
Городить 2 очереди?
В первой исходящее сообщение, а во второй ждем ответа?