[an error occurred while processing this directive]
для NAUT'а
(«Телесистемы»: Конференция «Микроконтроллеры и их применение»)

миниатюрный аудио-видеорекордер mAVR

Отправлено уни 09 апреля 2006 г. 13:24

Итак, здесь пример переключателя 2^n задач. Ниже жестко прописано для 2 задач.
Микроконтроллер - AT90S2313. Кварц вроде бы я спользовал 8 МГц-вый, частота влияет
на нулевой таймер, который по совместительству и отсчитывает кванты времени для
любой задачи (см. настройки таймера0). Поскольку 2313 имеет уж очень мало ресурсов,
(хотя это не помешало забацать на нём софтUSB) то память ОЗУ жестко разбита вручную
(мы же на асме пишем). Поскольку дело было очень давно, то точные значения в hex я уже
вспомнить не могу, поэтому обрисую картинку в общем.
1) Задачи 2, т.к. ОЗУ всего 128 байт - каши не сваришь. При использовании 8535
у меня было 8 задач.
2) Для каждой задачи определяется контекст, это просто означает, что для задачи
выделяется область ОЗУ определённого размера. По-моему, я использовал размер 32 байта.
Контекст - это всё то, что необходимо иметь в задаче (когда на неё передано управление),
чтобы она "думала", что монопольно владеет МК. Полностью конечно так сделать нельзя.
А частично в примере ниже в контексте сохраняются:
- текущее значение PC (нужно иметь в виду, что прямых команд для изменения этого
регистра нет, поэтому см. в таблицу команд в спецификации на МК и выбираем те,
которые модифицируют PC - для меня подошло использование ret и reti);
- текущее значние статус регистра;
- все верхние регистры от R16 до R31 - нижние сохранять не только по-моему не имеет
смысла, но и как это сделать я тогда не знал. Кроме того, как известно, нижние и
верхние регистры не равноценны, а те кто программил на чистом асме под avr, несомненно
в курсе, что иметь для каждой задачи все верхние регистры - это великое счастье.

Оставшиеся от 32 байт выделенной памяти место - стек текущей задачи. Ясный пень,
вложенность ограничена. 32 - 2 (PC) - 1 (SREG) - 16 (Rxx) = 13 делим попалам и получаем
вложенность около 6, либо использовать 13 байт для PUSH/POP'а регистров.

Вот значит, перед началом работы переключателя сначала нужно инициализирвать области
под контексты и кроме того в начало RAM есть 2 ячейки для 2-х адресов - места прерывания
задач Task0 и Task1. Данный переключаель называется неприоритетным. Прерывания у меня
использовали конец RAM при операции со стеком, либо вообще стек не использовали -
нельзя портить в ISR то, что находится в ОЗУ контекстов.
Наверное надо ещё че-нить написать... но не знаю, никогда описанием этой проги не занимался.
Про то, что это подходит под описание "карусельного" переключателя узнал недавно от
Энди Таненбаума - из книжки его по проектированию ОС Minix.

Вот код. Он может быть скомпилен в AVR Studio 4. Если Вы используете последнюю версию,
то поставьте галочку - использовать ASM версии 1, т.к. прога эта писалась в 2003 году и
тогда я пользовался этим убогим Ассемблером. Вторая версия удобнее несомненно.

// Main.asm
; CP-1251 (WIN)
; Таблица векторов прерываний
.cseg
.org 0000
rjmp Reset ; Reset Handle
reti ; IRQ0 Handler
reti ; IRQ1 Handler
reti ; Timer1 Capture Handler
reti ; Timer1 Compare Handler
reti ; Timer1 Overflow Handler
mTaskMngr:
rjmp TIM0_OVF ; Timer0 Overflow Handler
reti ; UART RX Complete Handler
reti ; UDR Empty Handler
reti ; UART TX Complete Handler

.include "inc\2313def.inc"
.include "inc\def.inc"
.include "task0.asm"
.include "task1.asm"

; Действия при перезагрузке
Reset:
ldi _tmp, Low(RAMEND)
mov RAMEndLo, _tmp
out SPL, _tmp
; Начальные установки регистров направления и портов
rcall InitExtInts
rcall InitPorts
rcall InitTimers
; Инициализация задач
rcall InitTask0
rcall InitTask1
clr ZeroReg
clr tSPlo
clr DFlags
clr MsgFlags
clr jTskCnt
; Установка флагов по умолчанию
; ...
; Переход на нулевую задачу
ldi _tmp, 0x8F
out SPL, _tmp
; Начальное значение PC для нулевой задачи
ldi _tmp, Low(Task0)
push _tmp
ldi _tmp, High(Task0)
push _tmp
ClearUpRegs

