scmRTOS для AVR. Первая программа

  В предыдущей статье по scmRTOS мы создали в IARe некую заготовку. Теперь на ее основе мы напишем полноценную программу для микроконтроллера AVR. Программа будет простая — два светодиода моргающих с разной частотой. Однако  даже для ее реализации придется напрячь извилины и разобраться с основными моментами использования операционной системы.

Процессы  

   Каждый процесс в scmRTOS представляет собой отдельный класс, который описан с использованием шаблонов C++. Для создания процесса нужно определить его тип, объявить объект этого типа и написать функцию процесса Exec().

   Создание процесса выглядит следующим образом:

// Определяем тип процесса
typedef OS::process<OS::pr0, 40, 10> TProc1;

//Объявляем объект
TProc1 Proc1;

//функция процесса, выполняющая полезную работу
OS_PROCESS void TProc1::Exec()
{
      //объявления
      //инициализация данных процесса

    for(;;)
    {
        //полезный код
    }
}

  При определении типа процесса нужно задать его приоритет, размер стека данных, размер стека возвратов и имя.


   Приоритеты процессов определяют порядок их запуска планировщиком. Например, если в системе несколько процессов находятся в состоянии готовности и возникло событие, по которому должна произойти передача управления, то при прочих равных условиях планировщик передаст управление процессу с более высоким приоритетом.
   Приоритеты задаются с помощью перечислимого типа и могут принимать значения pr0, pr1, pr2 и так далее. Порядок старшинства приоритетов настраивается в конфигурационном файле scmRTOS_config.h.

//0  соответствует варианту, когда pr0 наиболее приоритетный процесс
//1 соответствует варианту, когда pr0 наименее приоритетный процесс

#define  scmRTOS_PRIORITY_ORDER             0

   Приоритеты процессов должны идти подряд и не должны повторяться. Приоритеты процессов задаются на этапе объявления типа процесса и не могут быть изменены по ходу программы.

   Процессы в scmRTOS используют два стека. В стеке данных - сохраняется контекст процесса, а в стеке возвратов - адреса возвратов из подпрограмм и прерываний.
   Размер стека данных определяется количеством используемых регистров (регистры общего назначения R0-R30 + регистр SREG + регистры указатели стека SPL и SPH) и количеством переменных процесса (так написано в документации, хотя для меня этот момент не понятен). Также стек данных будет использоваться при вызове  из процесса других функций - для передачи функциям аргументов или для сохранения содержимого зарезервированных регистров, в случае нехватки рабочих.
   С темой расчета размера стека данных я пока не разобрался.
   Размер стека возвратов определяется количеством вложенности вызовов подпрограмм. Размер стека возвратов в 2*N байт — соответствует N уровням вложенности подпрограмм.

  У каждого процесса есть исполняемая функция Exec(). В ней размещается пользовательский код. Он должен быть написан таким образом, чтобы выход из функции Exec() был невозможен. В общем случае в этой функции содержится бесконечный цикл, перед которым размещены объявления переменных  и инициализационный код.
 
  Операционная система scmRTOS позволяет объявить до 31 процесса. Количество объявленных процессов должно быть указано в конфигурационном файле scmRTOS_CONFIG.h.

//количество пользовательских процессов
#define  scmRTOS_PROCESS_COUNT              2
 
   Помимо процессов определенных пользователем, в операционной системе существует системный процесс - IdleProcess  (его не нужно создавать, он прописан в коде ОС). У него всегда самый низкий приоритет, не зависимо от заданного порядка старшинства приоритетов.
   В конфигурационном файле scmRTOS_config.h для системного процесса есть несколько настроек.

//размер стека данных и стека возвратов для системного процесса
#define scmRTOS_IDLE_PROCESS_DATA_STACK_SIZE         70
#define scmRTOS_IDLE_PROCESS_RETURN_STACK_SIZE     10

//0 – откл./1 – вкл. в системном процессе вызов функции IdleProcessUserHook()
//если указанная функция включена, то она должна быть определена пользователем

#define  scmRTOS_IDLE_HOOK_ENABLE           0

функция IdleProcessUserHook() определяется так:

void OS::IdleProcessUserHook()
{
  //какой-нибудь пользовательский код
}

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

process<prIDLE,
        scmRTOS_IDLE_PROCESS_DATA_STACK_SIZE,
        scmRTOS_IDLE_PROCESS_RETURN_STACK_SIZE> IdleProcess;

OS_PROCESS void process<prIDLE, 70, 10>::Exec()
{

    for(;;)
    {
        #if scmRTOS_IDLE_HOOK_ENABLE == 1
        IdleProcessUserHook();
        #endif
    }
}

Ошибка при описании функции Exec()! Заменил заданные значения размера стеков на именованные константы, и все откомпилировалось.

OS_PROCESS void process<prIDLE,
                                         scmRTOS_IDLE_PROCESS_DATA_STACK_SIZE,
                                        scmRTOS_IDLE_PROCESS_RETURN_STACK_SIZE >::Exec()
{
   ……
}

Посмотрел код в scmRTOS 3.10 – там этот баг устранили.


