Любая задача в ОСРВ OSA - это функция, тело которой содержит бесконечный цикл. Внутри цикла должен быть хотя бы один вызов сервиса ОС, который переключает контекст задач, иначе планировщик (а следовательно, и все остальные задачи) никогда не получит управление обратно.
Простейшая задача может выглядеть так:
void SimpleTask (void) { for (;;) { OS_Yield(); } }
#pragma funcall main SimpleTask
Задача может находиться в одном из 5-и состояний:
Кроме того, задача может иметь приоритет от 0 (высший) до 7 (низший). Приоритет можно изменять в ходе выполнения программы. Подробнее о приоритетах и состояниях.
Для работы с задачами OSA резервирует память под дескрипторы задач. В файле OSAcfg.h задается константа OS_TASKS, которая показывает, сколько одновременно активных задач может работать в программе.
Большинство операций по управлению задачей осуществляется через указатель на ее дескриптор или 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();
Остановить задачу (удалить ее из списка активных задач) можно сервисом 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) | Проверить, приостановлена ли задача |