AVR. Учебный курс. Операционная система. УстановкаPrint This Post

Автор DI HALT
Опубликовано 09 Апр 2009 
Рубрики: AVR. Учебный курс
Метки: , ,

Ядро у нас есть, теперь осталось это все хозяйство запихать на МК. Для этого всего лишь надо рассовать нужные части кода в исходник. Показывать буду на примере 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

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

Комментарии

7 комментариев на «AVR. Учебный курс. Операционная система. Установка»


  1. Medved 09 Апр 2009 21:41

    Вот бы поглядеть на этот геморой в деле!!! Очень хочу увидеть!! ))))


  2. nestandart 09 Апр 2009 21:44

    чем таким красивым так красиво исходники в веб оформляешь ?

    DI HALT

    Плагин для вордпресса

    WP-Syntax 0.9.1
    Syntax highlighting using GeSHi supporting a wide range of popular languages. От Ryan McGeary.

    Syrok

    зачем он такие большие табы делает…
    беру свои слова обратно, иначе метки не влезут…

    DI HALT

    Эт я делаю по три таба от края.


  3. goodic 10 Апр 2009 17:09

    для 8-ки код выложить можешь?

    DI HALT

    На нее и будет

Оставьте свой отзыв

Вы должны войти, чтобы оставлять комментарии.


Материалы сайта являются авторскими. Копирование и публикация материалов без активной ссылки на первоисточник запрещено.

Реклама: