;**** Расширение портов AVR с помощью интерфейса SPI *********************
;*
;* Ширина портов микроконтроллера AT90S4414 увеличивается на 
;* 16 выходов и 8 входов.
;* Тактовая частота микроконтроллера AVR: 4 МГц (Стандарт STK200).
;*  
;*************************************************************************
.nolist
.include "4414def.inc"
.list

.def  Last = r15	; Промежуточный буфер
.def  tmp1 = r16	; Рабочий регистр 1
.def  Out1 = r17	; Выходное значение для выхода 1
.def  Out2 = r18	; Выходное значение для выхода 2
.def  In1  = r19	; Входное значение для входа 1

.equ  nss   = PB4	; /SS = PB4
.equ  Latch = PB3	; Импульс фиксации = PB3
.equ  okay  = PB1	; LED1 для порта B в случае корректной передачи
.equ  LEDK  = PB0	; LED0 для порта B в случае нажатия кнопки 0
.equ  time  = 117	; T = время * 1024 / 4 МГц = 30 мс

Reset:
rjmp  Initial

WaitTime:	; Задержка на 30 мс
ldi   tmp1,-time	; Загружаем интервал таймера
out   TCNT0,tmp1	; Инициализируем счетчик
ldi   tmp1,1<<TOV0	; Загружаем битовую позицию флага TOV0
out   TIFR,tmp1	; Сбрасываем флаг TOV0
WT1:
in    tmp1,TIFR	; Извлекаем содержимое регистра TIFR
sbrs  tmp1,TOV0	; Пропускаем следующую команду, если возникает переполнение T/C0
rjmp  WT1	; Дальше ожидаем переполнения
ret

WaitKey:	; Ожидаем нажатия кнопки 0
sbic  PinD,0	; Пропускаем следующую команду, если PD0=0 (нажата кнопка)
rjmp  WaitKey	; Ожидаем дальше
rcall WaitTime	; Задержка на 30 мс (учет дребезга контактов)
sbic  PinD,0	; Пропускаем следующую команду, если кнопка еще нажата
rjmp  WaitKey	; Ожидаем дальше
WK2:
sbis  PinD,0	; Пропускаем следующую команду, если PD0=1 (кнопка отпущена)
rjmp  WK2	; Ожидаем дальше
rcall WaitTime	; Задержка на 30 мс (учет дребезга контактов)
sbis  PinD,0	; Пропускаем следующую команду, если кнопка еще отпущена
rjmp  WK2	; Ожидаем дальше
ret

ToggleLED:	; Переключение светодиода LED0
sbis  DDRB,LEDK	; Пропускаем следующую команду, если светодиод, соотв. кнопке, включен
rjmp  TL1
cbi   DDRB,LEDK	; Отключаем светодиод
ret
TL1:
sbi   DDRB,LEDK	; Включаем светодиод
ret  

SPITransf:	; 16 бит через SPI, CPHA=0,CPOL=0
cbi   PortB,nss	; /SS = активный (лог. 0)
cbi   PortB,Latch	; Импульс фиксации (лог. 0)
sbi   PortB,Latch	;   выводим
out   SPDR,Out2	; Выводим Out2
spi1:
sbis  SPSR,SPIF	; Пропускаем следующую команду, если передача завершена
rjmp  spi1
in    In1,SPDR	; Считываем вход 1
out   SPDR,Out1	; Выдаем Out1
spi2:
sbis  SPSR,SPIF	; Пропускаем следующую команду, если передача завершена
rjmp  spi2
sbi   PortB,nss	; /SS = неактивный (лог. 1)
ret

Initial: 
ldi   tmp1,Low(RAMEND)
out   SPL,tmp1
ldi   tmp1,High(RAMEND)
out   SPH,tmp1	; Инициализация указателя стека
ldi   tmp1,$7C	; SCK=0,MISO=нагруз. сопр.,MOSI=1,/SS=1,
out   PortB,tmp1	;   Фикс.=1,Бит2=нагруз. сопр.,LED1,LED0=0
ldi   tmp1,$B8	; SCK,MOSI,/SS,Фиксация,
out   DDRB,tmp1	;   LED1,LED0 = выход,MISO, разряд 2 = вход
ldi   tmp1,$00	; Обнуляем все разряды
out   PortD,tmp1	; Все разряды = HiZ (кнопки)
out   DDRD,tmp1	; Направление передачи для порта D = вход
ldi   tmp1,$50	; SPIE=0,SPE=1,DORD=0,MSTR=1, 
out   SPCR,tmp1	;   CPHA=0,CPOL=0, f(SCK)=f(Сист.) / 4
ldi   tmp1,5	; Настройка делителя частоты
out   TCCR0,tmp1	; Запускаем таймер, коэф. деления = 1024
clr   Out2	; Инициализируем выходное значение

Haupt:
rcall WaitKey	; Ожидаем нажатия кнопки 0
rcall ToggleLED	; переключаем светодиод 0
inc   Out2	; Инкрементируем выходное значение
mov   Out1,Out2	; Копируем в Out1
com   Out1	; Дополнение до единицы
rcall SPITransf	; Передача через SPI: Out1, Out2 и In1
sbi   DDRB,okay	; В случае корректной передачи включаем светодиод 1
cpse  In1,Last	; Сравниваем входной байт с байтом, полученным ранее на выходе
cbi   DDRB,okay	; Если неравно, включаем светодиод 1
mov   Last,Out2	; Сохраняем текущее значение Out2
rjmp  Haupt

