;**** Программная реализация UART для микроконтроллера AT90S1200 *********
;*
;* Входной сигнал RxD принимается со скоростью 9600 бод.
;* Выходной сигнал TxD передается с такой же скоростью.
;* В главной программе в качестве примера принятый байт передается обратно.
;* Тактовая частота микроконтроллера AVR: 12 МГц
;* 
;*************************************************************************
.nolist
.include "1200def.inc"
.list
;***** Определения регистров:

.def	Save	= r12		; Буфер для временного хранения содержимого SREG
.def	RTmp	= r13		; Временный буфер приема
.def	RBuf	= r14		; Буфер приема
.def	TBuf	= r15		; Буфер передачи
.def	Stat	= r16		; Состояние UART (T_BUSY, T_Stop, T_Bit, T_Idle,  
				; R_RDY, R_Bit, R_Start, R_Idle)
.def	tmp1	= r17		; Общий рабочий регистр 1
.def	tmp2	= r18		; Общий рабочий регистр 2
.def	TBCnt	= r19		; Счетчик битов при передаче
.def	TICnt	= r20		; Счетчик интервалов опроса при передаче
.def	RBCnt	= r21		; Счетчик битов при приеме
.def	RICnt	= r22		; Счетчик интервалов опроса при приеме
.def	RSum	= r23		; Сумма приема

;***** Описания:

.equ	Time	= 250		; Количество тактовых импульсов до следующего прерывания

.equ	RxD	= PD0		; Разряд 0 порта D = RxD
.equ	TxD	= PD1		; Разряд 1 порта D = TxD

.equ	RIdle	= 0		; Разряд 0 = ждущий режим при приеме
.equ	RStrt	= 1		; Разряд 1 = начало приема
.equ	RBit	= 2		; Разряд 2 = опрос битов при приеме
.equ	RRdy	= 3		; Разряд 3 = в буфер приема получен новый байт
.equ	TIdle	= 4		; Разряд 4 = ждущий режим при передаче
.equ	TBit	= 5		; Разряд 5 = начало передачи
.equ	TStop	= 6		; Разряд 6 = опрос битов при передаче
.equ	TBusy	= 7		; Разряд 7 = в данный момент выполняется передача

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

;***************************************************************************
;* Прерывание от таймера 0
;* Эта подпрограмма вызывается каждые 20,833 мкs (250 тактовых импульса 
;* таймера при частоте кварца 12 МГц).
;* В начале обработки прерывания сохраняются флаги главной программы,
;* а таймер после его переполнения инициализируется по-новому.
;* Далее реализуется UART.
;* В конце обработки прерывания восстанавливаются флаги главной программы,
;* и с помощью команды reti опять разрешаются прерывания.
;***************************************************************************

Timer0_Int:	; Прерывание от таймера 0
in   Save,SREG	; Сохраняем флаги главной программы
in   tmp1,TCNT0	; Текущее содержимое таймера - в tmp1
subi tmp1,Time-3	; Интервал минус время выполнения
out  TCNT0,tmp1	;   трех команд инициализации
UARTIn:		; USTartIn: 20 тактов, UBitIn: 19 тактов
sbrc Stat,RBit;	; Следующий переход выполняется, если
rjmp UBitIn	;   UART – в режиме опроса битов
sbrc Stat,RStrt	; Следующий переход выполняется, если
rjmp UStrtIn	;   UART – в режиме начала передачи
UIdleIn:	; UART – в ждущем режиме при приеме
sbic PinD,RxD	; Пропускаем следующую команду, если RxD=0 (старт-бит)
rjmp UARTOut	; Не старт-бит, UART – еще в ждущем режиме
cbr  Stat,$07	; Сбрасываем флаги состояния UART 0..2
sbr  Stat,1<<RStrt	; Входное состояние UART = R_Strt
ldi  RICnt,5	; Инициализируем счетчик интервалов опроса
clr  RSum	; Обнуляем сумму приема
rjmp UARTOut	
UStrtIn:
inc  RSum	; Увеличиваем сумму приема на 1
sbic PinD,RxD	; Пропускаем следующую команду, если RxD=0
subi RSum,$02	; Уменьшаем сумму приема на 2
dec  RICnt	; Уменьшаем счетчик опросов на 1
brne UARTOut	; Переход, если старт-бит не завершен
sbrs RSum,7	; Если разряд 7 = 1, то неверный старт-бит
rjmp UIS3	; Переход, если корректный старт-бит
cbr  Stat,$07	; Сбрасываем флаги состояния UART 0..2
sbr  Stat,1<<RIdle	; Входное состояние UART = R_Idle
rjmp UARTOut	
UIS3:
ldi  RICnt,5	; 5 интервалов опроса на один бит
ldi  RBCnt,8	; Считываем 8 битов
clr  RSum	; Обнуляем сумму приема
cbr  Stat,$07	; Сбрасываем биты состояния UART 0..2
sbr  Stat,1<<RBit	; Входное состояние UART = R_Bit
rjmp UARTOut	
UBitIn: 			
inc  RSum	; Увеличиваем сумму приема на 1
sbic PinD,RxD	; Пропускаем следующую команду, если RxD=0
subi RSum,$02	; Уменьшаем сумму приема на 2
dec  RICnt	; Уменьшаем счетчик опросов на 1
brne UARTOut	; Переход, если бит еще не завершен
ldi  RICnt,5	; Инициализируем счетчик интервалов опроса
lsl  RSum	; Разряд 7 – в флаг переноса
ror  RTmp	; Флаг переноса вправо во временный буфер
clr  RSum	; Обнуляем сумму приема
dec  RBCnt	; Уменьшаем счетчик битов на 1
brne UARTOut	; Переход, если еще не все 8 битов
mov  RBuf,RTmp	; Копируем считанный байт в буфер приема
cbr  Stat,$07	; Сбрасываем флаги состояния UART 0..2
sbr  Stat,1<<RIdle | 1<<RRdy	; Входное состояние UART = R_Idle
	; R_RDY=1: байт считан
