V1.06.0

 

jacOS
 

 

 

 


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() вне прерываний.

 

            Здесь все очень просто. Этот вариант, пожалуй, будет единственным способом работы 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()

Прототип

            макрос OS_Accept_Msg(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение);

Описание

Принимает сообщение, разрешая тем самым посылку следующего. Если сообщение еще не поступило, возвращает NULL. Аналогичен сервису OS_Wait_Msg(), за исключением того, что не переводит задачу в режим ожидания.       

Разрешено использование: В задачах, в background

 

OS_Accept_MsgI()

Прототип

            макрос OS_Accept_MsgI(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение);

Описание

Принимает сообщение, разрешая тем самым посылку следующего. Если сообщение еще не поступило, возвращает NULL. Сервис предназначен для работы в прерываниях.

Разрешено использование: В прерываниях

 

OS_Accept_MsgQ()

Прототип

            макрос OS_Accept_MsgQ(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение);

Описание

Принимает сообщение из очереди. Если очередь пуста, возвращает NULL. Аналогичен сервису OS_Wait_MsgQ(), за исключением того, что не переводит задачу в режим ожидания.          

Разрешено использование: В задачах, в background

 

OS_Accept_MsgQI()

Прототип

            макрос OS_Accept_MsgQI(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение);

Описание

Принимает сообщение из очереди. Если очередь пуста, возвращает NULL. Аналогичен сервису OS_Wait_MsgQ(), за исключением того, что не переводит задачу в режим ожидания. Сервис предназначен для работы в прерываниях.

Разрешено использование: В прерываниях

 

 

OS_Accept_Sem()

Прототип

            макрос OS_Accept_Sem(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);

Описание

Если значение бинарного или счетного семафора больше 0, то уменьшит его на единицу. Аналогичен сервису OS_Wait_Sem(), но не переводит задачу в режим ожидания.

Разрешено использование: В задачах, в background

 

 

OS_Accept_SemI()

Прототип

            макрос OS_Accept_SemI(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);

Описание

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

Разрешено использование: В прерываниях

 

 

OS_Clear_Flag()

Прототип

            Макрос OS_Clear_Flag(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   набор_обнуляемых_флагов_события)

Описание

            Очистит указанные биты в значении маскируемого события.   

Разрешено использование: В задачах,в background

 

OS_Clear_FlagI()

Прототип

            Макрос OS_Clear_FlagI(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   набор_обнуляемых_флагов_события)

Описание

Очистит указанные биты в значении маскируемого события. Сервис предназначен для работы в прерываниях.

Разрешено использование: В прерываниях

 

OS_Cooperate()

Прототип       

            Макрос OS_Cooperate()

Описание

            Без всяких условий передает управление ядру ОС. 

Разрешено использование: В задачах

 

OS_Cur_Delay

Прототип

OST_TIMER OS_Cur_Delay

Описание

Открывает доступ к значению задержки для задачи с наименьшим временем ожидания. Используя это свойство, мы можем организовать переход контроллера в sleep на время равное OS_Cur_Delay. Затем: {OS_Cur_Delay = 1; OS_Timer();} Любое изменение значения OS_Cur_Delay приведет к изменению времени ожидания всех задач. Так что, использование сервиса для перехода в sleep это единственное осмысленное применение для него.  

Разрешено использование: В задачах, в background

 

OS_Cur_Task()

Прототип

макрос OST_TASK_P OS_Cur_Task()

Описание

            Возвращает адрес блока управления (TCB) текущей задачи.

Разрешено использование: везде

 

OS_Delay()

Прототип

            макрос OS_Delay(OST_TIMER задержка_в_тиках)

Описание

            Переводит задачу в состояние ожидания на указанное время. Значение параметра обязательно должно быть больше 0.

Разрешено использование: В задачах

 

OS_Flag_Create()

Прототип

макрос OS_Flag_Create(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   предустановленные_флаги_события)

Описание

            Создает маскируемое событие и присваивает ему первоначальное значение.

Разрешено использование: В задачах, в background

 

 

OS_Init()

Прототип

            макрос OS_Init()

Описание

            Подготавливает систему к работе. Должен быть вызван самым первым.

Разрешено использование: В background

 

OS_Msg_Create()

Прототип

            макрос OS_Msg_Create(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение)

Описание

            Создает сообщение и присваивает ему первоначальное значение.

Разрешено использование: В задачах, в background

 

OS_MsgQ_Create()

Прототип

            макрос 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()

Прототип

            макрос OS_Post_BSem(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);