; Обработчик прерывания от нулевого таймера
TIM0_OVF:
; в стеке находятся два байта PC
; необходимо ещё сохранить значение статус регистра
; и регистров общего назначения
; значение счётчика текущей задачи хранится в jTskCnt
in stmp, SReg
push stmp
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r28
push r29
push r30
push r31
; сохраняем указатель стека текущей задачи
mov TaskCnt, jTskCnt
ldi XH, High(BegCntxAddr)
ldi XL, Low(BegCntxAddr)
andi TaskCnt, 0x01 ; две задачи
mov CntxOfst, TaskCnt
add XL, CntxOfst
adc XH, ZeroReg
in _tmp, SPL
st X, _tmp
inc TaskCnt
; теперь переключаемся на стек следующей задачи
ldi XH, High(BegCntxAddr)
ldi XL, Low(BegCntxAddr)
andi TaskCnt, 0x01 ; две задачи
mov CntxOfst, TaskCnt
add XL, CntxOfst
adc XH, ZeroReg
ld _tmp, X
out SPL, _tmp
mov jTskCnt, TaskCnt
; восстанавливаем все регистры
pop r31
pop r30
pop r29
pop r28
pop r27
pop r26
pop r25
pop r24
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
; начальное значение счётчика нулевого таймера
out TCNT0, ZeroReg
pop stmp
out SReg, stmp
reti; TIM0_OVF

InitExtInts:
; ...
ret; InitExtInts

InitPorts:
; ...
ret; InitPorts