Планировщик

   Передача управления от одного процесса к другому может происходить в случаях когда:

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

   Собственно сама процедура передачи управления реализована в scmRTOS двумя способами:

- прямая передача управления путем вызова из планировщика функции переключателя контекста
- передача управления путем активации программного прерывания, в котором и происходит переключение контекста

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

   Способ передачи управления задается в конфигурационном файле scmRTOS_config.h.

//0 -  прямая передача управления,
//1 — передача управления с помощью программного прерывания

#define  scmRTOS_CONTEXT_SWITCH_SCHEME      0


Системный таймер

   Системный таймер служит для формирования временных интервалов, необходимых при работе процессов.
   Для реализации системного таймера в AVRках используется аппаратный таймер Т0. Перед запуском операционной системы этот таймер нужно инициализировать. Согласно документации на scmRTOS величину системного тика нужно установить в районе 1-10 мс.
    В конфигурационном файле scmRTOS_config.h для системного таймера есть несколько настроек.

//0 — выкл./1 — вкл. использование счетчика тиков системного таймера
#define  scmRTOS_SYSTEM_TICKS_ENABLE        1

//0 — запрещает/1 — разрешает  вложенные прерывания
//в обработчике прерывания от системного таймера

#define  scmRTOS_SYSTIMER_NEST_INTS_ENABLE  0

//0 выкл./1 вкл. в обработчике прерывания системного таймера
//вызов функции SystemTimerUserHook()
//в этом случае указанная функция
//должна быть определена в пользовательском коде   
 
#define  scmRTOS_SYSTIMER_HOOK_ENABLE       1

Функция SystemTimerUserHook() определяется так:

void OS::SystemTimerUserHook()
{
    //перезапуск таймера Т0 или какой-нибудь другой код

}

Первая программа на scmRTOS для AVR

  Описал вкратце ключевые моменты. Давайте собственно перейдем к нашей программе. Открываем проект. Открываем файл main.cpp. Нам нужно наполнить его полезным содержимым.

  В начале main.cpp подключаем заголовочные файлы.

#include <ioavr.h>
#include <scmRTOS.h> 

  Определяем типы процессов. У нас будет два процесса, каждый из которых будет управлять своим светодиодом. Размеры стеков процессов я задал импирически.

typedef OS::process<OS::pr0, 40, 10> TProc1;
typedef OS::process<OS::pr1, 40, 10> TProc2;

   Объявляем объекты процессов.

TProc1 Proc1;
TProc2 Proc2;

  Описываем функцию main. В ней находится инициализация порта B, инициализация системного таймера  T0 и функция запуска ОС.

#define LED1 0
#define LED2 1
#define LED_DDR DDRB
#define LED_PORT PORTB

int main()
{
    //настройка порта
    LED_DDR = 0xff;
    LED_PORT = (1<<LED1)|(1<<LED2);

    // инициализация системного таймера - период 1 мс
    TCNT0 = 0xf0;
    TCCR0 = 0x05;                  
    TIMSK |=  (1 << TOIE0);   
   
    //запуск операционной системы
    OS::Run();
}

   Описываем процессы.

OS_PROCESS void TProc1::Exec()
{
    for(;;)
    {
         LED_PORT ^= (1<<LED1);
         Sleep(500);  
    }
}

OS_PROCESS void TProc2::Exec()
{
    for(;;)
    {
         LED_PORT ^= (1<<LED2);
         Sleep(120);
    }
}

   В нашем примере мы используем два процесса и оба они передают передают управление сами, путем вызова функции Sleep(). Эта функция переводит текущий процесс из активного состояния в неактивное на заданное  количество системных тиков.

   Описываем функцию перезапуска системного таймера.

//функция перезапуска системного таймера
//она вызывается в прерывании

void OS::SystemTimerUserHook()
{
    TCNT0 = 0xf0;
}

   Настраиваем конфигурационный файл scmRTOS_CONFIG.h

//количество пользовательских процессов
#define  scmRTOS_PROCESS_COUNT              2

//запретить вложенные прерывания в обработчике прерывания системного таймера
#define  scmRTOS_SYSTIMER_NEST_INTS_ENABLE  0

//включить использование счетчика тиков системного таймера                 
#define  scmRTOS_SYSTEM_TICKS_ENABLE        1

//включить в обработчике прерывания системного таймера
// вызов функции SystemTimerUserHook()

#define  scmRTOS_SYSTIMER_HOOK_ENABLE       1

//выключить в системном процессе вызво функции IdleProcessUserHook()
#define  scmRTOS_IDLE_HOOK_ENABLE           0

//прямая передача управления
#define  scmRTOS_CONTEXT_SWITCH_SCHEME      0

//порядок приоритетов — pr0 имеет наивысший приоритет
#define  scmRTOS_PRIORITY_ORDER             0

Остальные настройки, не указанные здесь, трогать не нужно.

Компилируем проект и запускаем в железе или Proteus`e.


Файлы

Документация на scmRTOS v2
Дистрибутив scmRTOS v3.05
Проект для IARa - первая программа на scmRTOS
Проект для Proteus`a

Другие материалы в этой категории: scmRTOS для AVR. "Установка". Вариант для IAR`a »

У вас недостаточно прав для комментирования.