Описание

Установит значение бинарного семафора в 1 если он был равен 0. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. 

Разрешено использование: В задачах, в background

 

 

OS_Post_BSemI()

Прототип

            OST_ERR_MSG OS_Post_BSemI(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);

Описание

Установит значение бинарного семафора в 1 если он был равен 0. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. Сервис предназначен для работы в прерываниях. Для того чтобы воспользоваться этим сервисом необходимо определить символ OS_USE_BSEM_I в файле конфигурации.

Разрешено использование: В прерываниях

Возвращаемое значение

            OS_ERROR - ошибка, семафор уже установлен  

            OS_NOERR - отработали успешно

 

 

OS_Post_BSemR()

Прототип

            макрос OS_Post_BSemR(

OST_SEMAPHORE_P           указатель_на_ЕСВ_семафора,

OST_ERR_MSG                    код_возвтата)

Описание

Установит значение бинарного семафора в 1 если он был равен 0. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе.

Разрешено использование: В задачах, в background

Возвращаемое значение

            OS_ERROR - ошибка, семафор уже установлен  

            OS_NOERR – отработали успешно

 

OS_Post_CSem()

Прототип

            макрос OS_Post_CSem(

OST_SEMAPHORE_P           указатель_на_ЕСВ_семафора,

OST_SEM                               максимальное_значение_семафора);

Описание

Увеличит на единицу значение счетного семафора. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. Второй параметр должен быть больше 0, но не может превышать 255 (65535) для 8-и (16) битных семафоров.

Разрешено использование: В задачах, в background

 

OS_Post_CSemI()

Прототип

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()

Прототип

            макрос OS_Post_CSemR(

OST_SEMAPHORE_P           указатель_на_ЕСВ_семафора,

OST_SEM                               максимальное_значение_семафора,

OST_ERR_MSG                    код_возвтата)

Описание

Увеличит на единицу значение счетного семафора. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. Второй параметр должен быть больше 0, но не может превышать 255 (65535) для 8-и (16) битных семафоров.

Разрешено использование: В задачах, в background

Возвращаемое значение

OS_ERROR   - ошибка, семафор не может превысить свое максимальное значение.

            OS_NOERR   - отработали успешно

 

OS_Post_Msg()

Прототип

            макрос OS_Post_Msg(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение);

Описание

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

Разрешено использование: В задачах, в background

 

OS_Post_MsgI()

Прототип

            OST_ERR_MSG OS_Post_MsgI(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение)

Описание

Отправит сообщение, но только в том случае если нет, не прочитанного сообщения. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. Сервис предназначен для работы в прерываниях. Для того чтобы воспользоваться этим сервисом необходимо определить символ OS_USE_MSG_I в файле конфигурации.

Разрешено использование: В прерываниях

Возвращаемое значение

            OS_ERROR   - ошибка, предыдущее сообщение еще не прочитано  

            OS_NOERR   - отработали успешно

 

OS_Post_MsgQ()

Прототип

            макрос OS_Post_MsgQ(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение);

Описание

Поместит сообщение в очередь. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе.

Разрешено использование: В задачах, в background

 

OS_Post_MsgQI()

Прототип

            OST_ERR_MSG OS_Post_MsgQI(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение);

Описание

Поместит сообщение в очередь. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе. Сервис предназначен для работы в прерываниях. Для того чтобы воспользоваться этим сервисом необходимо определить символ OS_USE_MSGQ_I в файле конфигурации.

Разрешено использование: В прерываниях Возвращаемое значение

            OS_ERROR   - ошибка. буфер очереди полон, поэтому сообщение отправить не удалось.  

            OS_NOERR   - отработали успешно

 

OS_Post_MsgQR()

Прототип

            макрос OS_Post_MsgQR(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение,

OST_ERR_MSG        код_возвтата);

Описание

Поместит сообщение в очередь. Самая приоритетная из ожидающих это событие задач перейдет в состояние готовности к работе.

Разрешено использование: В задачах, в background

Возвращаемое значение

            OS_ERROR   - ошибка. буфер очереди полон, поэтому сообщение отправить не удалось.  

            OS_NOERR   - отработали успешно

 

OS_Post_MsgR()

Прототип

            Макрос OS_Post_MsgR(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение,

OST_ERR_MSG        код_возвтата)

Описание

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

Разрешено использование: В задачах, в background

Возвращаемое значение

            OS_ERROR   - ошибка, предыдущее сообщение еще не прочитано  

            OS_NOERR   - отработали успешно

 

