Недавно возникла необходимость переписать библиотеку для работы с LCD-индикаторами на базе контроллера HD44780 (KS066), и, как ни странно, не удалось найти нормальные исходники, хотя тема работы с индикатором довольно заезженная. В том, что нашлось то с временными задержками обращаются слишком вольно, то не используют минимальное количество проводов интерфейса, то исходный код написан так, чтобы вызывать Ктулху 🙂
Представленный код не претендует на шедевр, однако, 1) его размер стремится к минимальному, 2) управление индикатором производится с помощью 6-ти проводного интерфейса, 3) код работает с индикатором максимально быстро — все задержки установлены в соответствии со спецификацией HD44780.
Код легко модифицируется под любой тип индикатора. Для сохранения текущей позиции используется переменная col (номер текущей колонки): для двухстрочного индикатора col >= 16 при выводе во вторую строку. Вывод PB5 контроллера инициализирован как ШИМ для регулировки контрастности индикатора, цепочка R1C1 служит для получения постоянного напряжения: надоел подстроечный резистор 🙂
;* ************************************************************************ *
;* lcd44780.asm, вывод символов на LCD-индикатор на базе HD44780/KS066 *
;* используется 6-ти проводной интерфейс: E,RS,DB7..DB4; RW=0 *
;* текущий формат 16x2, для смены индикатора следует изменить lcd_setxy *
;* ************************************************************************ *
.include "tn43Udef.inc"
; опции программирования, fuses
; WDTON=SUT0=CKSEL3=CKSEL2=CKSEL0=0
.set VERSION = 0x0100
.equ XTAL = 8000000 ; тактовая частота в Hz
; * описание портов контроллера *
; порт A
.equ CTAout = PORTA ; выходной порт
.equ CTAcfg = DDRA ; порт конфигурации выходов
.equ CTAin = PINA ; входной порт
.equ CTA_CFG = 0x00 ; константа cfg порта
.equ CTA_OUT = 0x00 ; константа out порта
; порт B
.equ CTBout = PORTB ; выходной порт, LCD
.equ CTBcfg = DDRB ; порт конфигурации выходов
.equ CTBin = PINB ; входной порт
.equ DB4pin = 0 ; LCD_DB4
.equ DB5pin = 1 ; LCD_DB5
.equ DB6pin = 2 ; LCD_DB6
.equ DB7pin = 3 ; LCD_DB7
.equ ENpin = 4 ; LCD_EN(out_PP)
.equ BRpin = 5 ; регулировка яркости, PWM_OC1B
.equ RSpin = 6 ; LCD_RS(out_PP)
.equ IRpin = 7 ; не используется
.equ CTB_CFG = (1< 15mS
lcdi_strtlp:
rcall lcd_ldel ; длинная задержка
dec tmp1 ; декремент счётчика задержки
brne lcdi_strtlp ; закрытие петли стартовой задержки
clt ;
ldi tmp1,0x03 ; пытаемся инициализировать контроллер: указываем
; 8ми-битный интерфейс, чтобы не сбить синхронизацию
; вывода тетрад
rcall lcd_ioh ; передача команды
rcall lcd_ldel ; длинная задержка (1.6mS)
ldi tmp1,0x03 ; повторно инициализируем контроллер, шина 8-бит
rcall lcd_ioh ; передача команды (39uS)
ldi tmp1,0x02 ; теперь указываем 4х-битный интерфейс (39uS), но 1я
; команда "глотается" как с 8ми битным: мл.часть
; контроллер "не заглатывает"
rcall lcd_ioh ; передача команды (39uS)
ldi tmp1,0x28 ; к этому времени контроллер ведёт себя адекватно,
; указываем 4х-битный интерфейс, 2-строки
rcall lcd_io ; передача команды (39uS)
lcd_clrscr:
clt ; RS=0, выводим команду
ldi tmp1,0x08 ; индикатор выключен
rcall lcd_io ; передача команды (39uS)
ldi tmp1,0x01 ; очистка экрана
rcall lcd_io ; передача команды
rcall lcd_ldel ; длинная задержка (1.6mS)
ldi tmp1,0x0C ; индикатор включен
rcall lcd_io ; передача команды (39uS)
ldi tmp1,0x06 ; режим ввода со сдвигом
rcall lcd_io ; передача команды (39uS)
clr col ; сброс счётчика текущей колонки
ret ; выход
;______________________________________________________________
; установка позиции вывода по col
lcd_setxy:
; IN - нет
; OUT - нет
; CHG - tmp1
push tmp1 ; сохранение tmp1
mov tmp1,col ; установка колонки для получения адреса
bst tmp1,4 ; сохранение флага 2ой строки
andi tmp1,0x0F ; выделение битов колонки
ori tmp1,0x80 ; установка адреса для 0й строки
bld tmp1,6 ; установка адреса вывода (Row0->0x80 при col<16, Row1->0xC0 при col>=16)
clt ; установка флага передачи команды, RS=0
rcall lcd_io ; установка курсора (39uS)
pop tmp1 ; восстановление tmp1
ret ; выход
;______________________________________________________________
; вывод символа на индикатор
lcd_putc:
; IN - tmp1
; OUT - нет
; CHG - T
rcall lcd_setxy ; установка позиции вывода на индикаторе
; быстрый вывод символа на индикатор (без установки курсора)
lcd_putcex:
; IN - tmp1
; OUT - нет
; CHG - T
set ; установка флага вывода данных, RS=1
rcall lcd_io ; вывод символа
inc col ; инкремент текущей колонки
andi col,0x1F ; выделяем значащие биты
ret ; выход
;______________________________________________________________
; вывод строки из кодовой памяти на индикатор
lcd_puts:
; IN - в Z указатель на строку
; OUT - нет
; CHG - Z, tmp1, T
rcall lcd_setxy ; установка позиции вывода на индикаторе
lpm tmp1,Z+ ; загрузка байта
tst tmp1 ; проверка на ==0 (конец строки)
brne lcdp_putc ; переход, если !=0
ret ; выход
lcdp_putc:
rcall lcd_putcex ; вывод символа на индикатор
rjmp lcd_puts ; закрытие цикла вывода
Скачать исходник можно здесь.