;******** 10-разрядный ЦАП ***********************************************
;*
;* Три ШИМ-счетчика в подпрограмме обработки прерывания каждые 5 мкс
;* увеличиваются на соответствующее выходное значение. Разряд переполнения
;* выдается как ШИМ-сигнал.
;*
;* Тактовая частота микроконтроллеров AVR: 8 МГц.
;*  
;***************************************************************************
.nolist
.include "1200def.inc"
.list

.def	ac1L	= r9	; Младший байт текущего состояния счетчика канала 1
.def	ac2L	= r10	; Младший байт текущего состояния счетчика канала 2
.def	ac3L	= r11	; Младший байт текущего состояния счетчика канала 3
.def	rl1L	= r12	; Младший байт значения перезагрузки канала 1
.def	rl2L	= r13	; Младший байт значения перезагрузки канала 2
.def	rl3L	= r14	; Младший байт значения перезагрузки канала 3
.def	STAT	= r15	; Буфер для хранения флагов состояния главной программы
.def	WorkI	= r16	; Рабочий регистр для прерывания от таймера
.def	WorkH	= r17	; Рабочий регистр для главной программы
.def	ac1H	= r18	; Старший байт текущего состояния счетчика канала 1
.def	ac2H	= r19	; Старший байт текущего состояния счетчика канала 2
.def	ac3H	= r20	; Старший байт текущего состояния счетчика канала 3
.def	rl1H	= r21	; Старший байт значения перезагрузки канала 1
.def	rl2H	= r22	; Старший байт значения перезагрузки канала 2
.def	rl3H	= r23	; Старший байт значения перезагрузки канала 3
.def    tim1	= r24	; Счетчик внутреннего цикла
.def    tim2	= r25	; Счетчик внешнего цикла


.equ	OFL	= 2	; Переполнение при сложении в разряде 2
.equ	PWM1	= PD2	; Разряд 2 порта D – выход для канала 1
.equ	PWM2	= PD3	; Разряд 3 порта D – выход для канала 2
.equ	PWM3	= PD4	; Разряд 4 порта D – выход для канала 3
.equ	Time	= 40	; Число тактовых импульсов до следующего прерывания
	; 40 импульсов при f = 8 МГц -> 5 мкс

Reset:
rjmp Initial	; Переход к части инициализации
reti	; Внешнее прерывание 0 (не используется)

Timer0_Int:		; Прерывание от таймера 0 (для AT90S1200)
in   STAT,SREG	; Сохраняем флаги главной программы
in   WorkI,TCNT0	; Извлекаем текущее состояние счетчика
subi WorkI,Time-3	; Устанавливаем таймер: длительность
out  TCNT0,WorkI	;   интервала минус время выполнения трех команд
dec  tim2	; Декрементируем счетчик внутреннего цикла
in   WorkI,PortD	; Извлекаем текущие ШИМ-состояния
add  ac1L,rl1L	; Прибавляем младший байт значения перезагрузки
adc  ac1H,rl1H	; Прибавляем старший байт значения перезагрузки
bst  ac1H,OFL	; Переполнение (разряд 2 ) – в флаг T
bld  WorkI,PWM1	; Переполнение на ШИМ-выходе канала 1
andi ac1H,$03	; Обнуляем разряды 2..7
add  ac2L,rl2L	; Прибавляем младший байт значения перезагрузки
adc  ac2H,rl2H	; Прибавляем старший байт значения перезагрузки
bst  ac2H,OFL	; Переполнение (разряд 2 ) – в флаг T
bld  WorkI,PWM2	; Переполнение на ШИМ-выходе канала 2
andi ac2H,$03	; Обнуляем разряды 2..7
add  ac3L,rl3L	; Прибавляем младший байт значения перезагрузки
adc  ac3H,rl3H	; Прибавляем старший байт значения перезагрузки
bst  ac3H,OFL	; Переполнение (разряд 2 ) – в флаг T
bld  WorkI,PWM3	; Переполнение на ШИМ-выходе канала 3
andi ac3H,$03	; Обнуляем разряды 2..7
out  PortD,WorkI	; Выводим новые ШИМ-состояния в порт D
out  SREG,STAT	; Восстанавливаем старые флаги
reti	; Выход из прерывания от таймера 0