OS_Post_MsgRI()

Прототип

            Макрос OS_Post_MsgRI(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение,

OST_ERR_MSG        код_возвтата)

Описание

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

Разрешено использование: В прерываниях

Возвращаемое значение

            OS_ERROR   - ошибка, предыдущее сообщение еще не прочитано  

            OS_NOERR   - отработали успешно

 

OS_Read_Flag()

Прототип

            макрос OST_FLG OS_Read_Flag(OST_FLAGS_P указатель_на_ЕСВ_события)

Описание

Возвращает текущее состояние битов указанного маскируемого события. Не влияет на работу других сервисов связанных с этим событием.

Разрешено использование: везде

 

 

OS_Read_Msg()

Прототип

            Макрос OST_MSG_P OS_Read_Msg(OST_MESSAGE_P указатель_на_ЕСВ_сообщения)

Описание

Возвращает текущее значение указанного сообщения. Не влияет на работу других сервисов связанных с этим событием.

Разрешено использование: везде

 

OS_Read_Sem()

Прототип

макрос OST_SEM OS_Read_Sem(OST_SEMAPHORE_P  указатель_на_ЕСВ_семафора);

Описание

Возвращает текущее значение указанного семафора. Не влияет на работу других сервисов связанных с этим событием.

Разрешено использование: везде

 

 

OS_Scheduler()

Прототип

            макрос OS_Scheduler()

Описание

            Менеджер задач. Должен быть вызван из бесконечного цикла. 

Разрешено использование: В background

 

OS_Sem_Create()

Прототип

            макрос OS_Sem_Create(

OST_SEMAPHORE_P           указатель_на_ЕСВ_семафора,

OST_SEM                               первоночальное_значение_семафора);

Описание

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

Разрешено использование: В задачах, в background

 

OS_Set_Flag()

Прототип

            макрос OS_Set_Flag(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   набор_флагов_события)

Описание

Устанавливает в единицу указанные биты в значении маскируемого события. Все задачи, ожидающие это событие, переходят в состояние готовности. 

Разрешено использование: В задачах, в background

 

OS_Set_FlagI()

Прототип

            void OS_Set_FlagI(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   набор_флагов_события)

Описание

Устанавливает флаги у указанного маскируемого события. Сервис предназначен для работы в прерываниях. Для того чтобы воспользоваться этим сервисом необходимо определить символ OS_USE_FLAGS_I в файле конфигурации.

Разрешено использование: В прерываниях

 

OS_Status()

Прототип

            макрос OS_Status()

Описание

            Возвращает текущее состояние системы.

Разрешено использование: везде

Возвращаемое значение

OSS_EVENT              - произошло событие, но еще не обработано

OSS_IDLE                  - нет готовых к выполнению задач

 

OS_Task_Create()

Прототип

            макрос OS_Task_Create(

OST_ADDRESS указатель_на_функцию,

OST_TASK_P указатель_на_TCB,

OST_U8 приоритет);

Описание

Создает задачу с определенным адресом запуска, блоком управления и приоритетом.

Разрешено использование: В задачах, в background

 

OS_Task_Restart()

Прототип

            макрос OS_Task_Restart(

            OST_ADDRESS        адрес_возврата_в_задачу,

OST_TASK_P            указатель_на_TCB);

Описание

Указанная (отличная от текущей) задача возобновит выполнение с указанного адреса.      

Разрешено использование: везде

 

OS_Task_Resume()

Прототип

            OST_ERR_MSG OS_Task_Resume(OST_TASK_P указатель_на_TCB);

Описание

Возобновляет выполнение указанной задачи, если до этого она была остановлена. Выполнение будет продолжено в точке останова.

Разрешено использование: В задачах, в background

Возвращаемое значение

            OS_ERROR - ошибка, задача не находилась в состоянии "остановлена"  

            OS_NOERR - отработали успешно

 

OS_Task_Status()

Прототип

            макрос OS_Task_Status(OST_TASK_P указатель_на_TCB,)

Описание

            Возвращает текущее состояние указанной задачи.

Разрешено использование: везде

Возвращаемое значение

            Для анализа состояния следует использовать маски:

OSS_TIMER_Q                      - задача ожидает окончания задержки

OSS_EVENT_Q                     - задача ожидает события

OSS_STOPPED                    - задача остановлена

OS_STS_PRIO_MASK          - приоритет задачи

 

OS_Task_Stop()

Прототип

            макрос OS_Task_Stop()

Описание

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

Разрешено использование: В задачах

 