InitTimers:
ldi _tmp, ((1< out TCCR0, _tmp
ldi _tmp, ((1< out TCCR1B, _tmp
; начальное значение счётчика нулевого таймера
clr _tmp
out TCNT0, _tmp
; начальное значение счётчика Таймера 1 (~100 мс)
ldi _tmp, 0xFE
out TCNT1H, _tmp
ldi _tmp, 0x79
out TCNT1L, _tmp
; установка битов прерываний от таймеров
ldi _tmp, ((1< out TIMSK, _tmp
; инициализация программных таймеров
ldi XH, High(BegCntrAddr)
ldi XL, Low(BegCntrAddr)
; начальное значение счётчика 0
ser _tmp
st X+, _tmp
st X+, _tmp
clr _tmp
st X+, _tmp
st X+, _tmp
; начальное значение счётчика 1
ser _tmp
st X+, _tmp
st X+, _tmp
clr _tmp
st X+, _tmp
st X+, _tmp
ret; InitTimers

===============================================================

// task0.asm
.include "inc\task0.inc"

; нулевая задача
.cseg
TASK0:
rjmp Task0
; sjmp Task0 ; прерывание задачи и возврат к менеджеру
; --------------------- End Task0 -----------------------

InitTask0:
in tSPlo, SPL
; Указатель на контекст нулевой задачи находится в SRAM
; по адресу 0x0060, размером 2 байта, по меньшему адресу
; младший байт
ldi XH, High(PCntx0Addr)
ldi XL, Low(PCntx0Addr)
; Сохраняем указатель стека нулевой задачи
; Инициализируем указатель контекста нулевой задачи
ldi _tmp, 0x7C
st X, _tmp
ldi _tmp, 0x8F
out SPL, _tmp
; Начальное значение PC для нулевой задачи
ldi _tmp, Low(Task0)
push _tmp
ldi _tmp, High(Task0)
push _tmp
; Сохраняем текущее значение статус регистра
in _tmp, SReg
push _tmp
; Инициализируем контекст нулями
ldi _tmp, 29
mov ZeroReg, _tmp
clr _tmp
t0Loop0:
push _tmp
dec ZeroReg
brne t0Loop0
out SPL, tSPlo
ret; InitTask0

===============================================================

// task1.asm
.include "inc\task1.inc"

; первая задача
.cseg
TASK1:
nop
rjmp Task1
; sjmp Task1 ; прерывание задачи и возврат к менеджеру
; --------------------- End Task1 -----------------------

InitTask1:
in tSPlo, SPL
; Указатель на контекст первой задачи находится в SRAM
; по адресу 0x0061, размером 1 байт
ldi XH, High(PCntx1Addr)
ldi XL, Low(PCntx1Addr)
; Сохраняем указатель стека первой задачи
; Инициализируем указатель контекста первой задачи
ldi _tmp, 0x9C
st X, _tmp
ldi _tmp, 0xAF
out SPL, _tmp
; Начальное значение PC для первой задачи
ldi _tmp, Low(Task1)
push _tmp
ldi _tmp, High(Task1)
push _tmp
; Сохраняем текущее значение статус регистра
in _tmp, SReg
push _tmp
; Инициализируем контекст нулями
ldi _tmp, 29
mov ZeroReg, _tmp
clr _tmp
t1Loop0:
push _tmp
dec ZeroReg
brne t1Loop0
out SPL, tSPlo
ret; InitTask1

================================================================

// def.inc
.equ DelBit0 = 0
.equ DelBit1 = 1
.equ DelBit2 = 2
.equ DelBit3 = 3
.equ DelBit4 = 4
.equ DelBit5 = 5
.equ DelBit6 = 6
.equ DelBit7 = 7

.equ AddrCnt0 = 0x0062
.equ AddrCnt1 = 0x0066

.equ BegCntxAddr = 0x0060
.equ BegCntrAddr = 0x0062

.equ PCntx0Addr = 0x0060
.equ PCntx1Addr = 0x0061

.equ DataAddr = 0x006F


; Младшие регистры
;.def = r0
;.def = r1
;.def = r2
;.def = r3
;.def = r4
;.def = r5
;.def = r6
;.def = r7
.def RAMEndLo = r8
.def tSPlo = r9
.def StatReg = r10
.def ZeroReg = r11
.def CntxOfst = r12
.def stmp = r12
.def DFlags = r13
.def MsgFlags = r14
.def jTskCnt = r15

; Старшие регистры
.def _tmp = r16
.def TaskCnt = r17
;.def = r18
;.def = r19
;.def = r20
;.def = r21
;.def = r22
;.def = r23
;.def = r24
;.def = r25
;.def = r26
;.def = r27
;.def = r28
;.def = r29
;.def = r30
;.def = r31

.macro sjmp
cli
ldi ZL, Low(@0)
push ZL
ldi ZH, High(@0)
push ZH
; Jump to mTaskMngr
ldi ZL, Low(mTaskMngr)
ldi ZH, High(mTaskMngr)
ijmp
.endm

.macro SetFlag
set
bld @0, @1
.endm

.macro ClrFlag
clt
bld @0, @1
.endm

.macro ClearUpRegs
clr r16
clr r17
clr r18
clr r19
clr r20
clr r21
clr r22
clr r23
clr r24
clr r25
clr r26
clr r27
clr r28
clr r29
clr r30
clr r31
.endm

.macro ConvLow
cbi PortB, ConvPin
.endm

.macro ConvHigh
sbi PortB, ConvPin
.endm

.macro SClkLow
cbi PortB, SClkPin
.endm

.macro SClkHigh
sbi PortB, SClkPin
.endm

============================================

// task0.inc и task1.inc одинаковы
;.def = r16
;.def = r17
;.def = r18
;.def = r19
;.def = r19
;.def = r20
;.def = r21
;.def = r22
;.def = r23
;.def = r24
;.def = r25
;.def = r26
;.def = r27
;.def = r28
;.def = r29
;.def = r30
;.def = r31

Это проект просто шаблон для исследования переключателя или для новых простеньких проектов.
К этому переключателю идёт ещё целая библиотека подпрограмм, а написание задач - отдельная
песня. Так вот - тут можно вполне считать, что есть 2 более менее устойчивых состояния - задача 0
и задача 1. Прерываться они могу и по Таймеру0 и могу самостоятельно покидать свой цикл,
который в примере не показан. Я вообще дальше я "построил" очери сообщений и завязал их
с интерфейсом и получил нечто вроде Windows'а :) А основа - клавиатурный "автомат" как тут SM
описал. Только это уже на 8535 делано было.

2 NAUT: Про то, что неактуально я сказал, т.к. 2 года я уже не занимался вообще с тех пор контроллерами и радиотехникой вообще. А вот тянет обратно :) вот посмотрел, что нынче делается, так думаю Вам подскажут что нить более простое в использовании или общеупотребительное. Я по незнанию ваял своё и мало разумел в Си.

Составить ответ  |||  Конференция  |||  Архив

Ответы


Отправка ответа

Имя (обязательно): 
Пароль: 
E-mail: 
NoIX ключ Запомнить

Тема (обязательно):
Сообщение:

Ссылка на URL: 
Название ссылки: 

URL изображения: 


Rambler's Top100 Рейтинг@Mail.ru
Перейти к списку ответов  |||  Конференция  |||  Архив  |||  Главная страница  |||  Содержание

E-mail: info@telesys.ru