Операционная система для микроконтроллеров с ядром AVR.


AvrOS v.1.0

  1. Назначение.

Операционная система AvrOS v.1.0 для микроконтроллеров с ядром AVR предназначена для выполнения следующих функций:

  1. Общая структура системы.

Операционная система условно подразделяется на несколько модулей (уровней):



  1. Добавление нового модуля в систему.

    Программист-пользователь AVROS может добавлять в нее новые модули. Рассмотрим, как это делается.

    Предположим, нам надо ввести поддержку ШИМ. Это несложно.

    Шаг1. Принимаем решение на каком уровне будет размещен модуль. Если модуль напрямую взаимодействует с аппаратурой – то он должен располагаться в каталоге hl ровень аппаратного обеспечения). В нашем случае это так и есть.

    Шаг2. Пишем драйвер, который сохраним в файле hl/userpwm.c. Общая структура файла hl/userpwm.c следующая:

#ifdef USERPWM_ENABLED

// код драйвера

void userpwm_init(){/*код инициализации*/}

....

#endif /* USERPWM_ENABLED */

    Шаг2. Пишем заголовочный файл hl/userpwm.h. Этот файл – обязателен. В нем должен быть определен макрос userpwm_init_mac(). Это лучше делать следующим образом.

    #ifdef USERPWM_ENABLED

    #define userpwm_init_mac() userpwm_init()

// Все определения для драйвера, которые постоянны

...

#else

#define userpwm_init_mac()

    #endif /* USERPWM_ENABLED */

    Макрос userpwm_init_mac() испоьлзуется для автоматического вызова процедуры инициализации драйвера. Если даже драйвер не используется или процедура инициализации не нужна, то этот макрос все равно должен быть определен, хоть он и пустой. Общее правило определения имени макроса начальной инициализации

    #define <имя_модуля>_init_mac() <Имя процедуры инициализации>(), где

    <имя_модуля> - имя *.h файла, который описывает модуль. (В нашем случае userpwm, т.к. файл называется userpwm.h).

    <Имя процедуры инициализации> - имя процедуры инициализации, описанной в файле с кодом драйвера (В нашем случае это файл userpwm.c, процедура void userpwm_init()).

    Шаг 3. Создаем файл system/res_userpwm.h. В этом файле обязательно в начале должна быть строчка #define USERPWM_ENABLED. Если эту строчку закомментировать, то все, связанное с нашим драйвером не будет включено в код программы. Таким образом, чтобы добавить или удалить модуль из программы, достаточно раскомментировать или закомментировать всего одну строчку в файле res_<имя модуля>.h в каталоге system.

    Общая структура файла system/res_userpwm.h следующая:


