Управление LCD по 6-ти проводам

Недавно возникла необходимость переписать библиотеку для работы с LCD-индикаторами на базе контроллера HD44780 (KS066), и, как ни странно, не удалось найти нормальные исходники, хотя тема работы с индикатором довольно заезженная. В том, что нашлось то с временными задержками обращаются слишком вольно, то не используют минимальное количество проводов интерфейса, то исходный код написан так, чтобы вызывать Ктулху 🙂

lcd_sch

Представленный код не претендует на шедевр, однако, 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                ; закрытие цикла вывода

Скачать исходник можно здесь.