OS_Ticks()

Прототип

            макрос OS_Ticks()

Описание

Возвращает текущее значение счетчика системных тиков, по существу это счетчик  необработанных тиков. OS_Timer() увеличивает счетчик тиков  на 1-цу, но только в том случае, когда есть хотя бы одна ожидающая системный тик задача. После того как управление вернется ядру, будут обработаны все накопившиеся  тики и счетчик тиков обнулится. Значение OS_Ticks() >= 2 говорит о том, что, по всей вероятности, система не работает в режиме реального времени.

Разрешено использование: везде

 

OS_Timeout()

Прототип

            OST_ERR_MSG OS_Timeout(void)

Описание

Функция предназначена для проверки причины окончания ожидания события сервисами OS_Wait_SemT(), OS_Wait_MsgT(), OS_Wait_MsgQT().

 

Разрешено использование: В задачах

Возвращаемое значение

            OS_ERROR   - Ожидание события  было прекращено  по таймауту.

            OS_NOERR   - Ожидаемое событие произошло до возникновения таймаута.

 

OS_Timer()

Прототип

            макрос OS_Timer()

Описание

            Сигнализирует системе о прошедшем определенном интервале времени.

Разрешено использование: везде

 

OS_Timer_Queue()

Прототип

            OST_TASK_P OS_Timer_Queue()

Описание

Возвращает указатель на TCB задачи, имеющей наименьшее время задержки. Если в текущий момент ожидающих задач нет, то вернет NULL. 

Разрешено использование: везде

 

 

 

 

OS_Wait_Flag()

Прототип

            макрос OS_Wait_Flag(

OST_FLAGS_P          указатель_на_ЕСВ_события,

OST_FLG                   маска_события)

Описание

Проверяет, соответствуют ли установленные биты в значении события указанной маске, и если не соответствуют, то переводит задачу в ожидание такого соответствия.

Разрешено использование: В задачах

 

OS_Wait_Msg()

Прототип

            макрос OS_Wait_Msg(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение);

Описание

Проверяет наличие сообщения, и если сообщения нет, то переводит задачу в ожидание сообщения. При успешном ожидании передает задаче указатель на сообщение. Указатель на сообщение должен быть lvalue.

Разрешено использование: В задачах

 

OS_Wait_MsgQ()

Прототип

            макрос OS_Wait_MsgQ(

OST_MSGQUEUE_P указатель_на_ЕСВ_очереди_сообщений,

OST_MSG_P              указатель_на_сообщение)

Описание

Проверяет наличие сообщений в очереди, и если сообщений нет, то переводит задачу в ожидание. В том случае если сообщение существует, а так же после успешного ожидания, передает задаче указатель на сообщение. Второй параметр должен быть lvalue.

Разрешено использование: В задачах

 

OS_Wait_MsgQT()

Прототип

            макрос 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()

Прототип

            макрос OS_Wait_MsgT(

OST_MESSAGE_P    указатель_на_ЕСВ_сообщения,

OST_MSG_P              указатель_на_сообщение,

OST_TIMER               до_таймаута_тиков);

Описание

Проверяет наличие сообщения, и если сообщения нет, то переводит задачу в ожидание сообщения. Время ожидания ограничено таймаутом. При успешном ожидании передает задаче указатель на сообщение. В случае таймаута значение указателя не определено. Указатель на сообщение должен быть lvalue. Значение третьего параметра обязательно должно быть больше 0. Причину окончания ожидания можно установить с помошью функции OS_Timeout().

 

Разрешено использование: В задачах

 

OS_Wait_Sem()

Прототип

            макрос OS_Wait_Sem(OST_SEMAPHORE_P указатель_на_ЕСВ_семафора);

Описание

Проверяет значение семафора, и если семафор равен нулю, то переводит задачу в ожидание сигнала семафора.

Разрешено использование: В задачах

 

OS_Wait_SemT()

Прототип

            макрос OS_Wait_SemT(

OST_SEMAPHORE_P           указатель_на_ЕСВ_семафора,

OST_TIMER                           до_таймаута_тиков)

Описание

Проверяет значение семафора, и если семафор равен нулю, то переводит задачу в ожидание сигнала семафора. Время ожидания ограничено таймаутом. Значение второго параметра обязательно должно быть больше 0. Причину окончания ожидания можно установить с помошью функции OS_Timeout().

Разрешено использование: В задачах

 

 

 

_____________________________________________________________________

 

http://jacos.narod.ru

 

jacos_post@mtu-net.ru