#define USERPWM_ENABLED /* Закомментировать для отключения модуля */

    #ifdef USERPWM_ENABLED

    // Описание всех определений, которые часто изменяются (например разрядность ШИМ,

    // частота ШИМ в нашем случае и т.п.)

    #endif /* USERPWM_ENABLED */

    Шаг 4. Добавляем в файл system/sysdef.h строчку #include “res_userpwm.h”. Этим действием мы включаем в систему все определения из файла system/res_userpwm.h.

    Шаг 5. Открываем Makefile. Там есть строчки примерно такого вида

    # Hardware level

    HL-SRC = timer0.c int0.c adc0.c extports.c uart0.c pwm.c

    HL-OBJ = hl.o

    # Real-Time Level

    RTL-SRC = rt_task.c mlans.c mlan.c

    RTL-OBJ = rtl.o

    # Operating-System Level

    OSL-SRC = syscall.c led_mlan.c key.c

    OSL-OBJ = osl.o

    # User Program Level

    UPL-SRC = main.c

    UPL-OBJ = upl.o

    Поскольку наш новый модуль находится в каталоге hl, то нетрудно догадаться что для

    его добавления надо изменить строчку

    HL-SRC = timer0.c int0.c adc0.c extports.c uart0.c pwm.c

    на

    HL-SRC = timer0.c int0.c adc0.c extports.c uart0.c pwm.c userpwm.c

    То есть просто добавить имя файла с исходными текстами нашего нового модуля в список файлов с исходными текстами того уровня, где расположен наш модуль.

    Вуаля ! Можете компилировать вашу новую систему и наслаждаться.





  1. Вызов процедур пользователя по системному таймеру.

    После запуска системы прерывания всегда разрешены и периодически приходит прерывания от системного таймера. Пользователь может добавлять процедуры своих модулей для вызова их по прерыванию. Для этого надо открыть файл rtl_mac.h (для модулей уровней upl и osl) или hl_mac.h (для модулей уровня rtl), раскомментировать в нем одну из строчек и добавить в нее имя своей процедуры, которая должна вызываться по таймеру.

    Для модулей уровней upl и osl доступны следующие периоды вызова процедур:

    - 0.1 сек (вид #define UPL_TASK_100MS_xx или #define OSL_TASK_100MS_xx)

    - 1сек (вид #define UPL_TASK_1S_xx или #define OSL_TASK_1S_xx)

    - 1минута (вид #define UPL_TASK_1M_xx или #define OSL_TASK_1M_xx)

    - 1час. (вид #define UPL_TASK_1H_xx или #define OSL_TASK_1H_xx)

    Для модулей уровня rtl доступны следующие периоды вызова процедур:

    - FMAX (вид #define T_INT_MAX_xx )

    - 0.1 сек (вид #define T_INT_100MS_xx)

    - 1сек (вид #define T_INT_1S_xx)

    - 1минута (вид #define T_INT_1M_xx)

    - 1час. (вид #define T_INT_1H_xx)

    Частота FMAX – максимально возможная частота прерываний таймера. (определяется как F_CLK (частота кварца) деленная на количество тактов, через которые таймер посылает прерывания. Или FMAX= F_CLK/TIMER0_DEVIDER. Параметр F_CLK должен быть корректно указан в файле system/sysdef.h, а параметр TIMER0_DEVIDER – в файле system/res_timers.h.

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

    Приведем пример как создать эффект “бегущий огонь” на плате NedoPC-90.8535.

    Заходим в каталог upl.

    Пишем файл main.c (или изменяем его):

    //----------------------------------------------------------------------

    #define LEDPORT PORTB /*светодиодный порт*/

    #define LEDDDR DDRB /*регистр управления этим портом*/

    //----------------------------------------------------------------------

    char sled = 0x01; // 1 в том бите, где зажжен светодиод

    // Процедура, которая будет вызываемой по таймеру с периодом 1сек

    void ttask_leds_shift()

    {sled = sled << 0x01; // Сдвигаем 1 влево на 1 разряд

    if(sled > 0x0F){sled=0x01;} // Если 1 вышла за пределы младших 4х бит, возвращаем ее на место

    output(LEDPORT,(input(LEDPORT) | 0x0F) & (~sled));_NOP();_NOP(); // Изменяем состояния порта

    }

    //----------------------------------------------------------------------

    // Основная программа

    int main() {AUTOINIT_ALL_MODULES();

    // Инициализируем светодиодный порт

    output(LEDPORT,input(LEDPORT) | 0x0F);_NOP();_NOP();

    output(LEDDDR,input(LEDDDR) | 0x0F);_NOP();_NOP();

    //

    while(1){}

    //

    return(0);

    }

    //----------------------------------------------------------------------




    Пишем файл main.h (или изменяем его):

    //----------------------------------------------------------------------------

    // File: main.h

    // Contain headers for main.c

    //----------------------------------------------------------------------------

    void ttask_leds_shift();

    //----------------------------------------------------------------------------

    Заметьте, что имя процедуры, вызываемой по прерыванию, ОБЯЗАТЕЛЬНО должно быть указано в заголовочном файле. Иначе – ошибка компиляции.

    Пишем файл rtl_mac.h (или изменяем его):

    //----------------------------------------------------------------------

    // Макросы вызова процедур по таймеру уровня UPL

    //----------------------------------------------------------------------

    // 100ms

    //#define UPL_TASK_100MS_0()

    //#define UPL_TASK_100MS_1()

    //#define UPL_TASK_100MS_2()

    //#define UPL_TASK_100MS_3()

    //----------------------------------------------------------------------

    // 1s

    #define UPL_TASK_1S_0() ttask_leds_shift()

    //#define UPL_TASK_1S_1()

    //#define UPL_TASK_1S_2()

    //#define UPL_TASK_1S_3()

    //----------------------------------------------------------------------

    // 1minute

    //#define UPL_TASK_1M_0()

    //#define UPL_TASK_1M_1()

    //#define UPL_TASK_1M_2()

    //#define UPL_TASK_1M_3()

    //----------------------------------------------------------------------

    // 1hour

    //#define UPL_TASK_1H_0()

    //#define UPL_TASK_1H_1()

    //#define UPL_TASK_1H_2()

    //#define UPL_TASK_1H_3()

    //----------------------------------------------------------------------

    Можете компилировать вашу новую систему и наслаждаться тем, как красиво бегает огонек.

  1. Описание отдельных модулей пока не готово, но продолжение следует.