STM Урок 108. FreeRTOS. Параметры

 

 

 

Продолжаем работу с операционной системой реального времени FreeRTOS.

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

Оказывается, есть такой механизм — это механизм передачи параметров в функцию, которые мы туда передаём в момент создания задачи.

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

Можно передать один, два, одним словом сколько угодно параметров. Если нам нужно передать только один параметр, то мы создаём переменную любого типа, присваиваем её значение и передаём разименованный указатель типа void на эту переменную в качестве параметра. А если мы хотим передать несколько параметров, пусть даже различного типа, то тогда мы создаём структуру с разными переменными, также создаём переменную типа данной структуры, затем каждой переменной, которая станет уже полем структуры, присваиваем какие-либо значения и передаём уже указатель на данную структуру.

Так как работать мы будем с той же платой — STM32F746G-DISCOVERY, то для того, чтобы поработать с параметрами, мы создадим проект из проекта прошлого занятия CREATE_TASKS с именем PARAM01, откроем его в проектогенераторе Cube MX, и, вообще ничего не трогая сгенерируем проект для SystemWorkbench, откроем его там, настроим оптимизацию 1 и уберём отладочную конфигурацию, если та будет иметь место в проекте.

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

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

 

osThreadId Task01Handle,Task02Handle,Task03Handle;

typedef struct struct_arg_t {

  char str_name[10];

  uint16_t y_pos;

  uint32_t delay_per;

} struct_arg;

struct_arg arg01, arg02, arg03;

 

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

Из функции задачи по умолчанию StartDefaultTask убираем инициализацию SDRAM, LTDC и вывод шапки, в бесконечном цикле оставляем только небольшую задержку, а до бесконечного цикла выведем в USART список существующих задач с их некоторыми свойствами

 

/* USER CODE BEGIN 5 */

osThreadList((unsigned char *)str_buf);

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

HAL_UART_Transmit(&huart1,(uint8_t*)"\r\n",2,0x1000);

/* Infinite loop */

for(;;)

{

  osDelay(1);

}

 

А инициализацию нашей памяти и дисплея, а также вывод шапки на дисплей, мы добавим опять в функцию main()

 

/* USER CODE BEGIN 2 */

MT48LC4M32B2_init(&hsdram1);

HAL_LTDC_SetAddress(&hltdc,LCD_FRAME_BUFFER,0);

TFT_FillScreen(LCD_COLOR_BLACK);

TFT_SetFont(&Font24);

TFT_SetTextColor(LCD_COLOR_LIGHTGREEN);

TFT_DisplayString(0, 10, (uint8_t *)"Create tasks", CENTER_MODE);

TFT_SetTextColor(LCD_COLOR_MAGENTA);

TFT_DisplayString(14, 60, (uint8_t *)"Task1:", LEFT_MODE);

TFT_DisplayString(14, 110, (uint8_t *)"Task2:", LEFT_MODE);

TFT_DisplayString(14, 160, (uint8_t *)"Task3:", LEFT_MODE);

/* USER CODE END 2 */

 

Если мы теперь соберём и прошьём проект, то мы в терминальной программе теперь не увидим вновь созданных задач, так как мы удалили код их создания. Мы увидим лишь задачи, добавленные в Cube MX и задачу «Бездействие»

 

 

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

В функции main() после создания уже существующих задач, но до вызова планировщика, создадим пока одну задачу и пока без параметров

 

/* USER CODE BEGIN RTOS_THREADS */

/* add threads, ... */

osThreadDef(tsk01, Task01, osPriorityIdle, 0, 128);

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

/* USER CODE END RTOS_THREADS *

 

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

 

void Task01(void const * argument)

{

  TFT_SetTextColor(LCD_COLOR_BLUE);

  for(;;)

  {

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

    TFT_DisplayString(280, 60, (uint8_t *)str1, RIGHT_MODE);

    osDelay(1000);

  }

}

 

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

 

 

Пока у нас всё работает безо всяких параметров.

 

 

Вернёмся в функцию main() и проинициализируем наши параметры до создания нашей пока одной задачи

 

/* add threads, … */

strcpy(arg01.str_name,"task1");

strcpy(arg02.str_name,"task2");

strcpy(arg03.str_name,"task3");

arg01.y_pos = 60;

arg02.y_pos = 110;

arg03.y_pos = 160;

arg01.delay_per = 1000;

arg02.delay_per = 677;

arg03.delay_per = 439;

osThreadDef(tsk01, Task01, osPriorityIdle, 0, 128);

 

Мы здесь занесли значения во все поля наших структур — имена задач, позиции по вертикали, и значение задержки в милисекундах.

Теперь давайте при создании нашей первой задачи передадим в неё указатель на структуру с параметрами

 

Task01Handle = osThreadCreate(osThread(tsk01), (void*)&arg01);

 

Теперь у нас в задачу передан указатель на три параметра.

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

 

void Task01(void const * argument)

{

  volatile struct_arg *arg;

  arg = (struct_arg*) argument;

  TFT_SetTextColor(LCD_COLOR_BLUE);

 

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

 

for(;;)

{

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

  TFT_DisplayString(280, arg->y_pos, (uint8_t *)str1, RIGHT_MODE);

  sprintf(str1,"  %s   ", arg->str_name);

  TFT_DisplayString(275, arg->y_pos, (uint8_t *)str1, LEFT_MODE);

  osDelay(arg->delay_per);

}

 

Первый параметр мы использовали для позиции по вертикали, второй — для вывода на дисплей имени задачи, а третий — для значения задержки.

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

 

 

Отлично! Параметры передались, всё спозиционировалось, задержка срабатывает через заданное время и строка выводится.

Давайте аналогичным образом в функции main() создадим остальные две задачи и передадим им указатели на параметры, предназначенные для них

 

Task01Handle = osThreadCreate(osThread(tsk01), (void*)&arg01);

osThreadDef(tsk02, Task01, osPriorityIdle, 0, 128);

Task02Handle = osThreadCreate(osThread(tsk02), (void*)&arg02);

osThreadDef(tsk03, Task01, osPriorityIdle, 0, 128);

Task03Handle = osThreadCreate(osThread(tsk03), (void*)&arg03);

/* USER CODE END RTOS_THREADS */

 

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

 

 

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

При старте нашей программы в терминальной программе мы также видим, что все наши задачи в списке присутствуют

 

 

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

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

 

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

 

Исходный код

 

 

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

 

 

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

 

STM FreeRTOS. Параметры

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

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

*