:::: MENU ::::

Задачи RTOS

Данная серия статей посвящена описанию работы RTOS– написанной на основе TNKernel. Исходники вы можете скачать по этой ссылке.

Если вы хотите написать свою RTOS или разобраться в TNKernel читайте так-же:

Задачи

Каждая задача имеет свое описание — структура task_param.

typedef struct _task_param_init
{
    uint32_t *stk_top; // вершина стека (последний сохраненный в стек элемент)
    uint32_t *stk_start; // начало памяти, зарезервированной под стек
    uint32_t stk_size; // размер стека задачи

    uint8_t task_priority; // приоритет задачи

    uint32_t wakeup_count; // счетчик времени до выхода из sleep

    link_init task_queue; // очередь задач - связанным списком

    void *task_func; // указатель на функцию задачи
    void *task_func_param; // указатель на параметры задачи

} task_param_init;

Для создания каждой задачи, в файле user_func.c необходимо:

  • прописать стек
    align_attr_start unsigned int task_1_stack[128] align_attr_end;
    
    align_attr_start и align_attr_end - для выравнивания.
    
  • связать задачу со структурой
    task_param_init task_1;
    
  • связать задачу с функцией
    void task_1_func(void *par);
    
  • создать функцию задачи (обязательно должен быть бесконечный цикл)
    void task_1_func(void *par)
    {
        extern uint8_t g_flag;
    
        while(1)
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_6);
            GPIO_ResetBits(GPIOB, GPIO_Pin_6);
        }
    } 
    
  • в функции start_user_tasks прописать создание задачи
    void start_user_tasks(void)
    {
        task_create(&task_1,
                                task_1_func,
                                NULL,
                                1,
                                &task_1_stack[0],
                                128);                           
    }
    

NOTE: Кроме задач пользователя данной RTOS имеется еще две задачи самой ОС. idle_task_func (IDLE) — задача простоя. timer_task_func — задача переключения контекста. Их описание будет рассмотрено в другой статье.

Функция task_create — непосредственно создает задачу — связывает задачу со структурой, инициализирует ее стек и добавляет в очередь планировщика.

void task_create(task_param_init *task_param,
                                    void (*task_func) (void *param),
                                    void *param,
                                    uint8_t task_priority,
                                    uint32_t *task_stk_start,
                                    uint32_t task_stk_size)                                         
{
    uint32_t i;

    // задаем параметры задачи
    task_param->task_func = (void*)task_func;
    task_param->task_func_param = param;
    task_param->task_priority = task_priority;
    task_param->stk_start = (uint32_t*)&task_stk_start[task_stk_size - 1]; // стек заполняется со старшеего адреса обл. памяти к младшему - поэтому начало в конце массива под стек
    task_param->stk_size = task_stk_size;
    task_stk_start = task_param->stk_start;

    // для того, чтобы проще отслеживать область стека при отладке заполняем память FF
    for(i = 0; i < task_stk_size; i++)
    {
            *task_stk_start = 0xFFFFFFFF;
            task_stk_start--;
    }

    task_param->stk_top = tn_stack_init(task_param->task_func,
                             task_param->stk_start,
                             task_param->task_func_param);

    // добавление задачи в очередь по своему приоритету
    queue_task_add(task_param);
}

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

ris1

При добавлении нового элемента  — он добавляется в конец очереди для данного приоритета. Как следующая задача, указывается первая. Как последняя, предыдущая последняя (см. код).

// добавление задачи в конец очереди по своему приоритету
void queue_task_add(task_param_init* task)
{
    link_init *temp_link;
    
    // ищем последний элемент в очереди для данного приоритета данной задачи
    temp_link = &task_queue_priority[task->task_priority];
    while((*temp_link).next != temp_link)
    {
        temp_link = temp_link->next;
    }   
    
    // добавляем данную задачу в конец очереди
    temp_link->next = &(task->task_queue);
    task->task_queue.prev = temp_link;
    task->task_queue.next = &task->task_queue; 
}