;**** Управление ЖК-модулем с помощью микроконтроллеров AVR ***************
;*
;* Управление двухстрочным ЖК-табло(16 знаков в строке, 
;* тип контроллера - HD44780) через ЖК-интерфейс модуля STK200.
;* Тактовая частота AVR: 4 МГц (стандарт STK200).
;* Подпрограммы:
;* InitLCD: Инициализация ЖК-интерфейса для 8-разрядных символов
;* SendCom: Передает команду на ЖК-модуль
;* SendDat: Передает символ на ЖК-модуль
;* OutText: Передает текстовую строку на ЖК-модуль  
;* DefChar: Определяет пользовательский символ
;*  
;***************************************************************************
.nolist
.include "8515def.inc"
.list
;***** Регистровые переменные
.def  tmp1 = r16	; Рабочий регистр 1
.def  tmp2 = r17	; Рабочий регистр 2
.def  prm1 = r18	; Передаваемый параметр
.def  tim1 = r19	; Счетчик цикла 1
.def  tim2 = r20	; Счетчик цикла 2
.def  Cnt  = r21	; Вспомогательный счетчик

.equ  RS   = 6	; Register Select = разряд 6 порта C (A14)
.equ  Ena  = 7	; Enable Impuls = разряд 7 порта C (A15)
.equ  RW   = 6	; R/W = разряд 6 порта D (/WR)
.equ  RD   = 7	; /RD = разряд 7 порта D
.equ  BF   = 7	; Флаг занятости = разряд 7 порта A

.cseg

rjmp  Initial	; После сброса – к главной программе

InitLCD:
rcall wait5ms
rcall wait5ms
rcall wait5ms	; Задержка на 15 мс после подачи питания
ldi   prm1,$30	; $30: установка функции, 8 разрядов
rcall SendCom	; Отправка команды на ЖК-модуль
rcall wait5ms	; Задержка на 5 мс
ldi   prm1,$30	; $30: установка функции, 8 разрядов
rcall SendCom	; Повторяем команду
rcall wait5ms	; Задержка на 5 мс
ldi   prm1,$30	; $30: установка функции, 8 разрядов
rcall SendCom	; Повторяем команду
rcall wait5ms	; Задержка на 5 мс
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
ldi   prm1,$38	; $38: функция = 8 разрядов, 2 строки, 5x8
rcall SendCom	; Отправляем команду
rcall wait150us	; Задержка на 150 мкс
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
ldi   prm1,$08	; $08: отключение табло, курсора, мерцания
rcall SendCom	; Передаем команду
rcall wait150us	; Задержка на 150 мкс
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
ldi   prm1,$01	; $01: очистка табло
rcall SendCom	; Передаем команду
rcall wait5ms	; Задержка на 5 мс
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
ldi   prm1,$06	; $06: режим инкремента адреса, без сдвига
rcall SendCom	; Передаем команду
rcall wait150us	; Задержка на 150 мкс
ret

SendCom:	; Передача в ЖК-модуль команды
cbi   PortC,RS	; Register Select = команда
rcall Ausgabe
ret

SendDat:	; Передача в ЖК-модуль байта данных
sbi   PortC,RS	; Register Select = байт данных
rcall Ausgabe
ret

Ausgabe:	; Длина данных = 8 бит
out   PortA,prm1	; Выдача команды/данных на порт A
sbi   PortC,Ena	; Подаем импульс разрешения
nop
nop	; Длительность импульса – минимум 450 нс
cbi   PortC,Ena	; Снимаем импульс разрешения
ret

OutText:	; Вывод символа <cnt> на табло
lsl   ZL
rol   ZH	; Указатель Z – на 1 позицию влево
OT1:
lpm	; Загружаем код символа в r0
mov   prm1,r0	; Копируем код символа в prm1
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
rcall SendDat	; Передаем символ, автоинкремент адреса
adiw  ZL,1	; Инкрементируем указатель Z
dec   cnt	; Счетчик – 1
brne  OT1	; Выводим все символы на ЖК-табло
ret

