AVR. Учебный курс. Операционная система. Установка
Автор DI HALT
Опубликовано 09 Апр 2009
Рубрики: AVR. Учебный курс
Метки: Assembler, AVR, RTOS
Ядро у нас есть, теперь осталось это все хозяйство запихать на МК. Для этого всего лишь надо рассовать нужные части кода в исходник. Показывать буду на примере ATmega8. Для других МК разница минимальная. Может быть с таймером что нибудь помудрить придется, но не более того.
Например, недавно, вкорячивал ту же схему на ATmega168, так пришлось подправить иницилизацию таймера - регистры там зовутся по другому. Пришлось изменить макрос OUTI - так как многие привычные уже регистры перестали загружаться через комадну OUT - выпали из диапазона, только через LDS/STS ну и, собственно, все хлопоты. Потратил минут 20 на переименование регистров и заработало.
Итак. Есть у нас совершенно новый пустой файл NewMega8-rtos.asm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | ; Добавляем в него первым же делом инклюдник восьмой меги: .include "m8def.inc" ; Используем ATMega8 ; Следом я добавляю файл с моими макроопределениями в котором записаны все символические имена ; для ресурсов, вроде регистров, портов, отдельных пинов. ИМХО удобней все держать по разным ; файлам, но тут уже дело за вашими привычками. .include "define.asm" ;Потом файл с макросами, он тоже отдельный и кочует из программы в программу. Именно там всякие ; левые самодельные команды вроде OUTI прописаны. .include "macro.asm" ; Все макросы у нас тут ; Следом идет файл макросов ядра ОС. Он должен распологаться в начале программы, иначе компилятор ; не поймет. Именно там прописаны макросы таймерной службы, добавления задачи и таймера, там же ; заныкан стандартный макрос инциализации UART и многое другое. .include "kernel_macro.asm" ;Дальше прописывается сегмент оперативной памяти в котором заранее определены ; все очереди задач и таймеров. .DSEG .equ TaskQueueSize = 11 ; Размер очереди событий TaskQueue: .byte TaskQueueSize ; Адрес очереди сотытий в SRAM .equ TimersPoolSize = 5 ; Количество таймеров TimersPool: .byte TimersPoolSize*3 ; Адреса информации о таймерах ; Следом уже идет код, начинается кодовый сегмент. Надо заметить, что у меня вся таблица ; Прерываний спрятана в vectors.asm и вместо таблицы в коде .include "vectors.asm" это удобно .CSEG .ORG 0x0000 ; Проц стартует с нуля, но дальше идут вектора RJMP Reset .ORG INT0addr ; External Interrupt Request 0 RETI .ORG INT1addr ; External Interrupt Request 1 RETI .ORG OC2addr ; Timer/Counter2 Compare Match RJMP OutComp2Int ;<<<<<<<< Прерывание ОС!!! .ORG OVF2addr ; Timer/Counter2 Overflow RETI .ORG ICP1addr ; Timer/Counter1 Capture Event RETI .ORG OC1Aaddr ; Timer/Counter1 Compare Match A RETI .ORG OC1Baddr ; Timer/Counter1 Compare Match B RETI .ORG OVF1addr ; Timer/Counter1 Overflow RETI .ORG OVF0addr ; Timer/Counter0 Overflow RETI .ORG SPIaddr ; Serial Transfer Complete RETI .ORG URXCaddr ; USART, Rx Complete RJMP Uart_RCV .ORG UDREaddr ; USART Data Register Empty RETI .ORG UTXCaddr ; USART, Tx Complete RJMP Uart_TMT .ORG ADCCaddr ; ADC Conversion Complete RETI .ORG ERDYaddr ; EEPROM Ready RETI .ORG ACIaddr ; Analog Comparator RETI .ORG TWIaddr ; 2-wire Serial Interface RETI .ORG SPMRaddr ; Store Program Memory Ready RETI .ORG INT_VECTORS_SIZE ; Конец таблицы прерываний |
После таблицы векторов идут обработчики прерываний. Они короткие, поэтому их размещаю в начале. Первым же обработчиком идет обработчик прерывания от таймера на котором висит таймерная служба ОС. Под таймерную службу желательно отдать самый стремный таймер, на который не завязана ШИМ или еще какая полезная служба. В идеале бы под это дело пустить Timer0, как самый лоховский. Но он не умеет cчитать от 0 до регистра сравнения, только от нуля до 255, впрочем, в обработчик прерывания можно добавить предварительную загрузку таймера0 нужным значением и не расходовать более навороченный таймер. Мне было лень, я повесил все на Таймер2, а в регистр сравнения прописал такое значение, чтобы прерывание было ровно один раз в 1мс. Разумеется, выставив предварительно нужный делитель.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ; Interrupts procs ; Output Compare 2 interrupt - прерывание по совпадению TCNT2 и OCR2 ; Main Timer Service - Служба Таймеров Ядра - Обработчик прерывания OutComp2Int: TimerService ; Служба таймера OS ; Весь код обработчика в виде одного макроса ; Просто вставил и все. Куда угодно. Можно извратиться ; Подать импульсы с нужной частотой на какой-нибудь ; INT0 и службу таймеров повесить на его прерывание ; Разумеется, в таблице векторов из вектора прописан ;переход сюда RETI ; выходим из прерывания Uart_RCV: RETI ; Другие прерывания если нужны Uart_TMT: RETI |
То все было обязательной подготовкой и разметкой адресов и памяти, а вот тут уже начинается сама программа. Именно отсюда стартует проц. Вписываем следующий код:
1 2 | Reset: OUTI SPL,low(RAMEND) ; Первым делом инициализируем стек OUTI SPH,High(RAMEND) |
Все инициализации у меня спрятана в .include “init.asm”, но тут я распишу ее полностью.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ; init.asm ; Очистка памяти RAM_Flush: LDI ZL,Low(SRAM_START) LDI ZH,High(SRAM_START) CLR R16 Flush: ST Z+,R16 CPI ZH,High(RAMEND) BRNE Flush CPI ZL,Low(RAMEND) BRNE Flush CLR ZL CLR ZH ; Init RTOS ; В исходнике все сделано ; INIT_RTOS ; вот так вот, одним макросом. Макрос описан ; ; в файле kernel_macro.asm ; ; Но я распишу тут подробно. Там идет настройка ; ; Таймера в работу ; Содержимое макроса INIT_RTOS OUTI SREG, 0 ; Сброс всех флагов rcall ClearTimers ; Очистить список таймеров РТОС rcall ClearTaskQueue ; Очистить очередь событий РТОС sei ; Разрешить обработку прерываний ; Настройка таймера 2 - Основной таймер для ядра .equ MainClock = 8000000 ; CPU Clock .equ TimerDivider = MainClock/64/1000 ; 1 mS OUTI TCCR2,1<<CTC2|4<<CS20 ; Установить режим CTC и предделитель =64 OUTI TCNT2,0 ; Установить начальное значение счётчиков ldi OSRG,low(TimerDivider) out OCR2,OSRG ; Установить значение в регистр сравнения ; Конец макроса INIT_RTOS OUTI TIMSK,1<<OCF2 ; Разрешить прерывание по сравнению ; Инициализация остальной периферии USART_INIT ; Конец init.asm |
После инициализации идет секция запуска фоновых приложений. Добавим пока меточку про запас, с нас не убудет.
1 | Background: NOP ; Пока тут ничего нет |
Главный цикл. Его надо скопировать без изменений, как есть.
1 2 3 4 5 6 7 8 | Main: SEI ; Разрешаем прерывания. wdr ; Reset Watch DOG rcall ProcessTaskQueue ; Обработка очереди процессов rcall Idle ; Простой Ядра rjmp Main ; Основной цикл микроядра РТОС ; В Idle можно сунуть что нибудь простое, быстрое и некритичное. ; Но я обычно оставляю его пустым. |
После главного цикла вставляется шаблон под секцию задач. Именно сюда вписывается наш исполняемый код. Тут творится самое интересное
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Idle: RET ;----------------------------------------------------------------------------- Task1: RET ;----------------------------------------------------------------------------- Task2: RET ;----------------------------------------------------------------------------- Task3: RET ;----------------------------------------------------------------------------- Task4: RET ;----------------------------------------------------------------------------- Task5: RET ;----------------------------------------------------------------------------- Task6: RET ;----------------------------------------------------------------------------- Task7: RET ;----------------------------------------------------------------------------- Task8: RET ;----------------------------------------------------------------------------- Task9: RET ; А после секции задач вставляем шаблонную таблицу переходов и код ядра .include "kerneldef.asm" ; Подключаем настройки ядра .include "kernel.asm" ; Подклчюаем ядро ОС TaskProcs: .dw Idle ; [00] .dw Task1 ; [01] .dw Task2 ; [02] .dw Task3 ; [03] .dw Task4 ; [04] .dw Task5 ; [05] .dw Task6 ; [06] .dw Task7 ; [07] .dw Task8 ; [08] .dw Task9 ; [09] |
Готово! Можно компилировать, пока это пустой проект. Но ненадолго.
Итак, теперь то же самое, но по пунктам.
Установка AVR OS
- Создаем пустой проект
- Вставляем файлы макроопределений
- Вставляем разметку памяти под очереди задач/таймеров
- Вставляем таблицу векторов прерываний
- Прописываем в таблице векторов прерываний переход на обработчик таймера по переполнению
- Добавляем обработчик прерываний
- Прописываем стартовую метку и инициализацию стека
- Инициализация всего что только можно - портов, периферии, обнуление ОЗУ, обнуление очередей, запуск таймера ОС.
- Добавляем секцию фоновых задач
- Добавляем код главного цикла
- Добавляем шаблонную сетку задач
- Добавляем код ядра и таблицу переходов.
- Пишем наш код
- . . .
- PROFIT
В следующий раз я покажу практический пример работы с этой ОС. В котором будет красочно показано ради чего, собственно, этот геморрой и почему мне он так нравится :)
Комментарии
7 комментариев на «AVR. Учебный курс. Операционная система. Установка»
Оставьте свой отзыв
Вы должны войти, чтобы оставлять комментарии.





Вот бы поглядеть на этот геморой в деле!!! Очень хочу увидеть!! ))))
чем таким красивым так красиво исходники в веб оформляешь ?
Плагин для вордпресса
WP-Syntax 0.9.1
Syntax highlighting using GeSHi supporting a wide range of popular languages. От Ryan McGeary.
зачем он такие большие табы делает…
беру свои слова обратно, иначе метки не влезут…
Эт я делаю по три таба от края.
для 8-ки код выложить можешь?
На нее и будет