STM Урок 111. FreeRTOS. Очереди. Часть 2



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

 

Теперь мы попробуем в очереди передать целую структуру.

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

Поэтому добавим ещё одну глобальную переменную для очереди. Это будет несколько другой тип

 

osMessageQId pos_Queue;

osMailQId strout_Queue;

 

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

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

 

} struct_arg;

typedef struct struct_out_t {

  uint32_t tick_count;

  char str[60];

} struct_out;

 

Тогда и макрос тоже сделаем отдельный для такого типа очередей

 

#define QUEUE_SIZE (uint32_t) 1

#define MAIL_SIZE (uint32_t) 1

 

В main() создадим вторую очередь

 

pos_Queue = osMessageCreate(osMessageQ(pos_Queue), NULL);

osMailQDef(stroutqueue, MAIL_SIZE, struct_out);

strout_Queue = osMailCreate(osMailQ(stroutqueue), NULL);

 

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

 

arg = (struct_arg*) argument;

struct_out *qstruct;

 

Для очередей такого типа необходимо также выделить память. Сделаем это в бесконечном цикле в самом начале

 

for(;;)

{

  qstruct = osMailAlloc(strout_Queue, osWaitForever);

 

Запишем количество системных квантов и имя функции в строку в структуру очереди, взяв имя из параметров

 

qstruct = osMailAlloc(strout_Queue, osWaitForever);

qstruct->tick_count = osKernelSysTick();

sprintf(qstruct->str, "%s %d", arg->str_name, osThreadGetPriority(NULL));

 

Отправим структуру в очередь

 

osMessagePut(pos_Queue, arg->y_pos, 100);

osMailPut(strout_Queue, qstruct);

 

 

В функции задачи-приёмника TaskStringOut добавим ещё одну переменную типа структуры состояния очереди, а также переменную типа передаваемой в очереди структуры

 

osEvent event, event1;

struct_out *qstruct;

 

В бесконечном цикле заберём данные из очереди, тем самым заодно получим все статусы

 

for(;;)

{

  event1 = osMailGet(strout_Queue, osWaitForever);

 

Условие, находящееся в бесконечном цикле соберём ещё в тело другого условия

 

if (event1.status == osEventMail)

{

  if (event.status == osEventMessage)

  {

    sprintf(str1,"task %lu", osKernelSysTick());

    TFT_DisplayString(120, event.value.v, (uint8_t *)str1, LEFT_MODE);

  }

}

 

Таким образом мы проверим состояние обоих очередей. Вообще-то, так делать нежелательно, но у нас код построен так. что это сработает. Потом мы всё подправим.

В теле условия самого нижнего уровня присвоим переменной типа структуры очереди указатель на элемент очереди

 

if (event.status == osEventMessage)

{

  qstruct = event1.value.p;

 

И затем немного исправим вывод строки на дисплей, в котором помимо данных из первой очереди мы воспользуемся данными из второй — структурированной

 

sprintf(str1,"%s %lu", qstruct->str, qstruct->tick_count);

 

Соберём проект, прошьём контроллер и посмотрим результат

 

 

 

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

 

 

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

Удалим объявление очереди

 

osMessageQId pos_Queue;

 

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

 

typedef struct struct_out_t {

  uint32_t tick_count;

  uint16_t y_pos;

 

Удалим макрос

 

#define QUEUE_SIZE (uint32_t) 1

 

В main() удалим создание очереди

 

osMessageQDef(pos_Queue, QUEUE_SIZE, uint16_t);

pos_Queue = osMessageCreate(osMessageQ(pos_Queue), NULL);

 

В функции задач Task01 отправим в структуру вертикальную координату

 

qstruct->tick_count = osKernelSysTick();
qstruct->y_pos = arg->y_pos;

 

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

 

osMessagePut(pos_Queue, arg->y_pos, 100);

 

Удалим из функции TaskStringOut одну переменную состояния очереди

 

osEvent event, event1;

 

Также удалим строку, где выбираем данные из обычной очереди

 

event = osMessageGet(pos_Queue, 100);

 

Поправим переменную в следующей строке, убрав из неё единичку

 

event = osMailGet(strout_Queue, osWaitForever);

 

В условии также поправим

 

if (event.status == osEventMail)

 

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

 

if (event.status == osEventMail)

{

  if (event.status == osEventMessage)

  {

    qstruct = event.value.p;

    sprintf(str1,"%s %lu", qstruct->str, qstruct->tick_count);

    TFT_DisplayString(120, qstruct->y_pos, (uint8_t *)str1, LEFT_MODE);

  }

}

 

Соберём проект, прошьём контроллер, и посмотрим результат. У нас работает всё как и прежде не смотря на то, что мы всё объединили в одну очередь.

Итак, сегодня мы научились пользоваться очередями в операционной системе FreeRTOS, причём очереди мы умеем использовать как с элементами какого-то определённого типа переменной, так и со структурированными типами, что позволяет нам передавать из задачи в задачу данные одновременно нескольких типов, а также массивов.

Всем спасибо за внимание!

 

 

Предыдущая часть Программирование МК STM32 Следующий урок

 

Исходный код

 

 

Отладочную плату можно приобрести здесь 32F746G-DISCOVERY

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

STM FreeRTOS. Очереди

16 комментариев на “STM Урок 111. FreeRTOS. Очереди. Часть 2
  1. imperror:

    Уважаемый автор, подскажите, не будет ли в этом месте:
    for(;;)
    {
    qstruct = osMailAlloc(strout_Queue, osWaitForever);

    утечки памяти. На каждой итерации цикла, в qstruct будет выделяться новый кусок памяти, а что его удаляет?
    Может qstruct = osMailAlloc(strout_Queue, osWaitForever) должна быть до цикла?

    • Я думал об этом, но скорей всего не будет. У меня код работал почти сутки, не завис. В примере в репозитории было то же самое. Хотите, попробуйте до цикла, но заранее вижу, что работать не будет.

      • imperror:

        Да. Вы правы, функция osMailAlloc очищает и выделяет память одновременно.

        • Задался тем же вопросом. Но тогда зачем вот это:
          /// Free a memory block from a mail.
          /// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
          /// \param[in] mail pointer to the memory block that was obtained with \ref osMailGet.
          /// \return status code that indicates the execution status of the function.
          /// \note MUST REMAIN UNCHANGED: \b osMailFree shall be consistent in every CMSIS-RTOS.
          osStatus osMailFree (osMailQId queue_id, void *mail);

          • Тоже, сразу при просмотре кода, задался таким же вопросом. Недавно нашел неплохой код (проект) для отлова утечек и пр. во FreeRTOS, но т.к. только начал изучать материал по этой теме и еще не был готов его использовать, то потерял эту ссылку/материал. Может кто-то посоветует по этой теме что-то дельное? Спасибо. Если бы Владимир перешел бы снова к теме FreeRTOS, особливо к использованию STM32F746-Disco (ведь уже есть по ней наработки, да и плаха очень удалась у STM), то было очень-очень неплохо.

  2. NKP144:

    Как я понял, после того, как получили mail, память нужно освободить. Делается это функцией osMailFree (osMailQId queue_id, void *mail). Из того же примера, в функции, которая принимает mail, в конце обработки приёма mail используется функция osMailFree(mailId, pRMail).

  3. Евгений Войтюк:

    Здравствуйте, ошибочка у Вас закралась . Необходимо в функцию void TaskStringOut(void const * argument) в участке
    if (event.status == osEventMail) дописать
    osMailFree(strout_Queue, qstruct); /* free memory allocated for mail */
    …без этого работать не будет…а так все норм.

    • Это не страшно, я знаю. Без этого работает.
      Вообще-то надо, причём и в примере тоже ничего нет.
      Без этого работать не будет, если мы будем использовать более древнюю схему распределения памяти, а при пятой это само происходит.

  4. Сергей:

    Здравствуйте.
    Скажите как передать данные из двух разных задач в третью?

  5. Сергей:

    Доброго времени суток.
    А где же описано (вообще откуда растут функции) файла cmsis_os.c? В смысле что в мануале Mastering the FreeRTOS ™ Real Time Kernel. или The FreeRTOS™ Reference Manual вообще нет описания ни одной функции с префиксом os.

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*