Перейти к содержанию
    

hound

Участник
  • Постов

    12
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные hound


  1. В общем, думаю сделать следующее:

    при получении в прерывании сигнала о "другом режиме" выставлять флаг и в начале цикла этот флаг проверять, если он выставлен - выполнить нужное действие и дальше вернуться в "стандартный" режим.

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

     

    А, например, как лучше поступить с такой задачей:

    есть 2 таска,

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

    во втором таске это сообщение обрабатывается и ответ отправляется обратно первому таску.

    Городить 2 очереди?

    В первой исходящее сообщение, а во второй ждем ответа?

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

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

    Перезапуск основной задачи тоже не вариант, т.к в начале этой задачи идет инициализация этого устройства и его первая перезагрузка, что в "разгаре" работы нельзя делать.

  3. Добрый день, есть таск для работы с некоторым внешним устройством.

    Устройство общается с МК по юарту, данные в прерывании складываются в буфер и очередьми отправляется в этот таск.

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

    Т.е, грубо говоря:

    while (1) {
      func_1();
      func_2();
      func_3();
      func_4();
      func_5();  
    }

     

    Время выполнения каждой функции разное, примерно от 100мс до 1 сек.

     

    Но само устройство может перейти в другой режим работы, для которого нужно будет выполнять уже другие функции. И устройство может перейти в этот режим в любой момент.

    Когда устройство переходит в другой режим оно отправляет определенное сообщение МК. Это сообщение отлавливается в прерывании и дальше уже нужно заставить таск выйти из этого цикла и перейти в другой. Каким способ лучше подобное реализовать?

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

     

     

  4. А, вот какой обработчик. Это совсем другое дело! :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", которое в очередь не попадет уж точно.

    Как-то так.

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

     

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

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

     

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

     

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

     

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

     

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

     

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

    modem.rar

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

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

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

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

     

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

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

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

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

  8. Откопал же тему)) надеюсь тут еще кто-то есть и увидит мое сообщение)

    Ковыряю модем - 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;
        }
      }
    }

     

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

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

     

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

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

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

     

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

  10. Да, действительно очень и очень глупая ошибка...конечно же проще все делать в одном таске, чем городить новые.

    Спасибо за разъяснение.

     

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

    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 сек.) чтобы другие задачи могли выполняться.

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

    Или же можно реорганизовать работу данного таска?

     

  11. Добрый вечер, начал работать с 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);

     

    Но на сколько это оправданно?

    Заранее благодарен за ответ.

×
×
  • Создать...