Wait20ms:	; Каждые 5 мкс при прерывании от T/C0
ldi   tim1,16	; Загружаем счетчик внешнего цикла
L1:
ldi   tim2,250	; Загружаем счетчик внутреннего цикла
L2:
tst   tim2	; Счетчик = 0 ?
brne  L2	; Если нет, то ожидаем дальше
dec   tim1
brne  L1
ret

Taste:	; Ожидаем нажатия кнопки 
sbic PinD,0	; Пропускаем следующую команду, если нажата кнопка 0
rjmp Taste	; Ожидаем дальше
rcall wait20ms	; Пауза для учета дребезга контакта
sbic PinD,0	; Пропускаем следующую команду, если кнопка все еще нажата
rjmp Taste	; Ожидаем дальше
T1:
sbis PinD,0	; Пропускаем следующую команду, если кнопка 0 отпущена
rjmp T1	; Ожидаем дальше
rcall wait20ms	; Пауза для учета дребезга контакта
sbis PinD,0	; Пропускаем следующую команду, если кнопка все еще отпущена
rjmp T1	; Ожидаем дальше
sbis DDRB,7	; Пропускаем следующую команду, если светодиод включен
rjmp T2
cbi  DDRB,7	; Включаем светодиод
rjmp T3
T2:
sbi  DDRB,7	; Выключаем светодиод
T3:
ret

Initial:	; Инициализация регистров ввода/вывода
ldi  WorkH,$00	; Устанавливаем все разряды в лог. 0
out  PortB,WorkH	; Вывод в порт B
out  DDRB,WorkH	; Входы – в высокоомное состояние
out  PortD,WorkH	; Вывод в порт D
ldi  WorkH,$1C	; Загружаем направление передачи данных
out  DDRD,WorkH	; Разряды 2..4 - выходы, остальные - входы
clr  ac1H
clr  ac1L
clr  ac2H
clr  ac2L
clr  ac3H
clr  ac3L	; Сбрасываем текущие состояния счетчиков
clr  rl1L
clr  rl1H
clr  rl2L
clr  rl2H
clr  rl3L
clr  rl3H	; Сбрасываем значения перезагрузки
dec  rl1L	; Декрементируем на 1 ($FF)
inc  rl2L	; Инкрементируем на 1 ($01)
inc  rl3L	; Инкрементируем на 1 ($01)
ldi  WorkH,-Time	; Загружаем интервал (отрицательный, т.к. счетчик - суммирующий)
out  TCNT0,WorkH	; Инициализируем таймер, T = 5 мкс (8 МГц)
ldi  WorkH,1
out  TCCR0,WorkH	; Запускаем таймер, такт = такт системной синхронизации 
ldi  WorkH,2
out  TIMSK,WorkH	; Разрешаем прерывание от таймера
sei	; Общее разрешение прерываний (разряд I)

Haupt:
rcall Taste	; По нажатию кнопки выводим след. значение
cli	; Запрет прерываний
subi  rl1H,-$01	; Значение перезагрузки 1 = Значение перезагрузки 1 + $0100
andi  rl1H,$03	; Обнуляем разряды 2..7
subi  rl2H,$01	; Значение перезагрузки 2 = Значение перезагрузки 2 – $0100
andi  rl2H,$03	; Обнуляем разряды 2..7
subi  rl3H,-$02	; Значение перезагрузки 3 = Значение перезагрузки 3 + $0200
andi  rl3H,$03	; Обнуляем разряды 2..7
sei	; Опять разрешаем прерывание
rjmp  Haupt	; Продолжаем цикл
