Код
;----------------------------------------------------------------------------
;Программа мигания двумя светодиодами
;Микроконтроллер: AT90S2313
;Частота кварца: 10 МГц
;----------------------------------------------------------------------------
.include "2313def.inc" ;подключаем файл описания регистров
;----------------------------------------------------------------------------
;Константы:
.equ FCLK = 10000000 ;тактовая частота, Гц
.equ TSYS = 10000 ;системный квант времени, мкс
.equ LED1_ON = 500 ;время работы светодиода 1, мс
.equ LED1_OFF = 500 ;время паузы светодиода 1, мс
.equ LED2_ON = 100 ;время работы светодиода 2, мс
.equ LED2_OFF = 1900 ;время паузы светодиода 2, мс
;----------------------------------------------------------------------------
;Вычисляемые константы:
.equ MAXWORD = 0xFFFF
.equ MAXBYTE = 0xFF
.equ T1PRE = 8;перскалер таймера 1
.equ T1VAL = (((FCLK/T1PRE/1000*TSYS)/100)+5)/10
.if T1VAL > MAXWORD ;проверка значения константы T1VAL
.error "out of range constant"
.endif
;----------------------------------------------------------------------------
;Перевод времени в миллисекундах в системные тики:
#define ms2sys(X) X*1000/TSYS
;----------------------------------------------------------------------------
;Определение портов:
#define PORT_LED1 PORTB,PB0;светодиод 1 подключен к выводу PB0
#define PORT_LED2 PORTB,PB1;светодиод 2 подключен к выводу PB1
;----------------------------------------------------------------------------
;Глобальные регистровые переменные:
.def temp = r16 ;временный регистр temp
.def tempA = r17 ;временный регистр tempA
.def tempB = r18 ;временный регистр tempB
;----------------------------------------------------------------------------
.DSEG ;сегмент данных
TickCnt: .byte 1;переменная счетчика "тиков" (системное время)
;Для однобайтного счетчика "тиков" максимальный интервал (интервал)
;переполнения равен 256*TSYS. Например, если TSYS = 10 мс, то максимальный
;интервал равен 2.56 секунды. Если надо больше, нужно или увеличить
;квант времени TSYS, или сделать счетчик "тиков" большей разрядности.
;----------------------------------------------------------------------------
.CSEG ;сегмент кода
;----------------------------------------------------------------------------
;Векторы прерываний:
.org 0 ;по адресу 0 вектор сброса
rjmp Init ;переход на основную программу
.org OC1addr ;по адресу OC1addr вектор прерывания по
rjmp Timer ;совпадению таймера 1
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Программа начинает выполняться отсюда:
;Здесь пишем всякую инициализацию, которая должна
;выполняться один раз при старте программы.
Init: ldi temp,RAMEND ;константа RAMEND определена в 2313def.inc
out SPL,temp ;инициализируем стек
;Настройка портов:
ldi temp,0xFF ;загружаем в temp FF
out DDRB,temp ;настраиваем порт B на вывод
out DDRD,temp ;настраиваем порт D на вывод
;Настройка оборудования (таймера 1):
ldi temp,0x00
out TCCR1A,temp ;OC и PWM запрещены
ldi temp,(1<<CTC1) | (1<<CS11)
out TCCR1B,temp ;очистка при совпадении, CK/8
ldi temp,high(T1VAL-1);загрузкарегистра сравнения
out OCR1AH,temp
ldi temp,low(T1VAL-1)
out OCR1AL,temp
ldi temp,(1<<OCIE1A)
out TIFR,temp ;очистка флага прерывания
out TIMSK,temp ;разрешение прерывания по совпадению
sei ;общее разрешение прерываний:
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Инициализация процессов:
rcall Process1_Init ;инициализация процесса 1
rcall Process2_Init ;инициализация процесса 2
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Основной цикл программы (выполнение процессов):
Main: rcall Process1_Execute;выполнение процесса 1
rcall Process2_Execute;выполнение процесса 2
rjmp Main ;цикл
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Обработчик прерывания по совпадению регистра сравнения
;и содержимого таймера 1:
Timer: push temp ;сохранение регистра temp
in temp,SREG ;temp <- SREG
push temp ;сохранение SREG
lds temp,TickCnt ;загружаем в temp значение TickCnt
inc temp ;наращиваем значение
sts TickCnt,temp ;сохраняем новое значение
Ovf: pop temp ;восстановление SREG
out SREG,temp ;SREG <- temp
pop temp ;восстановление регистра temp
reti ;выход из обработчика и разрешение прерываний
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Процесс 1:
;Объявляем необходимые переменные:
.DSEG ;сегмент данных
Led1Tm: .byte 1;время изменения состояния светодиода 1 (1 байт)
;Дальше будет код процесса:
.CSEG
;----------------------------------------------------------------------------
;Инициализация процесса:
;Инициируем включение светодиода без задержки.
Process1_Init:
lds temp,TickCnt
subi temp,ms2sys(LED1_OFF)
sts Led1Tm,temp
ret
;----------------------------------------------------------------------------
;Выполнение процесса:
Process1_Execute:
lds tempA,TickCnt ;чтение системного времени
lds tempB,Led1Tm ;чтение времени предыдущего переключения
mov temp,tempA
sub temp,tempB ;вычисляем, сколько времени прошло
sbis PORT_LED1 ;проверяем состояние порта светодиода
rjmp On1 ;если был выключен, переход на включение
;Выключение светодиода 1:
Off1: subi temp,ms2sys(LED1_ON);прошло время LED1_ON?
brcs Pret1 ;если нет, ничего не делаем, выход
cbi PORT_LED1 ;если да, выключаем светодиод
sts Led1Tm,tempA ;и сохраняем время выключения
rjmp Pret1 ;выход
;Включение светодиода 1:
On1: subi temp,ms2sys(LED1_OFF);прошло время LED1_OFF?
brcs Pret1 ;если нет, ничего не делаем, выход
sbi PORT_LED1 ;если да, включаем светодиод
sts Led1Tm,tempA ;и сохраняем время включения
Pret1: ret ;выход
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;Процесс 2:
;Объявляем необходимые переменные:
.DSEG ;сегмент данных
Led2Tm: .byte 1;время изменения состояния светодиода 2 (1 байт)
;Дальше будет код процесса:
.CSEG
;----------------------------------------------------------------------------
;Инициализация процесса:
;Инициируем включение светодиода без задержки.
Process2_Init:
lds temp,TickCnt
subi temp,ms2sys(LED2_OFF)
sts Led2Tm,temp
ret
;----------------------------------------------------------------------------
;Выполнение процесса:
Process2_Execute:
lds tempA,TickCnt ;чтение системного времени
lds tempB,Led2Tm ;чтение времени предыдущего переключения
mov temp,tempA
sub temp,tempB ;вычисляем, сколько времени прошло
sbis PORT_LED2 ;проверяем состояние порта светодиода
rjmp On2 ;если был выключен, переход на включение
;Выключение светодиода 2:
Off2: subi temp,ms2sys(LED2_ON);прошло время LED2_ON?
brcs Pret2 ;если нет, ничего не делаем, выход
cbi PORT_LED2 ;если да, выключаем светодиод
sts Led2Tm,tempA ;и сохраняем время выключения
rjmp Pret2 ;выход
;Включение светодиода 2:
On2: subi temp,ms2sys(LED2_OFF);прошло время LED2_OFF?
brcs Pret2 ;если нет, ничего не делаем, выход
sbi PORT_LED2 ;если да, включаем светодиод
sts Led2Tm,tempA ;и сохраняем время включения
Pret2: ret ;выход
;----------------------------------------------------------------------------