STM Урок 106. FreeRTOS. Динамическое создание и уничтожение задач. Часть 2



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

 

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

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

 

char str1[60];

char str_buf[1000]={''};

 

Мы не знаем, сколько у нас будет задач, поэтому создадим буфер побольше, у нас памяти много.

Вернёмся в задачу по умолчанию и перед задержкой добавим следующий код

 

Task01Handle = osThreadCreate(osThread(tsk01), NULL);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 1:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

 

Мы вызвали функцию osThreadList, которая отправила в буфер информацию о задачах, затем передали в USART заголовок этапа (мы будем отслеживать наличие задач поэтапно), затем передали непосредственно активное содержимое буфера (до встречи с первым нулём, ибо именно так формируется строка с информацией). И чтобы отделить этапы друг от друга, перешли на следующую строку.

 

 

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

 

 

Мы видим, что у нас присутствуют 4 задачи: задача по умолчанию, задача myTask02, которую мы добавляли ещё одну в Cube MX, также есть наша задача tsk01 и задача IDLE, которая также называется задача «Бездействие», которая работает тогда, когда не работает ни одна задача, так как какой-то код должен всегда выполняться. Об этой задаче мы поговорим в более поздних занятиях, в которых мы также возможно с ней столкнёмся.

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

Теперь о значениях, расположенных после имени задачи. Буква R — это состояние задачи. В нашем случае Ready. Так как данная функция выполняется в ядре и, видимо, в этом момент задачи не выполняются, а находятся в состоянии готовности в очереди. Следующее значение — приоритет задачи, следующее — стек, и последнее — это идентификатор задачи, который у создаваемой задачи постоянно меняется, а у тех которые не уничтожаются, остаётся прежним.

Теперь давайте то же самое проделаем после задержки в 500 милисекунд, а затем ещё подождём 500 милисекунд

 

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 2:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

 

Здесь всё то же самое, за исключением заголовка стадии 2.

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

 

 

Мы видим, что во второй стадии у нас уже задачи tsk01 в списке нет, а остальные есть, то есть через 500 милисекунд после создания задача уже точно уничтожилась.

 

 

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

Для этого добавим ещё код в бесконечный цикл задачи по умолчанию

 

osDelay(500);

osThreadDef(tsk02, Task02, osPriorityNormal, 0, 128);

Task02Handle = osThreadCreate(osThread(tsk02), NULL);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 3:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 4:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

osThreadDef(tsk03, Task03, osPriorityNormal, 0, 128);

Task03Handle = osThreadCreate(osThread(tsk03), NULL);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 5:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

osThreadList((unsigned char *)str_buf);

sprintf(str1,"Stage 6:rn");

HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)str_buf,strlen(str_buf),0x1000);

HAL_UART_Transmit(&huart1,(uint8_t*)"rn",2,0x1000);

osDelay(500);

 

То есть мы сначала создаём задачу, отчитываемся о задачах в терминальной программе, ждём 500 милисекунд, отчитываемся ещё раз, ещё ждём 500 милисекунд, создаём вторую задачу, отчитываемся, ждём ещё 500 милисекунд, опять отчитываемся, ещё ждём 500 милисекунд, создаём третью задачу, отчитываемся, ждём ещё 500 милисекунд и последний раз отчитываемся. Затем цикл повторяется сначала. То есть у нас будет всего 6 стадий, 3 из которых сразу после создания задач, а остальные три — по истечении 500 милисекунд.

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

 

 

Мы видим, что счётчики у нас исправно обновляются раз в секунду, хотя не ровно секунду, а чуть больше. Это происходит из-за того, что у нас расходуется время на передачу в порт USART, если убрать передачу в USART, то процесс пойдёт чуть быстрее. Чтобы устранить эти неточности во времени, если нам нужен строго определённый период, проходящий между запусками процессов, есть несколько другая функция задержки, с которой мы также познакомимся, я думаю, в более поздних занятиях.

Теперь посмотрим результат в терминальной программе

 

 

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

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

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

 

 

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

 

Исходный код

 

 

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

 

 

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

 

STM FreeRTOS. Динамическое создание и уничтожение задач

2 комментария на “STM Урок 106. FreeRTOS. Динамическое создание и уничтожение задач. Часть 2
  1. Владимир:

    добрый день!
    хотел сделать уничтожение задачи по нажатию кнопки стоп в основной задаче…
    выполнение, конечно, сразу прекратилось… однако потом когда задача вновь создалась (по нажатию «старт») эта самая задача начала выполняться не с самого начала, а с шага завершения программы… не могли бы подсказать в чём дело?

    • Даже не знаю, что и сказать. Скорей всего оно всё так и должно быть. Поэтому лучше дать циклу задачи по нажатию кнопки выполниться до конца. Поэтому уничтожаемой задаче надо назначить приоритет выше, сделать отложенную обработку нажатия кнопки в задаче, которая, соответственно будет иметь приоритет ниже и код там будет выполняться, когда уничтожаемая задача выполнится до конца. Вернее не до конца, а там где есть задержка, а задержку вставить ближе к концу выполнения цикла.

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

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

*