Работа с задачами
Количество задач, которые можно создать в FreeRTOS, ограничено только возможностями железа. Для управления задачами система предоставляет ряд функций: создание задачи vTaskCreate()
(osThreadCreate()
); уничтожение vTaskDelete()
(osThreadTerminate()
); управление приоритетом uxTaskPriorityGet()
и vTaskPrioritySet()
; и управляющие vTaskDelay()
, vTaskDelayUntil()
, vTaskSuspend()
, vTaskResume()
, vTaskResumeFromISR()
.
Сами задачи определяются как обычные Си-функции, они ничего не возвращают и принимают в качестве аргумента указатель на тип void *
. Одна функция (тело задачи) — не равно одна задача. Программист вправе создать несколько экземпляров (англ. instance) одной и той же задачи. Из стандарта языка следует, что переменные, созданные в теле функции, для каждого экземпляра будут отдельными, если только не используется модификатор static
.
В общем случае задача должна иметь бесконечный цикл (во FreeRTOS принято использовать for
вместо while
), в котором описывается логика. При помощи функции vTaskDelay()
(osDelay()
) можно отдать управление операционной системе на ожидаемое время задержки, чтобы та могла запустить другую задачу. Для указания абсолютной задержки следует использовать функцию vTaskDelayUntil()
(osDelayUntil()
). Если задача по какой-то причине выходит из бесконечного цикла, то ее следует удалить через вызов vTaskDelete(NULL)
(osThreadTerminate()
). При передаче NULL
в качестве аргумента функция сама определит идентификатор задачи.
void task_01( void *pvParameters ) {
static uint32_t a = 0; // sharable variable
uint32_t a = 0; // unique variable
for( ;; )
{
if (smpr_flag)
break;
// code here
vTaskDelay(10);
}
vTaskDelete( NULL );
}
Функция xTaskCreate()
позволяет создать задачу и имеет следующие аргументы:
pvTaskCode
— указатель на функцию с кодом задачи;pcName
— имя, заданное программистом, оно нужно только для отладки;usStackDepth
— глубина стека в машинных словах (т.е. кратна 4 байтам для stm32);pvParameters
— указатель на аргументы задачи;uxPriority
— приоритет задачи от0
доMAX_PRIORITIES
;pxCreatedTask
— указатель на идентификатор, позволяющий обрабатывать задачу (может быть задан какNULL
).
portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode,
const signed portCHAR * const pcName,
unsigned portSHORT usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pxCreatedTask
);
Функция вернет pdTRUE
в случае, если в куче достаточно места, и pdFALSE
, если места нет. Минимальный размер стека определяется через макрос configMINIMAL_STACK_SIZE
в файле конфигурации. Вообще говоря, определение размера стека — нетривиальная задача, и чаще устанавливается оценочное значение, нежели точно рассчитанное.
Когда требуется передать через аргумент более одного параметра, создают специальную структуру, инициализируют ее и передают в виде указателя на void
. Разыменование происходит непосредственно в задаче.
typedef struct {
uint8_t x;
uint8_t y;
uint8_t z;
} agr_t;
static arg_t task01_args = { .x = 0, .y = 1, .z = 2 };
// ...
void task_01(void const * argument) {
volatile arg_t *arg = (arg_t*) argument;
argument->x = 3;
// ...
Уничтожить задачу можно, вызвав функцию xTaskDelete()
, принимающую в качестве аргумента только заданный при создании идентификатор pxCreatedTask
.
void vTaskDelete( xTaskHandle pxTask );
После удаления ответственность за освобождение выделенной под нее памяти ложиться на задачу idle
. Заметьте, что выделенная в самой задаче программистом память должна быть освобождена самим программистом.