UARTOut:	; 
sbrs Stat,TBusy	; Следующий переход выполняется, если
rjmp IntEnde	;   UART – не в режиме передачи
sbrc Stat,TBit;	; Следующий переход выполняется, если
rjmp UBitOut	;   UART в режиме опроса бита
sbrc Stat,TStop	; Следующий переход выполняется, если
rjmp UStopOut	;   UART в режиме прекращения передачи
UIdleOut:	; UART – в ждущем режиме при передаче
cbi  PortD,TxD	; Выдаем старт-бит (TxD=0)
ldi  TICnt,5	; 5 опросов на бит
ldi  TBCnt,9	; Выдаем старт-бит и 8 битов данных
cbr  Stat,$70	; Сбрасываем флаги состояния UART 4..6
sbr  Stat,1<<TBit	; Выходное состояние UART = T_Bit
rjmp IntEnde
UBitOut:
dec  TICnt	; Уменьшаем счетчик опросов на 1
brne IntEnde	; Переход, если вывод бита не завершен
ldi  TICnt,5	; Инициализируем счетчик опросов
dec  TBCnt	; Уменьшаем счетчик битов на 1
breq StopBit	; Переход, если переданы все биты
sbrc TBuf,0	; Пропускаем следующую команду, если младший разряд = 0
rjmp UO1
cbi  PortD,TxD	; Выход TxD=0
rjmp UO2
UO1:
sbi  PortD,TxD	; Выход TxD=1
UO2:	
lsr  TBuf	; Сдвигаем временный буфер на 1 вправо
rjmp IntEnde	; Переход, если не все биты переданы
StopBit:
sbi  PortD,TxD	; Передаем стоп-бит (TxD=1)
cbr  Stat,$70	; Сбрасываем флаги состояния UART 4..6
sbr  Stat,1<<TStop	; Выходное состояние UART = T_Stop
rjmp IntEnde
UStopOut:
dec  TICnt	; Уменьшаем счетчик опросов на 1
brne IntEnde	; Переход, если стоп-бит не завершен
cbr  Stat,$F0	; Сбрасываем флаги состояния UART 4..7
sbr  Stat,1<<TIdle	; Выходное состояние UART = T_Idle
	;   Флаг T_Busy = 0
IntEnde:
out  SReg,Save	; Устанавливаем старые флаги
reti	; Выход из подпрогр. обработки прерывания

Initial:
ldi  tmp1,2
out  PortD,tmp1	; PD1= 1
out  DDRD,tmp1	; Разряд 1 = выход, остальные - входы
ldi  Stat,$11	; Входное и выходное состояние UART = Idle
ldi  tmp1,-20	; Устанавливаем фиктивный интервал
out  TCNT0,tmp1	; Инициализируем таймер
ldi  tmp1,1
out  TCCR0,tmp1	; Запускаем таймер, делитель = 1
ldi  tmp1,2
out  TIMSK,tmp1	; Разрешаем прерывание от таймера
sei	; Общее разрешение прерываний

Haupt:
sbrs Stat,RRdy	; Пропускаем следующую команду, если байт принят
rjmp Haupt
H1:
sbrc Stat,TBusy	; Пропускаем следующую команду, если буфер передачи пуст
rjmp H1	; Ожидаем окончания передачи
mov  TBuf,RBuf	; Копируем байт в буфер передачи
sbr  Stat,1<<TBusy	; Флаг указывает на передачу байта
cbr  Stat,1<<RRdy	; Сбрасывает флаг приема
rjmp Haupt	; Ожидаем следующего байта