DefChar:	; Определение пользовательских символов
lsl   prm1	; Умножаем prm1 на 2
lsl   prm1	; Умножаем prm1 на 4
ldi   ZH,high(CharTab)	; Адрес старшего байта шаблона символа
lsl   prm1	; prm1 x 8
mov   ZL,prm1	; Z -> адрес символа
rol   ZH	; Указатель Z – на 1 позицию влево
sbr   prm1,1<<6	; Разряд 6 = 1 -> адрес в памяти CG
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
rcall SendCom	; Устанавливаем счетчик адреса памяти CG
ldi   cnt,8
DC1:
lpm	; Загружаем в r0 шаблон символа
mov   prm1,r0	; Копируем шаблон символа в prm1
rcall WaitBusy	; Ожидаем готовности ЖК-модуля к приему
rcall SendDat	; Передаем шаблон, автоинкремент адреса
adiw  ZL,1	; Инкремент указателя Z
dec   cnt	; Счетчик – 1
brne  DC1	; Копируем все 8 символов в память CG
ret

WaitBusy:	; Ожидание готовности ЖК-модуля к приему
clr   tmp1
out   DDRA,tmp1	; Порт A = вход
cbi   PortC,RS	; Register Select = команда
sbi   PortD,RW	; Направление = чтение из ЖК-модуля
WB1:
sbi   PortC,Ena	; Подаем импульс разрешения
nop
nop	; После макс. 320 нс стабильности данных
in    tmp1,PinA	; считываем BF и значение счетчика адреса
cbi   PortC,Ena	; Снимаем импульс разрешения
nop
sbrc  tmp1,BF	; Пропускаем следующую команду, если ЖК-модуль готов (BF=0)
rjmp  WB1
cbi   PortD,RW	; Направление записи = в ЖК-модуль
ser   tmp1
out   DDRA,tmp1	; Порт A = выход
ret

Wait50us:	; Такт системной синхронизации = 4 МГц
ldi   tim1,65	; Загрузка счетчика
Wait51:
dec   tim1
brne  Wait51
ret

Wait150us:	; Такт системной синхронизации = 4 МГц
rcall wait50us
rcall wait50us
rcall wait50us
ret

Wait5ms:	; Такт системной синхронизации = 4 МГц
ldi   tim2,100	; Загрузка счетчика
Wait501:
rcall wait50us
dec   tim2
brne  Wait501
ret

Wait38ms:	; Такт системной синхронизации = 4 МГц
ldi   tim2,0	; Загрузка счетчика
Wait381:
rcall wait150us
dec   tim2
brne  Wait381
ret

Wait5s:	; Такт системной синхронизации = 4 МГц
ldi   cnt,130	; Загрузка счетчика
Wait5s1:
rcall wait38ms
dec   cnt
brne  Wait5s1
ret

Initial:
ldi   tmp1,High(RamEnd)
out   sph,tmp1
ldi   tmp1,Low(RamEnd)
out   spl,tmp1	; Инициализируем стек
ldi   tmp1,$3F	; Разряды 7..6 = 0, остальные = 1
out   PortD,tmp1	; /RD,/WR = 0, остальные разряды порта D=1
out   PortC,tmp1	; Ena, RS = 0, остальные разряды порта С=1
ldi   tmp1,$C0	; Разряд 7 = 1, разряд 6 = 1
out   DDRD,tmp1	; Разряды 7..6 - выходы, остальные - входы
out   DDRC,tmp1	; Разряды 7..6 - выходы, остальные - входы
ser   tmp1	; Устанавливаем tmp1
out   DDRA,tmp1	; Порт A - выход
rcall InitLCD	; Инициализируем ЖК-модуль
clr   tmp2	; Счетчик символов
Ini1:
mov   prm1,tmp2	; ... переносим как код знака
rcall DefChar	; Определяем символ для этого кода
inc   tmp2
cpi   tmp2,8	; = 8?
brne  Ini1	; Переход, если нет

Haupt:
ldi   prm1,$0C	; Включаем табло, откл. курсор и мерцание
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Передаем команду
ldi   prm1,$01	; Очищаем табло, курсор – в начало
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Передаем команду
ldi   ZH,high(Text3)
ldi   ZL,low(Text3)	; Z указывает на начало текста
ldi   cnt,16	; Текст состоит из 16 символов
rcall OutText	; Выводим Text3

