Содержание

OSA : Задачи

Введение

Любая задача в ОСРВ OSA - это функция, тело которой содержит бесконечный цикл. Внутри цикла должен быть хотя бы один вызов сервиса ОС, который переключает контекст задач, иначе планировщик (а следовательно, и все остальные задачи) никогда не получит управление обратно.

Простейшая задача может выглядеть так:

void SimpleTask (void)
{
    for (;;)
    {
        OS_Yield();
    }
}

Если программа пишется под mikroC PRO, то линкеру нужно указывать, что функция задача будет вызвана через указатель, напимер:

#pragma funcall main SimpleTask

Задача может находиться в одном из 5-и состояний:

Кроме того, задача может иметь приоритет от 0 (высший) до 7 (низший). Приоритет можно изменять в ходе выполнения программы. Подробнее о приоритетах и состояниях.

Для работы с задачами OSA резервирует память под дескрипторы задач. В файле OSAcfg.h задается константа OS_TASKS, которая показывает, сколько одновременно активных задач может работать в программе.

Указатель на TCB

Большинство операций по управлению задачей осуществляется через указатель на ее дескриптор или tcb (Task Control Block). Дескрипторы задач недоступны напрямую, поэтому нужно создавать глобальную переменную типа OST_TASK_POINTER, и все опреации производить через нее:

OST_TASK_POINTER tp_MyTask;

Для работы с указателем он должен быть проинициализирован сервисом OS_Task_GetCur. Этот сервис желательно вызывать в самом начале функции-задачи:

void MtTask (void)
{
    tp_MyTask = OS_GetCurTask();
    for (;;) {
        /*....*/
    }
}

Для управления задачей из самой задачи в качестве параметра сервисов управления может применяться системный макрос this_task (или сам сервис OS_Task_GetCur):

void MyTask (void)
{
    for (;;) {
        /*...*/
        OS_Task_SetPriority(this_task, 0);    // Установить высший приоритет текущей задаче
        /*...*/
    }
}

Создание задачи

Созадется, т.е. делается активной задача сервисом OS_Task_Create, которому в параметрах передается начальный приоритет задачи и имя функции-задачи.

Например:

#include <osa.h>
 
void Task1 (void)
{
    for (;;)
    {
        OS_Yield()
    }
}
 
...
 
void main (void)
{
    OS_Init();
    OS_Task_Create(7, Task1);
    ...
    for (;;) OS_Sched();
}

В данном примере создается задача Task1 с низшим приоритетом. Теперь ОС будет знать, что есть такая активная задача и будет принимать ее в расчет, когда будет выбирать готовую задачу для выполнения. Сервис OS_Task_Create может вызываться в любом месте программы.

Если приоритетный режим не включен (OS_DISABLE_PRIORITY определена в файле OSAcfg.h), то параметр priority все равно нужно передавать, просто он будет игнорироваться. Это сделано для того, чтобы включать/отключать приоритетный режим было проще.

Ошибка при создании задачи

Если в памяти, отведенной системой под задачи, есть свободный дескриптор, то он будет выделен под новую задачу. Если свободных дескрипторов нет, то задача не будет создана, а после выполнения OS_Task_Create будет установлен флаг ошибки (его можно проверить сервисом OS_IsError).

    OS_Task_Create(7, Task1);
    if (OS_IsError()) ...	;	// Обрабатываем ошибку

Если такая ошибка возникает, то нужно либо увеличить значение константы OS_TASKS в файле OSAcfg.h и пересобрать проект, либо если предполагалось, что свободный дескриптор должен быть, искать ошибку в программе (например, предполагалось, что какая-то задача выполнит остановку, но по каким-то причинам ее не выполнила).

По завершению работы этого сервиса можно получить указатель на дескриптор вновь созданной задачи сервисом OS_Task_GetCreated() (бывает нужно, когда одна задача будет приостанавливаться или удаляться из другой):

    OST_TASK_POINTER  tp;
 
    OS_Task_Create(0, MyTask);
    if (!OS_IsError()) tp = OS_Task_GetCreated();

Если программа пишется под CCS, то в функции main для всех функций-задач нужно вызвать сервис OS_Task_Define

