:::: MENU ::::

Чередование задач при помощи отправки задачи в конец очереди для данного приоритета

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

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

Чередование задач при помощи отправки задачи в конец очереди для данного приоритета

Допустим мы создали две задачи, каждая из которых моргает собственным светодиодом. Приоритеты этих задач одинаковы, но одна из этих задач будет всегда следовать за другой в очереди по данному приоритету, а поскольку у нас из очереди вызывается всегда первая задача(которая чередуется с timer_task), то вторая задача в этой очереди выполнятся никогда не будет.

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

Итак у нас есть две задачи:

void task_1_func(void *par)
{
    while(1)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_6);
        GPIO_ResetBits(GPIOB, GPIO_Pin_6);
        task_move_tail_queue_priority();
    }
}

void task_2_func(void *par)
{   
    while(1)
    {
        GPIO_SetBits(GPIOB, GPIO_Pin_7);
        GPIO_ResetBits(GPIOB, GPIO_Pin_7);
        task_move_tail_queue_priority();
    }   
}

Функция task_move_tail_queue_priority — перекладывает выполняемую задачу в конец очереди и дает следующей задачи в очереди выполнится.

// задача переходит в конец очереди для данного приоритета
void task_move_tail_queue_priority(void)
{
    uint8_t priority;

    arm_disable_interrupts();

    queue_remove_task(&(curr_run_task->task_queue));
    queue_task_add(curr_run_task);

    priority = queue_find_priority_next_task();
    next_task_to_run = task_param_from_task_queue(task_queue_priority[priority].next);

    switch_context();

    arm_enable_interrupts();
}

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

Функция queue_remove_task удаляет элемент данной задачи из связанного списка — удаление элемента из очереди.

// удаление задачи из списка
void queue_remove_task(link_init *task_queue)
{
     if(task_queue->next == task_queue)
     {
         task_queue->prev->next = task_queue->prev;
     }
     else
     {
         task_queue->prev->next = task_queue->next;
     }
   task_queue->next->prev = task_queue->prev;
}

Функция queue_task_add добавляет данную задачу в конец связанного списка — в конец очереди задач для данного приоритета(приоритет определяется по приоритету данной задачи). Т.е. в совокупности с предыдущей функцией эта функция удаляет задачу из начала очереди и добавляет в конец очереди.

Note: последний элемент в очереди указывает сам на себя.

// добавление задачи в конец очереди по своему приоритету
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; 
}

После находим по приоритетам и очереди задач по приоритетам следующую задачу(подробнее см. в статье Работа планировщика RTOS) и переключаем контекст на новую задачу.

Таким же образом можно и совсем удалить задачу. Функция удаления будет отличаться от функции переноса в конец очереди —  только добавлением удаленного элемента обратно в очередь:

// выход из задачи навсегда - задача не будет учавствовать в планировании
void task_exit(void)
{
    uint8_t priority;

    arm_disable_interrupts();

    queue_remove_task(&(curr_run_task->task_queue));

    priority = queue_find_priority_next_task();
    next_task_to_run = task_param_from_task_queue(task_queue_priority[priority].next);

    switch_context();       

    arm_enable_interrupts();
}