TestBlinken:
ldi   prm1,64	; Адрес = начало второй строки
sbr   prm1,1<<7	; Устанавливаем разряд 7 -> память DD
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Устанавливаем указатель адреса в DD-RAM
ldi   ZH,high(Text4)
ldi   ZL,low(Text4)	; Z указывает на начало текста
ldi   cnt,16	; Текст состоит из 16 символов
rcall OutText	; Выводим Text4
ldi   prm1,64+5	; Адрес 6-го символа во второй строке
sbr   prm1,1<<7	; Устанавливаем разряд 7 -> память DD
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Устанавливаем указатель адреса в DD-RAM
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
ldi   prm1,$0D	; Включаем табло, мерцание, откл. курсор
rcall SendCom	; Передаем команду
rcall Wait5s	; Задержка на 5 секунд
ldi   prm1,$14	; Смещаем курсор вправо
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Передаем команду
rcall Wait5s	; Задержка на 5 секунд
ldi   prm1,$14	; Смещаем курсор вправо
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Передаем команду
rcall Wait5s	; Задержка на 5 секунд

TestCursor:
ldi   prm1,$0E	; Табло и курсор – вкл., мерцание – откл.
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Передаем команду
ldi   prm1,64	; Адрес = начало второй строки
sbr   prm1,1<<7	; Устанавливаем разряд 7 -> память DD
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Устанавливаем указатель адреса в DD-RAM
ldi   ZH,high(Text5)
ldi   ZL,low(Text5)	; Z указывает на начало текста
ldi   cnt,16	; Текст состоит из 16 символов
rcall OutText	; Выводим Text5
ldi   prm1,64+1	; Адрес 2-го символа во второй строке
sbr   prm1,1<<7	; Устанавливаем разряд 7 -> память DD
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Устанавливаем указатель адреса в DD-RAM
rcall Wait5s	; Задержка на 5 секунд

Sonderzeichen:
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
ldi   prm1,$01	; Очищаем табло
rcall SendCom	; Передаем команду
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
ldi   prm1,$0C	; Табло – вкл., курсор и мерцание – откл.
rcall SendCom	; Передаем команду
ldi   ZH,high(Text1)
ldi   ZL,low(Text1)	; Z указывает на начало текста
ldi   cnt,16	; Текст состоит из 16 символов
rcall OutText	; Выводим Text1
ldi   prm1,64	; Адрес = начало второй строки
sbr   prm1,1<<7	; Устанавливаем разряд 7 -> память DD
rcall WaitBusy	; Ожидаем готовность ЖК-модуля к приему
rcall SendCom	; Устанавливаем указатель адреса в DD-RAM
ldi   ZH,high(Text2)
ldi   ZL,low(Text2)	; Z указывает на начало текста
ldi   cnt,16	; Текст состоит из 16 символов
rcall OutText	; Выводим Text2 (8 служебных знаков)
rcall Wait5s	; Задержка на 5 секунд
rcall Wait5s	; Задержка на 5 секунд
rjmp  Haupt	; Бесконечный цикл

.org  $400
CharTab:
.db   $04,$04,$1F,$04,$04,$00,$1F,$00 	; Код символа $00: символ "+/-"
.db   $0C,$02,$04,$08,$0E,$00,$00,$00 	; Код символа $01: символ "^2"
.db   $0C,$02,$0C,$02,$0C,$00,$00,$00 	; Код символа $02: символ "^3"
.db   $18,$06,$01,$06,$18,$00,$1F,$00 	; Код символа $03: символ ">="
.db   $03,$0C,$10,$0C,$03,$00,$1F,$00 	; Код символа $04: символ "<="
.db   $04,$04,$1F,$04,$1F,$04,$04,$00 	; Код символа $05: символ "неравно"
.db   $00,$1F,$00,$1F,$00,$1F,$00,$00 	; Код символа $06: символ "равно"
.db   $15,$15,$15,$15,$15,$15,$15,$00 	; Код символа $06: символ "3 штриха"
Text1:
.db   "Sonderzeichen:  "
Text2:
.db   0,' ',1,' ',2,' ',3,' ',4,' ',5,' ',6,' ',7,' '
Text3:
.db   "  Display-Test  "
Text4:
.db   "Char 6-8 blinken"
Text5:
.db   "Cursor-Pos. = 65"
