jacOS –
кооперативная
многозадачная
операционная
система
реального
времени.
Порядок выполнения
задач
определяется
их приоритетами
и событиями,
обслуживаемыми
системой. Для
jacOS вполне
будут
уместны
термины event-driven priority-based RTOS. В
версии 1.06.0 реализованы
семафоры,
сообщения,
таймауты,
маскируемые
события. В
библиотечной
версии
количество
задач и
событий
искусственно
не
ограничено.
Приоритеты
задач статические.
Обычно
в ОС выделяют
два подхода к
обеспечению
многозадачности:
вытесняющий
и
кооперативный.
Вытесняющая
ОС в состоянии
отнять
управление у
текущей задачи
в любой
момент
времени и
передать его
другой
задаче.
Например,
если
появилась
готовая к
работе более
приоритетная
задача или
текущая
задача
отработала
свой квант
времени. В общем-то,
причины
вытеснения
не важны,
главное в
том, что ОС
имеет такую
возможность
и пользуется
ей. Напротив,
кооперативная
ОС поступает
менее
авторитарно.
Задача остается
текущей
столько
времени,
сколько захочет,
а управление
ядру
передает
исключительно
добровольно.
Другими
словами,
задачи должны
проявлять
желание к
сотрудничеству,
или
кооперации.
На практике
это означает,
что главное
отличие
между
вытесняющими
и кооперативными
ОС - у первых
приоритетная
задача может
получить
управление
сразу, как
только оно ей
потребуется,
а у вторых
придется подождать
пока текущая
задача
проявит волю
к
кооперации.
Так
почему же
существуют
кооперативные
ОС, если
вытесняющие
ОС в
состоянии
обеспечить
более
быструю
реакцию на событие?
Иногда
просто
невозможно
сделать вытесняющую
ОС. Как,
например, у
контроллеров
с аппаратной
реализацией
стека. А чаще
решающими
аргументами
в пользу
кооперативной
ОС могут
стать
требования,
предъявляемые
к памяти
контроллера.
Кооперативная
ОС требует
заметно
меньше ROM, и в
разы меньше RAM.
На самом
деле,
проблема не в
том, что для
кооперативной
ОС нельзя
добиться
времени
отклика сопоставимого
с тем, что
есть у
вытесняющих. Это как раз
достижимо ценой частых
переключений
задач.
Проблема в
том, что
обеспечить
такое
переключение
не всегда будет
легко.
Некоторые
стандартные
функции из
библиотек
компилятора
могут оставить
задачи без
переключений
на долгие
тысячи
тактов.
Радикальным
решением
подобной проблемы
будет
модификация
"медленных"
функций под
потребности
кооперативной
ОС (тексты
обычно
доступны), а
это
дополнительный
немалый труд,
пусть даже и
разовый.
С
"кооперативной"
ОС
разобрались,
а вот с ОС
"реального
времени",
боюсь, все не
так просто.
Можно
встретить
много разных
определений
реального времени,
вот одно из
них:
Реальное
время –
способность
ОС обеспечить
задачу всем
необходимым
сервисом за
определенный
интервал
времени.
Предположим
у нас есть, в
целом, очень
быстрая ОС.
Также
предположим,
что иногда,
пусть даже
крайне редко,
она сильно "притормаживает".
Можем ли мы
назвать ее RTOS? Может
и да, а может и
нет. Это
зависит от
того, успевают
ли наши
задачи
получить
весь сервис
вовремя. Или
совсем
другой
случай. У нас работает
RTOS на
конкретном
контроллере
и замечательно
справляется
с нашими конкретными
задачами. Но
вот мы
поменяли контроллер
на более
медленный. И
та же самая RTOS
больше не в
состоянии
обеспечить
всем необходимым
сервисом те
же самые
задачи. Значит
ли это, что
наша RTOS больше
никакая не RTOS, а
так, сплошное
недоразумение.
И если нет,
была ли она RTOS с
самого
начала, ведь
в ней ничего
не
изменилось?
Видимо,
оценка ситуации
должна быть
интегральной,
и зависеть не
только от ОС,
но и от
прочих
начальных условий.
jacOS
версии 1.06.0
поддерживает
12-и , 14-и и 16-и
битные
контроллеры
PIC, а так же AVR , MSP430 и
х51. Точнее
большинство
из них. В
качестве
ограничения
для первых
двух серий
выбрана
память данных,
и если ее не
меньше 41-ого
байта, то,
скорее всего,
библиотеки
созданы. Для
18-ой серии картина
другая. Дело
в том, что в
библиотечной
версии для каждого
процессора
была выбрана
только одна
"модель"
работы с
памятью.
Некий компромисс,
в котором, по
возможности,
максимальная
функциональность
сочеталась
бы с разумными
требованиями
к памяти
процессора. В
таблице
указаны
подробности
обо всех
ограничениях
для каждой
серии
контроллеров.
Контроллер |
Модель |
Ограничения |
Примечания |
PIC 12
бит H-T |
SMALL |
Строковые
константы и
таблица
переходов вместе
должны
поместиться
в 256 слов ROM. |
Каждая
точка
переключения
контекста
требует два
слова в
таблице
переходов. |
PIC 14
бит H-T |
COMPACT |
Все
системные
переменные + TCB + ECB должны
находиться
в bank0 и bank1 |
Каждая
задача
требует от 4
до 8 байт для
блока
управления (TCB). Для
каждого
события так
же
требуется
блок управления
(ECB) в 2-4 байта. Библиотеки
с
поддержкой
банков 2 – 3
можно получить
по запросу. |
PIC18 H-T |
COMPACT |
1.
Все
системные
переменные, TCB и ECB имеют
квалификатор
near,
следовательно,
должны
уместиться
в 128 байт RAM. (96
байт для
некоторых контроллеров) 2.
Эта модель
работает с
памятью до
64Kb(32килослова) |
TCB
занимает от 4
до 8-и байт. ECB - от 3
до 4-и. Максимальное
количество
задач для
этой модели ~=24 |
PIC18 IAR |
NORMAL |
1.
Все
системные
переменные, TCB и ECB
объявлены с
ключевым
словом __bank1 (256 б). |
Code model = Static overlay. TCB
занимает от 5
до 9-и байт. ECB - от 4
до 5-и. |
AVR GCC WINAVR |
NORMAL |
|
TCB
занимает от 5
до 9-и байт. ECB - от 4
до 6-и. |
AVR IAR |
COMPACT NORMAL |
1.
Системные
переменные
имеют
квалификатор
__tiny. 2.
Опция Cross Call при
оптимизации
должна быть
отключена 1.
Опция Cross Call при
оптимизации
должна быть
отключена |
Максимальное
количество
задач для
этой модели ~=30.
Поддерживаются
модели
компилятора
tiny и small в
конфигурациях
от v0,v1,v3. TCB
занимает от 4
до 8-и байт. ECB - от 2
до 5-и. Поддерживается
модель small в
конфигурации
v3. TCB -
от 5 до 12-и байт. ECB -
от 3 до 6-и. |
MSP430 IAR |
NORMAL |
|
TCB
занимает от 6
до 12-и байт. ECB - от
4 до 6-и. |
x51 Keil |
NORMAL |
1.
Программная
память до 64Kb |
TCB
занимает от 4
до 8-и байт. ECB - от 3
до 5-и. |
Контроллер |
Компилятор |
Примечания |
PIC12 ,
PIC16 |
HI-TECH PICC v8.05 |
Следует
использовать
опцию
Produce assembler list file |
PIC18 |
HI-TECH PICC-18 v8.35 |
|
PIC18 |
IAR C/EC++ Compiler for Microchip PIC18 2.12A/W32 |
При
построении
проекта, в
опциях XLINK
необходимо
отключить
проверку
глобальных типов
(Diagnostics -> No global type checking). |
AVR |
WinAVR 20040404 |
|
AVR |
IAR C/EC++ Compiler for AVR 3.20C/W32 |
Вероятны
проблемы
при
установке
опций оптимизации
Code motion (?) Cross Call (?) . |
MSP430 |
IAR C/EC++ Compiler for MSP430 V3.20A/W32 |
Иногда
возможны
проблемы
при
установке опций
оптимизации
: Size - High. |
x51 |
Keil Cx51 v7.04 |
При
построении
проекта, в
опциях BL51 Misc
необходимо
отключить Warning
L15 (Disable Warning Number: = 15) |
jacOS
написана на
С. Первая
"базовая"
версия для PIC18
заработала
вообще без
единой
ассемблерной
вставки
(впрочем, это
относится и к
и текущей
версии для HT-PICC18). В V1.06.0
ассемблер
применялся
только для
ускорения
функций
сохранения
контекста и
переключения
задач. Это
вселяет
уверенность
в том, что
система
легко может
быть
портирована
на другие
контроллеры.
Для этого
достаточно
наличие
программного
доступа к стеку
и
нормального
компилятора.
Для контроллеров
с аппаратным
стеком все
иначе, и порт
на PIC16 и PIC12 стал
возможным в
основном
благодаря
использованию
всяческих
"особенностей"
компилятора
HI-TECH PICC.
Архивный
файл с
системой
необходимо
распаковать
в корневой
каталог
диска,
используя path info
архива.
Аналогично
распаковываются
файлы с
библиотеками.
Задачу
можно
рассматривать
как отдельную
программу с
независимым
алгоритмом.
Весь обмен
информацией
между
задачами может
быть
организован
с помощью сервисов
ОС. Хотя, это и
не
обязательно.
Предположим,
мы хотим,
чтобы наш
контроллер
"одновременно"
работал с ADC,
опрашивал
состояние
кнопок, мигал
лампочками и
управлял
каким-нибудь
устройством.
Вот такой
набор
программ,
работающих
"одновременно",
и напрашивается
на перенос в
многозадачную
RTOS. Осталось
"лишь"
оформить эти
программы в
качестве
задач. А за
"одновременность"
должна
отвечать ОС, для
этого ее и
создают.
Правда,
сохранив массу
нервных
клеток и
времени
(значит и
денег), мы не
увеличим ни
мощность
нашего контроллера,
ни его
память.
Скорее
наоборот. И
если
контроллер
не
справляется
с одной задачей,
он точно не
справится
сразу с несколькими.
В общем, ОС
это "всего
лишь"
инструмент, и
только опыт
работы с ним
подскажет оптимальный
способ его
использования.
В jacOS
задача
выглядит как
обычная
функция с бесконечным
циклом в
теле.
Внутри цикла
мы обязаны
иметь хотя бы
один сервис,
передающий
управление
ядру ОС.
Пользуясь
таким
сервисом,
задача
проявляет
добрую волю к
кооперации, а
без
кооперации
наша система
с неизбежностью
превратится
в...
однозадачную.
Мы этого не
хотим,
уверен.
OST_TASK task_1; //1
__task void Task1( void )
{
static int counter; //2
for(;;) { //3
counter++; //4
OS_Cooperate(); //5
counter++; //6
}
}
При
описании
функции
задачи для
компиляторов
IAR необходимо
указать
ключевое
слово __task. Для других
компиляторов
это весьма
желательно,
для
обеспечения
лучшей
переносимости
программ, но
не
необходимо.
Также для
каждой задачи
потребуется
блок
управления
задачей (TCB). В
строке //1 мы
создаем
глобальную
переменную task_1
с типом OST_TASK, она
как раз и
будет хранить
TCB. Всего в jacOS
есть два
разных типа TCB.
Первый это OST_TASK,
а второй - OST_TASK_T,
его мы
обсудим
позже.
TCB мы
можем
разместить и
в bank1.
Разумеется,
если
контроллер
его имеет. Но jacOS
не будет
работать с
системными
переменными
в bank2 и bank3. Вернее,
для работы со
старшими
банками
потребуются
дополнительные
библиотеки,
которых
может не
оказаться в
обычном наборе.
Это
ограничение
не затрагивает
данные
пользователя,
их можно размещать
где угодно.
bank1 OST_TASK task_1; // ТСВ в bank1
Сервис
OS_Cooperate() в строке //5
нужен для
возврата управления
менеджеру
задач. Это
самый простой
и
фундаментальный
способ
обеспечения
кооперации
задач. Он
содержится
во всех
других
сервисах
переключающих
контекст. То
есть, по
существу, OS_Cooperate()
и делает jacOS
кооперативной
и
многозадачной
ОС.
В
задаче мы
можем
объявить и
инициализировать
локальные
переменные,
так же как и в
обычных
функциях //2.
Квалификатор
static должен быть
указан, когда
важно сохранить
значение
переменной
между переключениями
задач.
Локальные
переменные, не
имеющие static,
будут
многократно
затерты локальными
переменными
других задач.
Это связанно
с
отсутствием
программного
стека в PIC, и тем
как
компилятор
выделяет
память под
переменные и
под
параметры
функций.
В
описании для
каждого
сервиса
оговорена область,
в которой
разрешено
его использование. Для OS_Cooperate()
сказано, что
его можно использовать
только в
задачах. Что это обозначает,
и какие еще
есть области
применения? В
jacOS
существуют
две
основные
области и третья
область, к
которой
относится все, что
не попадает в
первые две.
·
задачи
·
прерывания
·
фон
Сервис
находится в
области
задачи, когда
его вызов
происходит
из функции
задачи. Это
жесткое
ограничение.
Например,
если из
задачи
вызывается
функция your_func() и из
нее
вызывается
сервис, то
этот сервис
не попадает в
область
задач. Это
ограничение преимущественно
относится к
сервисам, возвращающим
управление
ядру. Все дело
в том, что
задачи в
кооперативных
ОС обычно не
имеют
собственного
стека. После
того как
управление
будет
возвращено
ядру в стеке
не останется
корректного адреса для
возврата из your_func().
Сервисы,
предназначенные
для работы в
прерываниях,
могут быть
вызваны как
из самих
функций
обработчиков
прерываний,
так и
из вложенных
функций. Для
этих
сервисов
важно, чтобы их
выполнение
не было
прервано
другим прерыванием
вызывающим
аналогичные
сервисы.
Если
система была
установлена
на диск C:, то в
папке c:\jacos\prim
найдем
несколько
готовых к
компиляции проектов
с примерами
работы
сервисов jacOS.
Для PIC
проекты
примеров
созданы под
две IDE
- MPLAB 5.7x и
MPLAB 6.хх. Для
каждого из
них ниже
приводится краткое
описание. На
самом деле,
для того чтобы
разобраться
с работой ОС
необходимо
пошагово
выполнить
все эти
примеры.
Крайне
полезно будет
создать
несколько
собственных
тестовых
программ. Без
этого трудно
будет
составить
правильную
картину не
только о
логике работы
сервисов, но
и о том,
сколько тактов
занимает
выполнение
сервисов, и
сколько
памяти потребуется
для их
использования.
Во
всех
примерах counter++
будет
эвфемизмом
некоей
полезной
работы.
Примеры
совершенно
абстрактные,
так пусть
хоть counter
потрудится.
Кроме того,
не каждая IDE позволяет
ставить
точки
остановки на
макросы во
время отладки,
а counter++ для этого
в самый раз.
/********************************************/
__task void TaskABC(void)
{
char loc_var,
static char sta_var;
for(;;) {
loc_var = PORTA;
. . .
sta_var = 45;
OS_Cooperate(); //1
loc_var = PORTB; //2
. . .
OS_Cooperate();
}
}
OS_Cooperate()
сохраняет
контекст
задачи,
важнейшей частью
которого
является
адрес
возврата в задачу.
В строке //1 мы
уйдем из
задачи, а
вернемся в
нее в строке //2.
И как уже
было сказано,
в строке //2
переменная loc_var
будет иметь
неопределенное
значение, а sta_var,
напротив,
сохранит
значение
равным 45-и.
Третье
и последнее
действие -
зарегистрировать
задачу в ОС,
без этого
задача для
системы еще
не
существует.
/*****
c:\jacos\prim\prim1*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK task1;
OST_TASK task2;
unsigned char counter;
__task void T1( void )
{
while(1) {
counter++; //3 //8
OS_Cooperate(); //4 //9
counter++; //7 //13
}
}
__task void T2( void )
{
while(1) {
counter++; //5 //11
OS_Cooperate(); //6 //12
counter++; //10 //
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1); //1
OS_Task_Create(T2,&task2);
while(1) {
OS_Scheduler(); //2
}
}
/****Пример
1*******************************/
#define OS_MAIN
Только
файл,
содержащий
функцию main() должен
начинаться с
этой строчки,
во всех остальных
модулях
нельзя
определять OS_MAIN.
#include <jacos.h>
Файл
содержит
описание
всех типов
данных и внешних
функций
системы.
Порядок
следования
этих строк
должен быть
сохранен.
OS_Init();
Сервис
для
инициализации
ОС.
Параметров не
имеет.
OS_Task_Create(
OST_ADDRESS указатель_на_функцию,
OST_TASK_P указатель_на_TCB,
OST_U8 приоритет);
Сервис
создает
задачу. В prim1 не
используются
приоритеты,
поэтому OS_Task_Create()
получает
только два
параметра.
Первый - имя
задачи,
второй - указатель
на TCB.
Как
уже сказано,
приоритеты в
prim1 не используются.
Сделано это,
потому что
обе задачи должны
иметь
одинаковый
приоритет. В
противном случае
работала бы
только
задача с
большим приоритетом.
И вообще,
одинаковый
приоритет у
всех задач
дает
возможность выбрать
библиотеку
без
поддержки
приоритетов,
так код будет
заметно меньше,
а часто и
быстрее.
OS_Scheduler();
С
помощью OS_Scheduler()
управление
передается
ядру. Ядро
выберет
самую
приоритетную
созданную
первой
задачу и
передаст ей
управление. Как
видим, OS_Scheduler()
тоже
находится
внутри
бесконечного
цикла, и это не
случайно. При
каждом
переключении
задач
происходит
возврат в
этот цикл.
Значит, мы
можем
поместить
свой код до
или после OS_Scheduler() и
делать
что-нибудь
полезное каждый
раз, когда
задача
возвращает
управление
ядру. Иногда
это бывает
удобно, или
даже
необходимо.
Например, мы
можем
сбросить WDT, в самом
ядре это не
реализовано.
Для
того чтобы
посмотреть
пример в
работе проще
всего будет
воспользоваться
готовым
проектом из
папки c:\jacos\prim\prim1.
Итак,
на что нужно
обратить
внимание в jacOS
проекте?
Прежде всего,
на выбранный
вариант библиотеки
и модель
памяти.
Конфигурация
проекта
всегда
должна
находиться в
файле "jacnfg.h". Для
prim1 она
выглядит
примерно так:
#define OS_Lib_Type OS_ntn
/**** jacnfg.h ****************************/
В
проекте для
PIC12F675 должны
быть файлы:
main.c - сам
пример
40Antln.lib -
библиотека с
функциями ОС
А
также должны
быть указаны
два пути:
1)
на папку с
файлом jacnfg.h.
2)
на папку с
системными head
файлами ( \jacos\incl ).
Для
prim1 в MPLAB 6.2x это
проще всего
сделать так:
Project->Build
Options->Project->General->Include Path = \jacos\prim\prim1;\jacos\incl
Конечно
же, не
забываем
включить
оптимизацию
и отладочную
информацию. А
для PICC компилятора,
крайне
важно
установить
Produce assembler list file в ON. Без этой
галочки, для PIC12
и PIC16 ничего не
удастся оттранслировать. Одним
словом, это
важная
"особенность",
нельзя о ней
забывать.
В
первом
примере
задачи
просто
передают управление
друг другу по
кругу.
Система уделяет
им
одинаковое внимание.
И ресурсы
контроллера
делятся на
них поровну.
Но если мы не
хотим этого?
Предположим,
что одна из
задач следит
за состоянием
кнопок в
устройстве, и
нет никакой
нужды делать
это чаще чем
раз в 100 или 150мс?
Тогда нам
помогут
таймеры
контроллера
и сервис OS_Delay().
/***** c:\jacos\prim\prim2*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK_T task1;
OST_TASK task2;
unsigned char counter;
__task void T1( void )
{
while(1) {
counter++; //1 //N+1
OS_Delay(10); //2 //N+2
counter++; //N //
}
}
__task void T2( void )
{
while(1) {
counter++; //3 //6 . . .
OS_Cooperate(); //4 //7 //N-1
counter++; //5 //8 //N+3
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1);
OS_Task_Create(T2,&task2);
PR2
= OS_TMR_TICK;
TMR2
= 0;
INTCON = 0b11000000;
TMR2IE = 1;
TMR2IF = 0;
T2CON
= 0x05;
while(1) {
OS_Scheduler();
}
}
interrupt void intr(void)
{
if ( OS_Is_Timer()) {
OS_Timer_Tick_Set();
OS_Timer();
}
}
/******************************************/
#define OS_Lib_Type OS_dtn
#define OS_Timer_Tick_Set() {
TMR2IF = 0; }
#define OS_Is_Timer() TMR2IF
#define OS_TMR_TICK 249
/**** jacnfg.h ****************************/
Во
втором
примере для
задачи T1
использован
новый тип TCB - OST_TASK_T.
Он занимает
на 1-2 байта
больше
памяти, чем OST_TASK,
и содержит
дополнительную
информацию о
состоянии
таймера. И
если задача
использует
сервис
задержки или
сервисы с
таймаутами,
то ее ТСВ
обязан иметь
тип OST_TASK_T. В
противном
случае
система
рухнет, рано
или поздно. На
этапе
исполнения
не
проверяется
соответствие
типов задач,
это привело
бы к заметным
накладным
расходам. В
общем, от
программиста
требуется
аккуратность
на этапе проектирования.
С другой
стороны, тип OST_TASK_T с
успехом
может
заменить OST_TASK,
и это может
быть
полезным на
начальных
этапах.
Для
использования
сервиса OS_Delay()
требуется источник
более или
менее
периодических
событий
(скорее более
чем менее).
Удобнее
всего
использовать
какой-нибудь
из таймеров
контроллера,
они для этого
и созданы.
Главное в
том, что мы
должны
сигнализировать
системе о
каждом
прошедшем
интервале
времени.
Такие
сигналы
называют тиками.
Частоту
тиков
программист
выбирает исходя
из
требований
задач.
Как правило,
в свою
очередь, она
будет
выбрана
исходя из
потребностей
самой
требовательной
к таймеру задачи.
Но нельзя
увеличивать
частоту тиков
бесконечно. С
увеличением
частоты падает
общая
производительность
системы, а с некоторого
порога ОС
перестанет
успевать корректно
обрабатывать
каждый тик. В jacOS
рекомендуется
ограничить
интервал
между тиками
1000-ю программными
тактами
контроллера.
Для 4МГц PIC это
будет 1мс.
Программист
должен
позаботиться
о том, чтобы
за эту 1000-у
тактов
произошло хотя
бы одно
переключение
задач, а
лучше два.
Тысяча
тактов это
общая рекомендация,
не всегда
возможно
будет в нее
уложиться.
Например,
сравнение
небольшой строки
со строковой
константой,
само по себе, может
занять у PIC16
больше 1000-и
тактов. В
общем, опять
ищем
компромисс.
Либо переписываем
такие
функции, либо
уменьшаем частоту
тиков. Все
сказанное
напрямую
относится к PIC
контроллерам.
Для AVR и MSP430
рекомендуемый
интервал
между тиками
- 2000 тактов.
OS_Delay(OST_TIMER
задержка_в_тиках);
OS_Delay()
отправит
задачу в
"ожидание" на
то
количество
тиков, какое
указано в
параметре. А
что значит
"ожидание"?
Ожидание это
одно из
состояний
задачи. Всего
их пять:
·
Задача
может быть не
создана или
уничтожена.
ОС о ней
ничего не
знает, но мы
то знаем. И о такой
задаче может
знать другая
задача. Дело
в том, что не
обязательно
создавать
задачу в main, так
сказать в
фоновом
режиме. Ее можно
создать и на
уровне задач.
Так что "не создана"
это
полноценное
состояние
задачи, она
еще может
поработать.
·
Задача
может быть
готова к
исполнению,
но не иметь
управления.
Назовем
такое
состояние "готова".
Готова то
она, может и
готова, но это
не значит,
что она
когда-нибудь
получит управление.
Для работы
задача
должна стать
самой
приоритетной
из задач в
подобном состоянии,
а
выполняемая
задача
должна помнить
о кооперации.
·
Задача
может иметь
управление,
то есть работать.
Так и назовем
это
состояние
"работает".
·
Задача
может
находиться в
состоянии
ожидания. Она
не будет
работать,
даже если в
системе нет
других
претендентов
на процессорное
время. Задача
выйдет из
этого
состояния
только тогда,
когда что-нибудь
случится.
Например,
произойдет
ожидаемое ей
событие, или
закончится
оговоренное
с системой
время
ожидания.
·
И
последнее
состояние.
Задача может
быть остановлена.
Вывести ее из
этого
состояния можно
двумя способами.
Перевести в
состояние
"готова", или
отнять у нее TCB
и тем самым
перевести в
состояние
"не создана".
Вернемся
к нашему
примеру.
Источником
тиков
системного
таймера
послужил TMR2
контроллера.
Он
инициализирован
так, чтобы
вызывать
прерывание
каждую миллисекунду
(4МГц). А сервис
OS_Timer() сообщает
об этом
системе.
Другими
словами без
периодической
работы OS_Timer(), в
системе
нельзя
пользоваться
задержками и
таймаутами. В
нашем примере
OS_Delay(10) отправит
задачу Т1 в
ожидание на 10
тиков, то
есть от 9+ до 10+
миллисекунд.
Почему такой
разброс? Думаю,
ответ
получить не
сложно, если
вспомнить то,
что тики это
события
следующие с
определенным
интервалом
времени, а не
сам интервал.
Задача
уходит в
ожидание в
произвольном
месте между
тиками.
Предположим,
запись
одного байта EEPROM
происходит
за 8-9мс и тик в
системе = 5мс.
while ( EECR & (1 <<
EEWE) ) OS_Delay(5ms); // :)
Длительность
задержки
равна одному
тику, но
такая
проверка на
завершение
записи совершенно
корректна и
обеспечит
средний темп
записи в EEPROM в
10мс.
OS_Delay(10ms); // :(
А
такая
задержка
между двумя
записями некорректна.
Несмотря
на то, что
первый тик
при задержке имеет
неопределенную
длительность,
тем не менее,
с помощью OS_Delay()
легко
получить
стабильную
среднюю
задержку. Для
достижения
часовой
точности
задача должна
переключаться
только с
помощью OS_Delay(), иметь
высший
приоритет и
самый длительный
участок
между
переключениями
у других
задач должен
быть меньше
одного тика.
То есть нужно
добиться,
чтобы наша
задача
никогда не
оставалась в
состоянии
"готова"
дольше
одного тика.
Кстати,
отсюда
следует, что важно
еще и
грамотно
подобрать
интервал
самого тика.
В jacOS V1.06.0,
по умолчанию,
макросы OS_Timer_Tick_Set() и
OS_Is_Timer() будут настроены
на Timer0 для PIC12 и PIC16, и
на Timer2 для PIC18. Но в
jacnfg.h их можно
переопределить
на любой
другой
периодический
источник прерываний.
А можно и
вовсе не
использовать,
например,
заменить
явным
обращением к
регистрам.
__task void
следим_за_кнопкой(
void )
{
static char всего_нажатий_кнопки = 0;
while(1) {
if
(кнопка == 0) {
if
(всего_нажатий_кнопки
== 0) {
OS_Delay(120_ms);
} else {
всего_нажатий_кнопки--;
}
} else {
if
(++всего_нажатий_кнопки
== 4) {
всего_нажатий_кнопки
= 0;
Сообщить_кому_нибудь();
}
}
if
(всего_нажатий_кнопки
!= 0) {
OS_Delay(10_ms);
}
}
}
Видимо,
это очень
важная
кнопка, раз
мы за ней так
тщательно
присматриваем.
В жизни можно
быть
скромнее. Но
не в этом дело.
Главное, что
рядом с этой
кнопкой
уживаются
другие
задачи. И не
ценой наших
нервных клеток.
Во
втором
примере мы
увидели, как
OS_Delay переводит
задачу из
одного
состояния в
другое. В третьем
примере мы
увидим еще
парочку
сервисов с
подобным
свойством.
/*****
c:\jacos\prim\prim3*****************/
. . .
__task void T1( void )
{
while(1) {
counter++;
OS_Cooperate();
OS_Task_Stop();
counter++;
}
}
. . .
__task void T3( void )
{
while(1) {
counter++;
OS_Cooperate();
if
(counter > 15) {
if (OS_Task_Resume(&task1) ==
OS_ERROR) {
counter = 0;
}
}
counter++;
}
}
OS_Task_Stop
переводит
задачу из
состояния
"работает" в
состояние
"остановлана".
В jacOS задача может
приостановить
только саму
себя. Сделано
это, в
основном, из
желания предотвратить
ситуации,
когда задача
завладела
каким-нибудь
ресурсом, и
ее остановка
грозит
потерей
доступа к
этому
ресурсу. Правильней
будет, чтобы
до останова
задача
отдала все
долги.
OST_ERR_MSG
OS_Task_Resume(OST_TASK_P
указатель_на_задачу);
Парным
для OS_Task_Stop
является
сервис OS_Task_Resume. В
качестве
параметра
передаем
указатель на
TCB. OS_Task_Resume успешно
отработает,
только если TCB
принадлежит
задаче в
состоянии
"остановлена".
В итоге, задача
будет
переведена в
состояние
"готова". Более
того, она
встанет в
начало
списка таких
задач. То
есть,
"новая"
задача
получит
управление
первой из
задач с
равным приоритетом.
Подобную
разовую
добавку к
приоритету
получит и
задача,
пробудившись
после таймаута
или задержки,
а так же
задачи после
ожидания
события. В
некоторых
случаях это
позволит
выбрать
библиотеку
без
поддержки
приоритетов
и сэкономить
память. На
самом деле, приоритеты
задач не
меняются в
явном виде, в V1.06.0
они вообще
статические
и назначаются
только при
создании
задачи. А
эффект "увеличения
приоритета"
возникает
из-за положения
задачи в
списке
"готовых".
Указанные в
явном
порядке
приоритеты
делают то же самое.
В
задаче с
"важной"
кнопкой был
намек на межзадачные
обмены
информацией.
Обеспечение
подобных
межзадачных связей
это одна из
самых важных
функций ОС. Самый
простой
способ для
коммуникаций
это семафоры.
В jacOS есть два
типа
семафоров:
бинарный и
счетный. Они
очень похожи,
даже создаются
одним и тем
же сервисом,
и сервис ожидания
сигнала
семафора для
них тоже один.
Бинарный
семафор
может
принять два
значения - 0 и 1. А
уж как
интерпретировать
эти два значения
подскажет
фантазия.
Занято - свободно,
готово - не
готово, можно
- нельзя, и т.д. Задачи,
претендующие
на какой-нибудь
ресурс,
например,
периферийное
устройство, с
помощью
семафора
могут
сообщать друг
другу
свободно ли
оно. Семафоры
так же используют
и для
синхронизации
работы задач,
обычно в тех
случаях,
когда
требуется
строгий
порядок
выполнения
нескольких
задач. Счетный
семафор
принимает
любое
значение, какое
сможет
сохранить.
Для обоих
типов семафоров,
значение
равное 0,
обозначает,
что семафор
не
установлен.
/*****
c:\jacos\prim\prim4*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK_T task1;
OST_TASK task2;
OST_TASK task3;
OST_SEMAPHORE event1;
unsigned char counter;
__task void T1( void )
{
OST_ERR_MSG y;
while(1) {
counter++; //0 //N+2
OS_Delay(10); //1 //N+3
OS_Post_BSemR(&event1,y); //N
counter++; //N+1
}
}
__task void T2( void )
{
while(1) {
counter++; //2
OS_Wait_Sem(&event1); //3
counter++; //N+4
}
}
__task void T3( void )
{
while(1) {
counter++; //4 //7
OS_Cooperate(); //5 //8 . . .
//N-1
counter++; //6
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1);
OS_Task_Create(T2,&task2);
OS_Task_Create(T3,&task3);
OS_Sem_Create(&event1,0);
PR2 =
OS_TMR_TICK;
TMR2 =
0;
INTCON =
0b11000000;
TMR2IE =
1;
TMR2IF =
0;
T2CON =
0x05;
while(1) {
OS_Scheduler();
}
}
interrupt void intr(void)
{
if ( OS_Is_Timer()) {
OS_Timer_Tick_Set();
OS_Timer();
}
}
/*****
c:\jacos\prim\prim4*****************/
#define OS_Lib_Type OS_ctn
#define OS_Timer_Tick_Set() {
TMR2IF = 0; }
#define OS_Is_Timer() TMR2IF
#define OS_TMR_TICK 249
/**** jacnfg.h
****************************/
OST_SEMAPHORE event1;
Прежде
чем
воспользоваться
семафором создаем
блок
управления
событием (ECB) c
типом OST_SEMAPHORE.
Можем
поместить
его и в bank1.
OS_Sem_Create(
OST_SEMAPHORE_P указатель_на_ЕСВ,
OST_SEM первоночальное_значение_семафора
);
Сервис
OS_Sem_Create создает
бинарный или
счетный семафор.
Первый
параметр это
указатель на
ECB, второй -
значение с
которым
семафор
начнет работу.
Для
бинарного
семафора это
должны быть 0
или 1.
Корректность
не
проверяется,
поэтому требуется
аккуратность
при
написании
программы.
Кстати,
события, так
же как и
задачи, можно
создавать в
самих
задачах.
OS_Post_BSemR(
OST_SEMAPHORE_P указатель_на_ЕСВ,
OST_ERR_MSG код_возврата
);
Сервис
OS_Post_BSemR имеет два
параметра,
указатель на
ECB и
переменную,
куда будет
возвращен
код успешности
работы. Код
возврата OS_ERROR -
неудача, это
означает, что
семафор уже
был установлен
в единицу.
Дважды
подряд
установить
семафор не
удастся. OS_NOERR -
семафор
установлен
успешно. Надо
сказать, что
любое событие
может
ожидать
сразу
несколько
задач. И если
оно
произойдет (а
ведь может и
не произойти),
то самая
приоритетная
и пришедшая
первой
задача
получит
управление.
OS_Wait_Sem(OST_SEMAPHORE_P указатель_на_ЕСВ
);
Сервис
OS_Wait_Sem проверит,
был ли
установлен
семафор, и
если был, то OS_Wait_Sem
сбросит его в
0 и задача продолжит
работу. Если
же флаг
семафора не был
установлен,
то задача
перейдет в
ожидание.
Ядро поместит
ее в список
задач,
связанный с
событием.
Этот список
упорядочен
по
приоритетам, а
для задач с
равным
приоритетом
он FIFO. Количество
задач в
списке не
ограничено.
После успешного
ожидания
событья OS_Wait_Sem
повторит проверку
флага
семафора. Это
необходимо.
Дело тут в
том, что
более
приоритетная
задача может
захватить
желанный
ресурс
первой, и
флаг
семафора
опять будет
сброшен.
При
использовании
семафора для
защиты ресурса
важно
помнить, что
OS_Wait_Sem сбросит
флаг в 0, то
есть задача
займет ресурс.
И чтобы его
освободить
нужно будет
установить
флаг
семафора
заново.
__task void
пользуем_ресурс(
void )
{
for(;;) {
OS_Wait_Sem(&семафор); //если
ресурс занят,
то ждем его
освобождения
Работаем_с_ресурсом(); //захватили
ресурс
OS_Post_BSem(&семафор); //освободили
ресурс
OS_Cooperate(); //ушли
из задачи
}
}
В этой
задаче
просто
необходимо
использовать
OS_Cooperate(), иначе мы
из нее
никогда не
выйдем.
Связано это с
тем, что в jacOS
сервисы
ожидания
событий переключают
контекст только
тогда, когда
ожидание
действительно
требуется, и
не делают
этого, если
событие уже
произошло. В
целом это
положительная
черта ОС, но
если ее не
учитывать
могут возникнуть
проблемы.
Например,
если событие
происходит
слишком
часто, то
задача,
связанная с
этим
событием,
"задушит"
другие
задачи.
Для решения
подобной
проблемы
будет
достаточно
добавить OS_Cooperate.
В
jacOS есть два
сервиса
аналогичных
OS_Post_BsemR: OS_Post_BSem - не имеет
второго
параметра, и
не
возвращает
код завершения.
OS_Post_BSemI -
предназначен
для работы в
прерываниях.
Семафоры
также очень
удобны и для
синхронизации
работы задач.
И вообще
полезная штука.
Имея в
распоряжении
одни только
семафоры
можно
сконструировать
большинство
других
"событийных"
сервисов.
Иногда нужда заставит,
а иногда
просто для
души
захочется. Но
этого не
придется
делать для
сообщений, в
V1.06.0 они уже
реализованы.
/***** c:\jacos\prim\prim5*****************/
. . .
OST_MESSAGE event1;
unsigned char * msg;
__task void T1( void )
{
while(1) {
counter++;
OS_Delay(10);
OS_Post_Msg(&event1,&counter);
counter++;
}
}
__task void T2( void )
{
while(1) {
counter++;
OS_Wait_Msg(&event1,msg);
counter++;
}
}
. . .
OS_Msg_Create(&event1,0);
. . .
OST_MESSAGE event1;
OST_MESSAGE - тип ECB сообщения.
OS_Post_Msg(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение
);
Пример
полностью
повторяет
предыдущий, просто
семафор
заменен
сообщением.
Сообщения в
целом
работают
точно так же
как и семафоры.
За одним исключением.
Сигнализирующая
задача не только
сообщит
ожидающей
задаче, что
произошло
событие, но и
передаст
какую-то
дополнительную
информацию.
Обмен
сообщениями
производится
с помощью
указателей,
поэтому тело
сообщения
может быть
сколь угодно
сложным. В jacOS V1.06.0
есть
библиотеки,
поддерживающие
как однобайтные
указатели на
сообщения,
так и двухбайтные.
Двухбайтные
указатели
позволяют
работать, в
том числе, и
со
строковыми константами.
OS_Wait_Msg(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение
);
Второй
параметр OS_Wait_Msg
обязательно
должен быть lvalue,
то есть
переменной с
типом
указателя на ожидаемый
объект.
OS_Msg_Create(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение
);
Мы
можем отправить
одно
сообщение
уже в OS_Msg_Create. Если
этого не нужно
делать, то
второй
параметр
должен быть
равен нулю.
Корректнее
всего будет
написать (void*)0,
но HT PICC не
ругается и на
просто 0.
Может
случиться
так, что
задачи ждут
событие, а оно
по какой-то
причине не
происходит.
Что делать в
таких
ситуациях? В jacOS
V1.06.0
реализованы
таймауты для
семафоров и
сообщений,
они и предназначены
для решения
подобных
проблем.
/*****
c:\jacos\prim\prim6*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK_T task1;
OST_TASK_T task2;
OST_TASK task3;
OST_MESSAGE event1;
unsigned char counter;
unsigned char even;
unsigned char odd;
__task void T1( void )
{
OST_ERR_MSG y;
unsigned char * tx;
while(1) {
counter++;
OS_Delay(20);
if (counter & 1) {
tx = &odd;
} else {
tx = &even;
}
OS_Post_MsgR(&event1,tx,y);
if (y == OS_ERROR)
{
counter = 0;
}
}
}
__task void T2( void )
{
OST_MSG_P rx;
while(1) {
counter++;
OS_Wait_MsgT(&event1,rx,15);
if (! OS_Timeout() ) {
(*(unsigned char *)rx)++;
}
}
}
__task void T3( void )
{
while(1) {
counter++;
OS_Delay(2);
counter++;
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1,1);
OS_Task_Create(T2,&task2,1);
OS_Task_Create(T3,&task3,0);
OS_Msg_Create(&event1,0);
PR2 =
OS_TMR_TICK;
TMR2 =
0;
INTCON =
0b11000000;
TMR2IE =
1;
TMR2IF =
0;
T2CON =
0x05;
while(1) {
OS_Scheduler();
}
}
interrupt void intr(void)
{
if ( OS_Is_Timer()) {
OS_Timer_Tick_Set();
OS_Timer();
}
}
void OS_Idle(void)
{
counter--;
}
/*****
c:\jacos\prim\prim6*****************/
#define OS_Lib_Type OS_tt
#define OS_Timer_Tick_Set() {
TMR2IF = 0; }
#define OS_Is_Timer() TMR2IF
#define OS_TMR_TICK 249
#define OS_MAX_PRIO 1
/**** jacnfg.h
****************************/
OS_Task_Create
третьим
параметром
принимает
приоритет
задачи. Всего
их может быть
16, от 0 до 15-и. Чем больше
число, тем
выше
приоритет.
Смысл приоритетов
заключается
в том, что
ядро никогда
не запустит
задачу, если
существует
более
приоритетная
задача в
состоянии
готовности.
Отсюда
следует, что все
задачи,
кроме
самых
низкоприоритетных,
должны иметь
хотя бы один
сервис, переводящий
их в
состояние
ожидания. В
противном
случае, часть
задач
никогда не
получит
управление.
Исключением
будет случай,
когда низкоприоритетная
задача сама
создает высокоприоритетные.
OS_Wait_MsgT(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение
OST_TIMER задержка_в_тиках
);
Этот
сервис будет
работать
также, как и OS_Wait_Msg
если
сообщение
придет до
истечения
времени
таймаута.
Если же событие
задерживается,
то задача
продолжит работу.
Проверить
произошло ли
событие или
задача
продолжила
работу по
истечении
времени ожидания
можно с
помощью
функции OS_Timeout().
OST_ERR_MSG OS_Timeout(void);
Функция
возвращает
логическую
1-цу если
ожидание
события было
прекращено
по таймауту.
Признак
таймаута это
одно из
состояний задачи,
которое
можно узнать.
Для чтения других
текущих
состояний
задачи следует
воспользоваться
сервисом OS_Task_Status().
Ниже приведены
все флажки
доступные
для анализа:
OST_TASK_STS OS_Task_Status( OST_TASK_P
указатель_на_TCB);
OSS_TIMER_Q -
задача
ожидает
окончания
задержки (delayed)
OSS_EVENT_Q -
задача
ожидает
события
(семафора,
сообщения,
маскируемого
события)
OSS_STOPPED -
задача
остановлена
OS_STS_PRIO_MASK -
наложив эту
маску можно
считать приоритет
задачи
prio = OS_Task_Status(&task) & OS_STS_PRIO_MASK;
notready = OS_Task_Status(&task) &
(OSS_TIMER_Q + OSS_EVENT_Q + OSS_STOPPED);
При
выходе из
ожидания
сообщения по
таймауту,
указатель на
сообщение не
определен, и
пользоваться
им нельзя.
Значит, мы
просто
обязаны
отслеживать
причины завершения
ожидания. А
для
семафоров
все будет зависеть
от условий
задачи,
возможно, в
проверке на
выход по
таймауту
просто не
будет нужды.
Все
три задачи в
этом примере
рано или поздно
оказываются
в состоянии
ожидания
одновременно.
Контроллер в
это время
будет гонять
пустой цикл,
и ждать
события. На
самом деле
этот цикл не
совсем
пустой. В нем
есть вызов
функции OS_Idle(), и мы
можем этим
воспользоваться.
Просто
создадим такую
же функцию и
нагрузим ее
полезной
работой.
Если в
программе
несколько
задач
ожидают одного
события с
использованием
таймаутов, и
если может
случиться
так, что
событие и
таймаут
произойдут
одновременно,
то желательно
отдать
управление
ядру после
каждого
таймаута.
__task void Task1( void )
{
while(1) {
counter++;
OS_Wait_SemT(&event1,5);
if ( OS_Timeout() ) {
counter++;
OS_Cooperate();
}
}
}
Таким
образом, мы
предотвратим
использование
"чужого"
события.
Помимо
семафоров и
сообщений jacOS
поддерживает
маскируемые
события. В
чем-то работа
с этими
событиями
похожа на
работу с
бинарными
семафорами,
но и отличий
предостаточно.
Маскируемое
событие
работает с
набором
битов
(флагов),
каждый из
которых
может быть
установлен и
сброшен. Один
такой флаг может
рассматриваться
как бинарный
семафор, а
его
установка
как
отдельное
событие. Задача
перейдет из
состояния
ожидания в готовность,
только если
будут
установлены все
ожидаемые ей
флаги.
OS_Wait_Sem(&ev1);
//1
OS_Wait_Sem(&ev2);
OS_Wait_Sem(&ev3);
. . .
#define flag1 0x01
#define flag2 0x02
#define flag3 0x04
#define mask (flag1 +
flag2 + flag3)
OS_Wait_Flag(&event1,mask);
//2
В
первом
случае
задача
продолжит
работу, если
произойдут
три события: ev1,
ev2, ev3. Во втором,
если будут
установлены
все три
флага: flag1,flag2,flag3. На
этом
"похожесть"
завершается,
начинаются
различия.
Задача //1,
прорвавшись
через набор
семафоров,
сбросит их в 0.
Задача //2,
напротив, оставит
все флаги в
неприкосновенности.
Это
необходимо
для других
задач,
работающих с
тем же
событием.
Второе
отличие в
том, что при
установке
любого из
флагов все
задачи из
очереди
маскируемого
события
перейдут в
состояние
готовности.
Каждая из них
проверит,
соответствуют
ли
установленные
флаги ее
маске, и если
нет, то
вернется к
ожиданию. Сброс
же любого
флага
происходит за
несколько
тактов.
/***** c:\jacos\prim\prim7*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK task1;
OST_TASK task2;
OST_TASK task3;
OST_FLAGS event1;
unsigned char counter = 0;
__task void T1( void )
{
while(1) {
counter++;
OS_Set_Flag(&event1,0x01); //4
OS_Cooperate(); //5
counter++;
}
}
__task void T2( void )
{
while(1) {
counter++;
if (counter > 17) {
OS_Set_Flag(&event1,0x02); //2
OS_Task_Stop(); //3
}
counter++; //9
}
}
__task void T3( void )
{
while(1) {
counter++;
OS_Wait_Flag(&event1,0x03); //1 //8
OS_Clear_Flag(&event1,0x03); //6
OS_Task_Resume(&task2); //7
counter = 0;
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1,0);
OS_Task_Create(T2,&task2,1);
OS_Task_Create(T3,&task3,2);
OS_Flag_Create(&event1,0);
while(1) {
OS_Scheduler();
}
}
/*****
c:\jacos\prim\prim7*****************/
#define OS_Lib_Type OS_et
#define OS_MAX_PRIO 2
/**** jacnfg.h
****************************/
Задача
Т3 будет
ожидать
установки
двух флагов и
затем
продолжит
работу. При
этом флаги
она сбросит
явным
образом.
OST_FLAGS event1;
OST_FLAGS -
тип ECB
маскируемых
событий.
OS_Flag_Create(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG предустановленные_флаги_события);
OS_Wait_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG маска_события);
OS_Set_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_устанавливаемых_флагов);
OS_Clear_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_обнуляемых_флагов_события);
По
существу,
особых
объяснений к
этим сервисам
не требуется.
Единственное,
на что хотелось
бы обратить
внимание - в
библиотеках jacOS
V1.06.0 есть 8-и и 16-и
битные
маскируемые
события.
Кстати,
с помощью
маскируемых
событий удобно
делать
запрос на
остановку
какой-нибудь
задачи.
Например, так:
#define task1_run
0x01
OS_Flag_Create(&event1,0xFF);
// до 8-и задач
. . .
__task void
Task1( void )
{
static counter = 0;
while(1)
{
while(1) {
//
Такая
проверка
занимает очень
мало ROM и
происходит
за 2-3 такта
// для PIC16.
if (!(OS_Read_Flag(&event1) &
task1_run)) break;
counter++;
if (!(OS_Read_Flag(&event1) &
task1_run)) break;
OS_Cooperate();
counter++;
}
OS_Task_Stop();
}
}
__task void
Task2( void )
{
while(1) {
// посылаем
запрос на
остановку
задачи Task1
OS_Clear_Flag(&event1,task1_run);
OS_Cooperate();
// а
вот такой
комбинацией
мы можем "воскресить"
Task1
if
(OS_Task_Resume(&task1_tcb) != OS_ERROR) {
OS_Task_Restart(Task1,&task1_tcb);
}
OS_Set_Flag(&event1,task1_run);
OS_Cooperate();
}
}
Как
показала
практика,
нельзя
вынести даже
OS_Task_Stop() за пределы
бесконечного
цикла задачи.
Оптимизирующий
компилятор
может (что
обычно и
делает)
заменить
последнюю
инструкцию call на
goto, а для
системных
сервисов это
смертельно.
Вместо пары
сервисов OS_Task_Resume и
OS_Task_Restart в нашем
случае,
казалось бы,
логичнее
было бы
воспользоваться
одним OS_Task_Create.
Но это не
правило, а редкое
исключение.
Дело в том, что
OS_Task_Create не
проверяет, в
каком
состоянии
находится
связанная с TCB
задача, а OS_Task_Resume
работает
только с
задачами в
состоянии "остановлена".
Попытка
воспользоваться
TCB повторно
будет
законной
лишь в одном
случае -
связанная с
ним задача
остановлена,
в противном
случае
произойдет
сбой в работе.
OS_Task_Resume
восстановит
задачу в той
точке, где
она остановилась.
В нашем
примере OS_Task_Restart
нам будет нужен
только для
повторной
инициализации
переменных в
Task1.
В
прерываниях
нельзя
использовать
сервисы,
переключающие
контекст.
Точнее, эти
сервисы
можно
использовать
только на уровне
задач.
А для
сигнализации
о событиях из
прерывания
нужны
специальные
сервисы: OS_Post_CSemI(),
OS_Post_BSemI(), OS_Post_MsgI(), OS_Set_FlagI(), OS_Clear_FlagI(),
OS_Accept_MsgI(), OS_Accept_SemI(). В jacOS
только часть
библиотек
допускает
использование
этих сервисов.
У систем, в
которых не
используются
сервисы в
прерываниях,
нигде в коде
критические
участки не
будут
защищены.
Вернее, не
будет самих
критических
участков (для
AVR
прерывания,
все же, будут
запрещены на
несколько
тактов после
каждого
срабатывания
OS_Timer()) . А
правила
использования
прерываний в
таких
системах не
отличаются
от правил
работы с
прерываниями
в любой другой
программе.
Второй
тип
библиотек
допускает
использование
в
прерываниях
перечисленных
ранее сервисов.
Критические
участки ядра
и самих
сервисов,
там, где это
необходимо,
защищены от
прерываний.
На уровне
задач и в
фоне (background)
прерывания
разрешены.
Возможно, некоторые
участки
задачи нужно
будет защищать
в явном виде,
но это уже
будет
целиком
зависеть то
того, что и
как
используется
в теле
прерывания.
/***** c:\jacos\prim\prim8*****************/
#define OS_MAIN
#include <jacos.h>
OST_TASK
task1;
OST_TASK
task2;
OST_TASK
task3;
OST_MESSAGE
event1;
char * msg;
unsigned char counter;
char st1 = 'A';
char st2 = 'B';
__task void T1( void )
{
OST_ERR_MSG y;
while(1) {
counter++;
OS_Post_MsgR(&event1, &st1,y );
if
(y) {
y =
0;
}
OS_Cooperate();
counter++;
}
}
__task void T2( void )
{
while(1) {
counter++;
OS_Cooperate();
}
}
__task void T3( void )
{
while(1) {
counter++;
OS_Wait_Msg(&event1,msg);
if(*msg
== 'B') {
counter = 0;
}
else if (*msg == 'A') {
counter
= 1;
}
else {
counter--;
}
}
}
void main(void)
{
OS_Init();
OS_Task_Create(T1,&task1);
OS_Task_Create(T2,&task2);
OS_Task_Create(T3,&task3);
OS_Msg_Create(&event1,0);
PR2 = OS_TMR_TICK;
TMR2 = 0;
INTCON = 0b11000000;
TMR2IE = 1;
TMR2IF = 0;
T2CON = 0x05;
while(1) {
OS_Scheduler();
}
}
#pragma interrupt_level 0
interrupt void intr(void)
{
char i;
if (
OS_Is_Timer()) {
OS_Timer_Tick_Set();
if
(OS_Post_MsgI(&event1,&st2) == OS_ERROR) {
i--;
}
}
}
/*****
c:\jacos\prim\prim8*****************/
#define OS_Lib_Type OS_ein2
#define OS_TMR_TICK 249
#define OS_USE_MSG_I
/**** jacnfg.h ****************************/
OST_ERR_MSG
OS_Post_MsgI(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение);
Код
возврата
равный OS_ERROR
говорит о
том, что предыдущее
сообщение
еще не
прочитано, и,
следовательно,
второе
отправить не
удалось. OS_NOERR –
отработали
успешно. По
своим
функциям OS_Post_MsgI
полностью
аналогичен
OS_Post_MsgR.
#pragma interrupt_level 0
Эта
строчка
обязательно
должна
предшествовать
функции
обработчика
прерывания для
компиляторов
H-T PICС и PICC18, если
в прерывании
используются
сервисы.
#define OS_USE_MSG_I
Определение
символа OS_USE_MSG_I в
файле
конфигурации
разрешает
использование
OS_Post_MsgI() в
прерывании.
В этом
примере
задача Т3
ждет
сообщения.
Сообщения же
формируются
в двух точках
программы. В
прерывании и
в задаче Т1. В
результате
такой
жесткой конкуренции,
как Т1, так и
прерывание
не всегда смогут
успешно
отправить
свое
сообщение. В
этом случае
следует
анализировать
коды возврата.
Честно
говоря, в
этом примере
больше была
бы уместна
очередь
сообщений. На
самом деле,
очереди
сообщений не
сложно создать
с помощью
семафоров
или
сообщений.
Например,
можно
использовать
счетный
семафор и
кольцевой
буфер. Тем не
менее, в V1.06.0
существует
основные
сервисы для
работы с
очередями
сообщений (
OS_Post_MsgQ(), OS_Post_MsgQI(),
OS_Post_MsgQR(),
OS_Wait_MsgQ, OS_Wait_MsgQT,
OS_MsgQ_Create(), OS_Accept_MsgQ(), OS_Accept_MsgQI() ).
Здесь
все очень
просто. Этот
вариант,
пожалуй,
будет
единственным
способом
работы OS_Timer для
12-и битных PIC.
/***** c:\jacos\prim\prim9*****************/
. . .
while(1) {
OS_Scheduler();
if (OS_Is_Timer()) {
OS_Timer();
OS_Timer_Tick_Set();
}
}
. . .
По
умолчанию OS_Is_Timer()
и OS_Timer_Tick_Set() в
системе
определены следующим
образом:
//Для 12-и битных PIC.
#define OS_Is_Timer() TMR0 & 0x80
#define OS_Timer_Tick_Set() TMR0 -=
OS_TMR_TICK/2
//Для 14-и битных PIC.
#define OS_Is_Timer() T0IF
#define OS_Timer_Tick_Set()\
{\
TMR0 -=
OS_TMR_TICK;\
T0IF =
0;\
}
//Для PIC18.
#define OS_Is_Timer() TMR2IF
#define OS_Timer_Tick_Set() TMR2IF = 0
//Для AVR.
#define OS_Is_Timer() (TIFR & (1 <<
TOV0))
#define OS_Timer_Tick_Set() {TCNT0
-= OS_TMR_TICK;}
//Для MSP и 8051 эти макросы по умолчанию не связаны с каким-то определенным
//таймером. Если планируется их использование, то следует определить их
//значение в файле конфигурации проекта.
#define OS_Is_Timer() (1)
#define OS_Timer_Tick_Set() {}
В
названии
библиотеки
содержится
вся необходимая
информация о
сервисах,
которые она
поддерживает.
Всего может
быть от трех
до пяти
значащих групп
символов.
Группы это,
конечно,
громко сказано,
только в
первой из них
три символа,
а в остальных
по одному, но
пусть будут
группы.
Первая
группа:
Три
символа,
определяющие
тип
контроллера.
(см. таблицу 1)
Вторая
группа:
n
- round robin,
нет ни delays, ни
событий
d
- поддерживает
только delays
e
- поддерживает
только
события
c
- поддерживает
delays и события
t
- поддерживает
delays и события с
таймаутами
Третья
группа:
t
- поддерживает
сервисы
только в
задачах
i
- поддерживает
сервисы и в
задачах и в
прерываниях
Четвертая
необязательная
группа:
n
- без
поддержки
приоритетов
отсутствует
- с
приоритетами
Пятая
необязательная
группа:
отсутствует
- семафоры, delays и
маскируемые
события
используют один
байт для
своих
значений.
2 - семафоры,
delays и
маскируемые
события
используют
два байта для
своих
значений.
Типы
и размеры
указателей
сообщений см.
в таблице 2.
таблица
1. Типы
контроллеров.
PIC (HI-TECH) |
|
211 |
12C(R)509(A,AF,AG), 12CE519, 12F509 |
212 |
16C505 |
222 |
16C(R)57(A,B,C), 16C(R)58(A,B), 16F57 |
401 |
16C554(A), 16C556(A), 16C558(A), 16C(R)62(A,B), 16C620, 16C621,
16C622, 16C(R)64(A), 16C712, 16C715, 16C716, 16C(R)72(A) |
40A |
12F629, 12F675, 16C71, 16C710, 16C711, 16C(R)84, 16F630, 16F676, 16F84(A) |
40B |
12C671, 12C672, 12CE673, 12CE674, 12F683, 16C432, 16C433, 16C620A,
16C621A, 16C622A, 16C641, 16C661, 16CE623, 16CE624, 16CE625, 16F684, 16F716 |
40C |
12F635, 16C717, 16C770, 16C781, 16C782, 16F72, 16F627(A), 16F628(A), 16F636,
16F818, 16F819, 16F870, 16F871, 16F872 |
411 |
16C(R)63(A), 16C(R)65(A,B), 16C73(A,B), 16(L)C74(A,B) |
412 |
16F73,
16F74, 16F737, 16F747, 16F873(A),
16F874(A) |
41B |
16C642,
16C662 |
41C |
16C771, 16C773, 16C774, 16C923, 16C924, 16C925, 16F648A, 16F688,
16F87, 16F88 |
42C |
16C66, 16C67, 16C745, 16C76, 16C765, 16C77, 16F76, 16F77, 16F767,
16F777, 16F876(A), 16F877(A) |
800 |
PIC18 |
80E |
PIC18
с учетом errata (18Fх5x) |
PIC (IAR) |
|
I18 |
Target: PIC18, Code model -
Static overlay |
AVR GCC WINAVR |
|
A20 |
Target: Instruction set - avr2 (MCU types: at90s2313, at90s2323,
attiny22, at90s2333, at90s2343, at90s4414, at90s4433, at90s4434, at90s8515, at90c8534,
at90s8535) |
A30 |
Target: Inst. set - avr3 (MCU types: atmega103, atmega603, at43usb320,
at76c711) |
A40 |
Target: Inst. set - avr4 (MCU types: atmega8, atmega83, atmega85) |
A50 |
Target: Inst. set - avr5 (MCU types: atmega16, atmega161, atmega163,
atmega32, atmega323, atmega64, atmega128, at43usb355, at94k) |
AVR (IAR) |
|
At0 |
Target: Processor configuration -v0 ; Memory model - Tiny |
At1 |
Target: Processor configuration -v1 ; Memory model - Tiny |
At3 |
Target: Processor configuration -v3 ; Memory model - Tiny |
As1 |
Target: Processor configuration -v1 ; Memory model - Small |
Ns1 |
Target: Processor configuration -v1 ; Memory model - Small Для
контроллеров,
которым не
выделяются tiny_*
сегменты. (mega48, mega88, ...) В
файл
конфигурации
проекта
требуется
вставить
строчку: #define
OS_MEM_MODEL NORMAL |
As3 |
Target: Processor configuration -v3 ; Memory model - Small Для
контроллеров
без
инструкции ELPM
(откомпилировано
с ключом --64k_flash). Для
этих
библиотек
необходимо,
чтобы линкер
выделял
достаточное
количество RAM в tiny_*
сегментах. |
Ns3 |
Target: Processor configuration -v3 ; Memory model - Small Для
контроллеров
без
инструкции ELPM
(откомпилировано
с ключом --64k_flash). В
файл
конфигурации
проекта
требуется
вставить
строчку: #define
OS_MEM_MODEL NORMAL |
Es3 |
Target:
Processor configuration -v3 ; Memory model - Small Для
контроллеров
c
инструкцией
ELPM и RAMPZ
регистром (mega128, mega64 и т.п.). В файл
конфигурации
проекта
требуется
вставить
строчку: #define
OS_MEM_MODEL NORMAL |
MSP430 |
|
430 |
Target:
msp430xx |
x51 |
|
S51 |
Target:
Memory Model - Small |
C51 |
Target:
Memory Model - Compact |
L51 |
Target:
Memory Model - Large |
таблица
2. Типы и
размер
указателей
сообщений.
Тип контроллера |
Тип
указателя и
размер в
байтах |
Все PIC
12, 14 бит |
(void const
*) 2 |
800, 80E |
(void far *) 2 |
I18 |
(void __dptr
*) 3 |
A20, A30,
A40, A50 |
(void *) 2 |
At0, At1,
As1, |
(void
__generic *) 2 |
At3, As3,
Ns3, Es3 |
(void
__generic *) 3 |
430 |
(void *) 2 |
S51, C51, L51 |
(void *) 3 |
Файл
jacnfg.h должен
определять
тип
библиотеки. Например,
если была
выбрана
библиотека
800ctun.lib, то должна
быть
добавлена
следующая
строчка:
#define OS_Lib_Type OS_ctun
В
символе,
который
присвоен OS_Lib_Type,
после OS_ следуют
все символы
из описания
библиотеки
за исключением
типа
контроллера.
Если
используются
приоритеты,
то должен
быть определен
максимальный
приоритет. Например,
если задачи
имеют
приоритеты 0,1,2
и 3, то пишем:
#define OS_MAX_PRIO 3
Нужно
стремиться
уменьшить
количество приоритетов
до
действительно
необходимого
числа, в
противном случае
вырастут
накладные
расходы на их
поддержание.
Если
была выбрана
библиотека с
поддержкой
сервисов в
прерываниях,
то они должны
быть
разрешены в
явном виде.
Всего может
понадобиться
пять таких
разрешений.
#define OS_USE_CSEM_I //
разрешает
использование
сервиса OS_Post_СSemI()
#define OS_USE_BSEM_I //
разрешает
использование
сервиса OS_Post_BSemI()
#define OS_USE_MSG_I //
разрешает
использование
сервиса OS_Post_MsgI()
#define OS_USE_FLAGS_I //
разрешает использование
сервиса OS_Set_FlagI()
#define OS_USE_MSGQ_I // для OS_Post_MsgQI()
Макросы
OS_Timer_Tick_Set() и OS_Is_Timer() также
должны быть
определены в
файле
конфигурации,
если,
конечно, их планируется
использовать.
Если
в программе
используется
несколько сервисов
ожидания
события, как
с таймаутом,
так и без
него, то
можно
попытаться уменьшить
занимаемую
ими память с
помощью:
#define OS_OPTI_SEMT //
оптимизирует
сервисы OS_Wait_Sem() и
OS_Wait_SemT()
#define OS_OPTI_MSGT //
для OS_Wait_Msg() и OS_Wait_MsgT()
#define OS_OPTI_MSGQT // для
OS_Wait_MsgQ() и OS_Wait_MsgQT()
#define
OS_OPTI_DELAY // для OS_Delay()
В
конфигурации
иногда нужно
будет определить
модель
памяти
библиотек, но
только в том
случае если
это
специально
оговаривается.
Например, для
библиотек Es3, Ns3
и Ns1
добавляем
строчку:
#define OS_MEM_MODEL NORMAL
Прототип
макрос
OS_Accept_Msg(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение);
Описание
Принимает
сообщение,
разрешая тем
самым посылку
следующего.
Если
сообщение
еще не
поступило,
возвращает NULL.
Аналогичен
сервису OS_Wait_Msg(), за
исключением
того, что не
переводит
задачу в
режим
ожидания.
Разрешено
использование: В
задачах, в background
Прототип
макрос
OS_Accept_MsgI(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение);
Описание
Принимает
сообщение,
разрешая тем
самым посылку
следующего.
Если
сообщение
еще не поступило,
возвращает NULL.
Сервис
предназначен
для работы в
прерываниях.
Разрешено
использование: В
прерываниях
Прототип
макрос
OS_Accept_MsgQ(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение);
Описание
Принимает
сообщение из
очереди. Если
очередь
пуста,
возвращает NULL.
Аналогичен
сервису OS_Wait_MsgQ(), за
исключением
того, что не
переводит
задачу в режим
ожидания.
Разрешено
использование: В
задачах, в background
Прототип
макрос
OS_Accept_MsgQI(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение);
Описание
Принимает
сообщение из
очереди. Если
очередь
пуста,
возвращает NULL.
Аналогичен
сервису OS_Wait_MsgQ(), за
исключением
того, что не
переводит
задачу в
режим
ожидания.
Сервис
предназначен
для работы в
прерываниях.
Разрешено
использование: В
прерываниях
Прототип
макрос
OS_Accept_Sem(OST_SEMAPHORE_P
указатель_на_ЕСВ_семафора);
Описание
Если
значение
бинарного
или счетного
семафора
больше 0, то
уменьшит его
на единицу. Аналогичен
сервису OS_Wait_Sem(), но
не переводит
задачу в
режим
ожидания.
Разрешено
использование: В
задачах, в background
Прототип
макрос
OS_Accept_SemI(OST_SEMAPHORE_P
указатель_на_ЕСВ_семафора);
Описание
Если
значение
бинарного
или счетного
семафора
больше 0, то
уменьшит его
на единицу. Аналогичен
сервису OS_Wait_Sem(), за
исключением
того, что не
переводит задачу
в режим
ожидания.
Сервис
предназначен
для работы в
прерываниях.
Разрешено
использование: В
прерываниях
Прототип
Макрос
OS_Clear_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_обнуляемых_флагов_события)
Описание
Очистит
указанные
биты в
значении
маскируемого
события.
Разрешено
использование: В
задачах,в background
Прототип
Макрос
OS_Clear_FlagI(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_обнуляемых_флагов_события)
Описание
Очистит
указанные
биты в
значении
маскируемого
события.
Сервис
предназначен
для работы в
прерываниях.
Разрешено
использование: В
прерываниях
Прототип
Макрос
OS_Cooperate()
Описание
Без
всяких условий
передает
управление
ядру ОС.
Разрешено
использование: В
задачах
Прототип
OST_TIMER OS_Cur_Delay
Описание
Открывает
доступ к
значению
задержки для задачи
с наименьшим
временем
ожидания. Используя
это свойство,
мы можем
организовать
переход
контроллера
в sleep на время
равное OS_Cur_Delay. Затем:
{OS_Cur_Delay = 1; OS_Timer();} Любое
изменение
значения OS_Cur_Delay
приведет к
изменению
времени
ожидания всех
задач. Так
что,
использование
сервиса для перехода
в sleep это
единственное
осмысленное
применение
для него.
Разрешено
использование: В
задачах, в background
Прототип
макрос OST_TASK_P OS_Cur_Task()
Описание
Возвращает
адрес блока
управления (TCB)
текущей
задачи.
Разрешено
использование: везде
Прототип
макрос
OS_Delay(OST_TIMER
задержка_в_тиках)
Описание
Переводит
задачу в
состояние
ожидания на указанное
время.
Значение
параметра
обязательно
должно быть
больше 0.
Разрешено
использование: В
задачах
Прототип
макрос
OS_Flag_Create(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG предустановленные_флаги_события)
Описание
Создает
маскируемое
событие и
присваивает
ему
первоначальное
значение.
Разрешено
использование: В
задачах, в background
Прототип
макрос
OS_Init()
Описание
Подготавливает
систему к
работе.
Должен быть вызван
самым первым.
Разрешено
использование: В background
Прототип
макрос
OS_Msg_Create(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение)
Описание
Создает
сообщение и
присваивает
ему
первоначальное
значение.
Разрешено
использование: В
задачах, в background
Прототип
макрос
OS_MsgQ_Create(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSGQ_DESCR_P указатель_на_дескриптор_очереди,
OST_U8 длина_очереди,
OST_MSG_P *указатель_на_массив_указателей_на_сообщения)
Описание
Создает
пустую
очередь
сообщений.
Очередь
организована
в виде
кольцевого
буфера указателей
на сообщения.
Поэтому,
кроме самого
буфера,
потребуется
создать дескриптор
очереди.
Дескриптор
содержит
информацию о
состоянии
очереди
(указатели на
голову и
хвост
очереди,
максимальный
размер, и
количество
непрочитанных
сообщений, а
так же
указатель на
начало
буфера).
Дескриптор
вынесен за
рамки ECB
для
обеспечения
большей
гибкости при
размещении в RAM.
Параметр
длина_очереди
ни в коем
случае не
должен
превышать
размерности
массива, отведенного
под очередь.
Разрешено
использование: В
задачах, в background
Пример:
OST_MSGQUEUE msgq;
bank1 OST_MSGQ_DESCR mqdscr;
bank1 OST_MSG_P mbuff[5];
__task void T_2( void )
{
OST_ERR_MSG
y;
OS_MsgQ_Create(&msgq,
&mqdscr, 5, mbuff);
while(1) {
counter++;
OS_Post_MsgQR(&msgq, rx, y);
}
}
Прототип
макрос OS_Post_BSem(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);
Описание
Установит
значение
бинарного
семафора в 1 если
он был равен 0.
Самая
приоритетная
из ожидающих
это событие
задач
перейдет в
состояние
готовности к
работе.
Разрешено
использование: В
задачах, в background
Прототип
OST_ERR_MSG
OS_Post_BSemI(OST_SEMAPHORE_P
указатель_на_ЕСВ_семафора);
Описание
Установит
значение
бинарного
семафора в 1 если
он был равен 0.
Самая
приоритетная
из ожидающих
это событие
задач
перейдет в
состояние
готовности к
работе.
Сервис
предназначен
для работы в
прерываниях.
Для того
чтобы
воспользоваться
этим
сервисом
необходимо
определить
символ OS_USE_BSEM_I в
файле
конфигурации.
Разрешено
использование: В
прерываниях
Возвращаемое
значение
OS_ERROR
- ошибка,
семафор уже
установлен
OS_NOERR
- отработали
успешно
Прототип
макрос
OS_Post_BSemR(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_ERR_MSG
код_возвтата)
Описание
Установит
значение
бинарного
семафора в 1 если
он был равен 0.
Самая
приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе.
Разрешено
использование: В
задачах, в background
Возвращаемое
значение
OS_ERROR
- ошибка,
семафор уже
установлен
OS_NOERR
– отработали
успешно
Прототип
макрос
OS_Post_CSem(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_SEM максимальное_значение_семафора);
Описание
Увеличит
на единицу
значение счетного
семафора.
Самая
приоритетная
из ожидающих
это событие
задач
перейдет в
состояние готовности
к работе. Второй
параметр
должен быть
больше 0, но не
может
превышать 255 (65535)
для 8-и (16) битных
семафоров.
Разрешено
использование: В
задачах, в background
Прототип
OST_ERR_MSG OS_Post_CSemI(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_SEM максимальное_значение_семафора);
Описание
Увеличит
на единицу
значение счетного
семафора.
Самая
приоритетная
из ожидающих
это событие
задач
перейдет в
состояние готовности
к работе. Сервис
предназначен
для работы в
прерываниях. Второй
параметр
должен быть
больше 0, но не может
превышать 255 (65535)
для 8-и (16) битных
семафоров.
Для того
чтобы воспользоваться
этим
сервисом
необходимо определить
символ OS_USE_CSEM_I в файле
конфигурации.
Разрешено
использование: В
прерываниях
Возвращаемое
значение
OS_ERROR -
ошибка,
семафор не
может
превысить свое
максимальное
значение.
OS_NOERR
- отработали
успешно
Прототип
макрос
OS_Post_CSemR(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_SEM максимальное_значение_семафора,
OST_ERR_MSG
код_возвтата)
Описание
Увеличит
на единицу
значение
счетного
семафора.
Самая
приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе.
Второй параметр
должен быть
больше 0, но не
может превышать
255 (65535) для 8-и (16)
битных
семафоров.
Разрешено
использование: В
задачах, в background
Возвращаемое
значение
OS_ERROR -
ошибка,
семафор не
может
превысить
свое максимальное
значение.
OS_NOERR
-
отработали
успешно
Прототип
макрос
OS_Post_Msg(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение);
Описание
Отправит
сообщение, но
только в том
случае если
нет, не
прочитанного
сообщения.
Самая приоритетная
из ожидающих
это событие
задач
перейдет в
состояние
готовности к
работе.
Разрешено
использование: В
задачах, в background
Прототип
OST_ERR_MSG
OS_Post_MsgI(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение)
Описание
Отправит
сообщение, но
только в том
случае если
нет, не
прочитанного
сообщения.
Самая приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе. Сервис
предназначен
для работы в
прерываниях.
Для того
чтобы
воспользоваться
этим сервисом
необходимо
определить
символ OS_USE_MSG_I в файле
конфигурации.
Разрешено
использование: В
прерываниях
Возвращаемое
значение
OS_ERROR
-
ошибка,
предыдущее
сообщение
еще не прочитано
OS_NOERR
-
отработали
успешно
Прототип
макрос
OS_Post_MsgQ(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение);
Описание
Поместит
сообщение в
очередь.
Самая
приоритетная
из ожидающих это
событие
задач
перейдет в
состояние готовности
к работе.
Разрешено
использование: В
задачах, в background
Прототип
OST_ERR_MSG OS_Post_MsgQI(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение);
Описание
Поместит
сообщение в
очередь.
Самая приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе. Сервис
предназначен
для работы в
прерываниях.
Для того
чтобы
воспользоваться
этим сервисом
необходимо
определить
символ OS_USE_MSGQ_I в файле
конфигурации.
Разрешено
использование: В
прерываниях
Возвращаемое
значение
OS_ERROR
-
ошибка. буфер
очереди
полон,
поэтому
сообщение
отправить не
удалось.
OS_NOERR
-
отработали
успешно
Прототип
макрос
OS_Post_MsgQR(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение,
OST_ERR_MSG
код_возвтата);
Описание
Поместит
сообщение в
очередь.
Самая приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе.
Разрешено
использование: В
задачах, в background
Возвращаемое
значение
OS_ERROR
-
ошибка. буфер
очереди
полон,
поэтому
сообщение
отправить не
удалось.
OS_NOERR
-
отработали
успешно
Прототип
Макрос
OS_Post_MsgR(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение,
OST_ERR_MSG
код_возвтата)
Описание
Отправит
сообщение, но
только в том
случае если
нет, не
прочитанного
сообщения.
Самая приоритетная
из ожидающих
это событие
задач перейдет
в состояние
готовности к
работе.
Разрешено
использование: В
задачах, в background
Возвращаемое
значение
OS_ERROR
-
ошибка,
предыдущее
сообщение
еще не прочитано
OS_NOERR
-
отработали
успешно
Прототип
Макрос
OS_Post_MsgRI(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение,
OST_ERR_MSG
код_возвтата)
Описание
Отправит
сообщение, но
только в том
случае если
нет, не
прочитанного
сообщения.
Самая приоритетная
из ожидающих
это событие
задач
перейдет в
состояние
готовности к
работе.
Сервис
предназначен
для работы в
прерываниях.
Разрешено
использование: В
прерываниях
Возвращаемое
значение
OS_ERROR
-
ошибка,
предыдущее
сообщение
еще не прочитано
OS_NOERR
-
отработали
успешно
Прототип
макрос
OST_FLG OS_Read_Flag(OST_FLAGS_P
указатель_на_ЕСВ_события)
Описание
Возвращает
текущее
состояние
битов указанного
маскируемого
события. Не
влияет на работу
других
сервисов
связанных с
этим событием.
Разрешено
использование: везде
Прототип
Макрос
OST_MSG_P OS_Read_Msg(OST_MESSAGE_P
указатель_на_ЕСВ_сообщения)
Описание
Возвращает
текущее
значение
указанного сообщения.
Не влияет на
работу
других сервисов
связанных с
этим
событием.
Разрешено
использование: везде
Прототип
макрос OST_SEM OS_Read_Sem(OST_SEMAPHORE_P
указатель_на_ЕСВ_семафора);
Описание
Возвращает
текущее
значение
указанного семафора.
Не влияет на
работу
других сервисов
связанных с
этим
событием.
Разрешено
использование: везде
Прототип
макрос
OS_Scheduler()
Описание
Менеджер
задач. Должен
быть вызван
из бесконечного
цикла.
Разрешено
использование: В background
Прототип
макрос
OS_Sem_Create(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_SEM первоночальное_значение_семафора);
Описание
Создает
бинарный или
счетный
семафор, используя
указанный ECB, и
присваивает
ему первоначальное
значение.
Разрешено
использование: В
задачах, в background
Прототип
макрос OS_Set_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_флагов_события)
Описание
Устанавливает
в единицу
указанные
биты в значении
маскируемого
события. Все
задачи,
ожидающие
это событие,
переходят в
состояние
готовности.
Разрешено
использование: В
задачах, в background
Прототип
void
OS_Set_FlagI(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG набор_флагов_события)
Описание
Устанавливает
флаги у
указанного
маскируемого
события.
Сервис
предназначен
для работы в
прерываниях.
Для того
чтобы воспользоваться
этим
сервисом
необходимо
определить
символ OS_USE_FLAGS_I в
файле
конфигурации.
Разрешено
использование: В
прерываниях
Прототип
макрос
OS_Status()
Описание
Возвращает
текущее
состояние
системы.
Разрешено
использование: везде
Возвращаемое
значение
OSS_EVENT -
произошло событие,
но еще не
обработано
OSS_IDLE -
нет готовых к
выполнению
задач
Прототип
макрос OS_Task_Create(
OST_ADDRESS
указатель_на_функцию,
OST_TASK_P
указатель_на_TCB,
OST_U8
приоритет);
Описание
Создает
задачу с
определенным
адресом запуска,
блоком
управления и
приоритетом.
Разрешено
использование: В
задачах, в background
Прототип
макрос OS_Task_Restart(
OST_ADDRESS адрес_возврата_в_задачу,
OST_TASK_P указатель_на_TCB);
Описание
Указанная
(отличная от
текущей) задача
возобновит
выполнение с
указанного адреса.
Разрешено
использование: везде
Прототип
OST_ERR_MSG
OS_Task_Resume(OST_TASK_P
указатель_на_TCB);
Описание
Возобновляет
выполнение
указанной
задачи, если
до этого она
была
остановлена.
Выполнение
будет
продолжено в
точке
останова.
Разрешено
использование: В
задачах, в background
Возвращаемое
значение
OS_ERROR
- ошибка,
задача не
находилась в
состоянии
"остановлена"
OS_NOERR
- отработали
успешно
Прототип
макрос
OS_Task_Status(OST_TASK_P
указатель_на_TCB,)
Описание
Возвращает
текущее
состояние
указанной задачи.
Разрешено
использование: везде
Возвращаемое
значение
Для
анализа
состояния
следует
использовать
маски:
OSS_TIMER_Q - задача
ожидает
окончания
задержки
OSS_EVENT_Q - задача
ожидает
события
OSS_STOPPED - задача
остановлена
OS_STS_PRIO_MASK -
приоритет
задачи
Прототип
макрос
OS_Task_Stop()
Описание
прекращает
выполнение
задачи
и переводит
ее в
состояние
"остановлена".
В дальнейшем
она может быть
восстановлена,
или ее ТСВ
может быть использован
для создания
другой
задачи.
Разрешено
использование: В
задачах
Прототип
макрос OS_Ticks()
Описание
Возвращает
текущее
значение
счетчика системных
тиков, по
существу это
счетчик
необработанных
тиков. OS_Timer()
увеличивает
счетчик
тиков
на 1-цу, но
только в том
случае, когда
есть хотя бы
одна ожидающая
системный
тик задача.
После того
как
управление
вернется
ядру, будут
обработаны
все
накопившиеся тики и
счетчик
тиков
обнулится.
Значение OS_Ticks() >= 2 говорит
о том, что, по
всей
вероятности,
система не
работает в
режиме
реального
времени.
Разрешено
использование: везде
Прототип
OST_ERR_MSG
OS_Timeout(void)
Описание
Функция
предназначена
для проверки
причины
окончания
ожидания
события
сервисами OS_Wait_SemT(),
OS_Wait_MsgT(), OS_Wait_MsgQT().
Разрешено
использование: В
задачах
Возвращаемое
значение
OS_ERROR
-
Ожидание
события было
прекращено по
таймауту.
OS_NOERR
-
Ожидаемое
событие
произошло до
возникновения
таймаута.
Прототип
макрос
OS_Timer()
Описание
Сигнализирует
системе о
прошедшем
определенном
интервале
времени.
Разрешено
использование: везде
Прототип
OST_TASK_P
OS_Timer_Queue()
Описание
Возвращает
указатель на
TCB задачи,
имеющей
наименьшее
время
задержки.
Если в текущий
момент
ожидающих
задач нет, то
вернет NULL.
Разрешено
использование: везде
Прототип
макрос
OS_Wait_Flag(
OST_FLAGS_P указатель_на_ЕСВ_события,
OST_FLG маска_события)
Описание
Проверяет,
соответствуют
ли
установленные
биты в значении
события
указанной
маске, и если
не соответствуют,
то переводит
задачу в
ожидание
такого
соответствия.
Разрешено
использование: В
задачах
Прототип
макрос
OS_Wait_Msg(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение);
Описание
Проверяет
наличие
сообщения, и
если сообщения
нет, то
переводит
задачу в
ожидание сообщения.
При успешном
ожидании
передает задаче
указатель на
сообщение.
Указатель на
сообщение
должен быть lvalue.
Разрешено
использование: В
задачах
Прототип
макрос OS_Wait_MsgQ(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение)
Описание
Проверяет
наличие
сообщений в
очереди, и если
сообщений
нет, то
переводит
задачу в
ожидание. В
том случае
если
сообщение существует,
а так же
после
успешного
ожидания,
передает
задаче
указатель на
сообщение.
Второй
параметр
должен быть lvalue.
Разрешено
использование: В
задачах
Прототип
макрос OS_Wait_MsgQT(
OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,
OST_MSG_P указатель_на_сообщение,
OST_TIMER до_таймаута_тиков)
Описание
Проверяет
наличие
сообщений в
очереди, и если
сообщений
нет, то
переводит
задачу в ожидание.
В том случае
если
сообщение
существует, а
так же после
успешного
ожидания,
передает задаче
указатель на
сообщение.
Время ожидания
ограничено
таймаутом. В
случае
таймаута
значение
указателя на
сообщение
равно NULL.
Второй
параметр
должен быть lvalue.
Причину окончания
ожидания
можно
установить с
помошью
функции OS_Timeout().
Разрешено
использование: В
задачах
Пример:
bank1 OST_TASK_T task3;
OST_MSGQUEUE msgq;
OST_MSG_P xx;
__task void T_3( void )
{
while(1) {
counter++;
OS_Wait_MsgQT(&msgq, xx, 3);
if (!
OS_Timeout() ) {
*(int *)xx = -18;
}
else {
counter = 0;
}
counter++;
}
}
Прототип
макрос
OS_Wait_MsgT(
OST_MESSAGE_P указатель_на_ЕСВ_сообщения,
OST_MSG_P указатель_на_сообщение,
OST_TIMER до_таймаута_тиков);
Описание
Проверяет
наличие сообщения,
и если
сообщения
нет, то
переводит задачу
в ожидание
сообщения.
Время
ожидания ограничено
таймаутом.
При успешном
ожидании
передает
задаче
указатель на
сообщение. В
случае
таймаута
значение
указателя не
определено.
Указатель на
сообщение
должен быть lvalue.
Значение
третьего
параметра
обязательно
должно быть
больше 0.
Причину
окончания ожидания
можно
установить с
помошью
функции OS_Timeout().
Разрешено
использование: В
задачах
Прототип
макрос
OS_Wait_Sem(OST_SEMAPHORE_P
указатель_на_ЕСВ_семафора);
Описание
Проверяет
значение
семафора, и
если семафор
равен нулю,
то переводит
задачу в
ожидание
сигнала
семафора.
Разрешено
использование: В
задачах
Прототип
макрос
OS_Wait_SemT(
OST_SEMAPHORE_P указатель_на_ЕСВ_семафора,
OST_TIMER до_таймаута_тиков)
Описание
Проверяет
значение
семафора, и
если семафор
равен нулю,
то переводит
задачу в
ожидание
сигнала
семафора.
Время
ожидания
ограничено
таймаутом.
Значение
второго
параметра
обязательно
должно быть
больше 0.
Причину
окончания
ожидания
можно
установить с
помошью функции
OS_Timeout().
Разрешено
использование: В
задачах
_____________________________________________________________________
http://jacos.narod.ru
jacos_post@mtu-net.ru