;******************************************************************************** ; @module LCD-W164B ; @type driver ; @device ELECTRONIC ASSEMBLY - W164B-NLW ; @details ; The LCD must be connected to a W65C22N Interface Chip: ; - IO.RB0-7 -> LCD.D0-7 data lines ; - IO.RA5 -> LCD.RS register select ; - IO.RA6 -> LCD.R/W read/write ; - IO.RA7 -> LCD.E enable ; ; @requires LCD_IO: Base Address of IO Chip ; @optparam LCD_MEM: Memory address for a runtime variable. Default = $300 ; @depends IO-W65C22N ;******************************************************************************** .ifndef LCD_IO .fatal "LCD_IO is not defined: set it to the base address of the IO chip of the LCD" .endif .ifndef INCLUDE_IOW65C22 .error "IO-W65C22 module is not included" .endif .include "utility.h65" ; RAM VARIABLES .ifndef LCD_MEM LCD_MEM = $300 .endif LCD_CHARCOUNT = LCD_MEM ; PIN MASKS LCD_E = %10000000 LCD_RW = %01000000 LCD_RS = %00100000 ; .local LCD_E,LCD_RW,LCD_RS ; LCD Instructions (see datasheet for more) LCD_CMD_CLEAR = %00000001 ; clear display LCD_CMD_ENTRY_MODE = %00000110 ; auto-shift cursor LCD_CMD_DISPLAY_ON = %00001111 ; everything on, with blinking cursor LCD_CMD_FUNCTION_SET = %00111000 ; 8-bit, 4 lines, 5x7 font LCD_CMD_SET_ADDRESS = %10000000 ; or with the address ; LCD Constants LCD_LINE1 = $00 LCD_LINE2 = $40 LCD_LINE3 = $10 LCD_LINE4 = $50 LCD_CLEAR = %00000000 ;******************************************************************************** ; @function Initialize the lcd module ; @details call before doing anything else ;******************************************************************************** .proc lcd_init ; init IO lda #$ff ; RB 0-7 output sta LCD_IO+IO::DDRB ; UT_update_with_mask LCD_IO + IO::DDRA, (LCD_RS | LCD_RW | LCD_E), (LCD_RS | LCD_RW | LCD_E) lda #(LCD_RS | LCD_RW | LCD_E) ; RA 5-7 output sta LCD_IO+IO::DDRA ; init lcd lda #LCD_CMD_FUNCTION_SET jsr _lcd_cmd lda #LCD_CMD_DISPLAY_ON jsr _lcd_cmd lda #LCD_CMD_CLEAR jsr _lcd_cmd lda #LCD_CMD_ENTRY_MODE jsr _lcd_cmd stz LCD_CHARCOUNT rts .endproc ;******************************************************************************** ; PRINTING TO LCD ;******************************************************************************** ;******************************************************************************** ; @function Clear the display ; @see lcd_print ;******************************************************************************** .proc lcd_clear stz LCD_CHARCOUNT lda #LCD_CLEAR jsr _lcd_cmd lda #(LCD_CMD_SET_ADDRESS | LCD_LINE1) jsr _lcd_cmd rts .endproc ;******************************************************************************** ; @function Print a null-terminated string ; @param ARG0-1 Address of the string to print ;******************************************************************************** .proc lcd_print ldy #$00 @lcd_print_loop: lda (ARG0),y beq @lcd_print_end jsr lcd_char iny bra @lcd_print_loop @lcd_print_end: rts .endproc ;******************************************************************************** ; @macro Print a null-terminated string ; @param message: Address of the message ;******************************************************************************** .macro Print message jsr lcd_clear lda #.LOBYTE(message) sta ARG0 lda #.HIBYTE(message) sta ARG1 jsr lcd_print .endmacro ;******************************************************************************** ; @macro Print a single character ; @param A: Character to print ;******************************************************************************** .proc lcd_char pha pha ; TODO use UT_update_with_mask? jsr _lcd_wait_nbusy pla sta LCD_IO + IO::RB lda #LCD_RS sta LCD_IO + IO::RA lda #(LCD_RS | LCD_E) sta LCD_IO + IO::RA lda #LCD_RS sta LCD_IO + IO::RA inc LCD_CHARCOUNT jsr _lcd_set_address pla ; put char back in a .endproc ;******************************************************************************** ; Internal LCD Commands ;******************************************************************************** ; read busy flag .proc _lcd_wait_nbusy stz LCD_IO + IO::DDRB ; set IO1-LCD_IO + IO::RB to input @lcd_wait_nbusy_loop: ; read the busy flag lda #LCD_RW sta LCD_IO + IO::RA lda #(LCD_RW | LCD_E) sta LCD_IO + IO::RA lda LCD_IO + IO::RB and #%10000000 ; and updates zero flag, if not set retry bne @lcd_wait_nbusy_loop lda #%00000000 ; TODO dont overwrite 0-4 sta LCD_IO + IO::RA lda #%11111111 ; set IO1-LCD_IO + IO::RB to output sta LCD_IO + IO::DDRB rts .endproc .proc _lcd_cmd ; send cmd in acc pha jsr _lcd_wait_nbusy pla ; TODO use UT_update_with_mask? sta LCD_IO + IO::RB lda #LCD_CLEAR sta LCD_IO + IO::RA lda #LCD_E sta LCD_IO + IO::RA lda #LCD_CLEAR sta LCD_IO + IO::RA rts .endproc ;******************************************************************************** ; Set the LCD DD-RAM Address so that text linebreaks after 16 chars ;******************************************************************************** .proc _lcd_set_address ; check if checks are necessary lda LCD_CHARCOUNT beq @lcd_line1 and #%10001111 ; ($10 | $20 | $30 | $40) = %01110000 bne @lcd_set_address_done ; checks necessary lda LCD_CHARCOUNT beq @lcd_line1 cmp #$10 beq @lcd_line2 cmp #$20 beq @lcd_line3 cmp #$30 beq @lcd_line4 cmp #$40 ; set to line1 when full ; set to line1 when full beq @lcd_line1 @lcd_set_address_done: rts @lcd_line1: stz LCD_CHARCOUNT lda #(LCD_CMD_SET_ADDRESS | LCD_LINE1) jsr _lcd_cmd rts @lcd_line2: lda #(LCD_CMD_SET_ADDRESS | LCD_LINE2) jsr _lcd_cmd rts @lcd_line3: lda #(LCD_CMD_SET_ADDRESS | LCD_LINE3) jsr _lcd_cmd rts @lcd_line4: lda #(LCD_CMD_SET_ADDRESS | LCD_LINE4) jsr _lcd_cmd rts .endproc