Удаление задачи

Остановить задачу (удалить ее из списка активных задач) можно сервисом OS_Task_Delete. Перед удалением следует быть уверенным в том, что задача освободила все занимаемые ей ресурсы и нет задач, ожидающих от нее событий.

Рекомендуется удалять задачу из самой себя.

При удалении задачи дескриптор освобождается и, если удаление производилось из самой задачи (с параметром this_task), то сразу же происходит передача управления планировщику. В случае удаления задачи самой себя после вызова OS_Task_Delete(this_task) уже ничего не будет выполняться. Например:

    ...
    OS_Task_Delete(this_task);
    Counter ++;
    ...

В этом примере переменная Counter не увеличится.

Если нужно остановить задачу, но при этом сразу же запустить другую, то нужно сначала запустить другую, а затем остановить текущую.

    . . .
    OS_Task_Create(1, Task_NewTask);
    OS_Task_Delete(this_task);
    . . .

При таком подходе есть недостаток: нужно резервировать память под большее количество задач, чем требуется. Например, в диктофоне есть несколько задач: обработка кнопок, индикация, воспроизведение, запись, проверка заряженности батареек. Здесь есть две задачи, которые не могут выполняться одновременно: запись и воспроизведение. Таким образом, активных задач в один момент времени может быть только 4. Переходя в запись из воспроизведения, нам нужно создавать новую задачу до завершения старой, а это требует наличия свободного дескриптора задач. Т.е. должно быть зарезервировано место под 5 задач (OS_TASKS = 5).

void Task_Play (void)
{
    . . .
    OS_Task_Create(1, Task_Record);
    OS_Task_Delete(this_task);
    . . .
}

Фактически получается, что несколько байт (один дескриптор задачи) будут просто заняты под ничто. Чтобы этого избежать, есть системный сервис OS_Task_Replace. Этот сервис удаляет текущую задачу, а на освободившееся место формирует новую. В нашем примере для диктофона это будет выглядеть так:

    . . .
    OS_Task_Replace(1, Task_Record);
    . . .

Изменение приоритета задачи

Есть еще два системных сервиса для работы с приоритетом текущей задачи.

Сервис Описание Свойства
char OS_Task_GetPriority (tp) Возвращает приоритет задачи
OS_Task_SetPriority (tp, priority) Изменить приоритет задачи на priority

Локальные переменные в задачах

Все активные (созданные) задачи могут выполняться параллельно, поэтому следует помнить, что их локальные переменные могут пересекаться, следовательно, их значения могут быть потеряны после передачи управления системе. Если нужно, чтобы переменная помнила свое значение, то ее нужно определять как static. Например:

void Task1 (void)
{
  static char s_cCounter;  // Эта переменная будет сохраняться
                           // при переключении контекста.
  char i, j;               // Эти переменные врЕменные, и их можно
                           // использовать только в пределах одного
                           // вызова задачи.
  . . .
}

Все сервисы для работы с задачами

Сервис Аргументы Описание Свойства
Создание/удаление
OS_Task_Define (TaskName) Сообщает компилятору, что функция вызывается по указателю (CCS и HT-PICC PRO) main()
OS_Task_Create (priority, TaskName) Инициализируем конкретную задачу. Нельзя вызывать из прерывания
OS_Task_Replace (priority, TaskName) Заменить текущую задачу на новую. Разрешен вызов только в контексте задачи
OS_Task_Delete (tp) Удалить задачу Переключает контекст
Управление состоянием
OST_TASK_POINTER
OS_Task_GetCur
Получить указатель на дескриптор текущей задачи Разрешен вызов только в контексте задачи
OST_TASK_POINTER
OS_Task_GetCreated
Получить указатель на дескриптор только что созданной задачи
OS_Task_Pause (tp) Приостановить задачу Переключает контекст
OS_Task_Continue (tp) Продолжить выполнение ранее приостановленной задачи
char
OS_Task_GetPriority
(tp) Возвращает приоритет текущей задачи
OS_Task_SetPriority (tp, priority) Изменить приоритет текущей задачи
OS_Task_IsPaused (tp) Проверить, приостановлена ли задача