Dragon12 LCD Demo
 
In a previous hardware overview in Lab 4, we presented the basics of using an LCD controller, including specific information concerning the Hantronix LCD that is used on the Dragon12. In this demo, we will present a simple Code Warrior project for writing to the Dragon12 LCD.
 
 
Writing to the LCD
 
When we write to the Hantronix LCD module, we are either sending a command byte or a data byte. The command bytes are those used during initialization to clear a display, set the display characteristics (4 bit transfers, blinking cursor, etc.), etc. A particular command byte sets the DDRAM address (DD = Display Data) of the LCD, which tells us where to put a character (eg, line 1, 3rd character from the left). Once we set the starting DDRAM address, we can set up the LCD controller to automatically advance to the next position after each character is written. When the character position reaches the end-of-line, it does not advance to the next line but instead wraps around and overwrites the first character of the current line. So, once we reach the end of the first line, we must manually change the DD RAM to the address of the second line.
 
The data bytes correspond to the characters that we actually want to display on the LCD. To display a character data byte, we write to the CG RAM/ROM (CG = Character Generator). The CG ROM table contains preformatted ASCII characters and other special characters, and when we 'write' to a particular CG ROM address we are actually selecting the character stored at that address. The CG RAM is an area where we can define our own custom characters (which we don't cover in this lab). The data bytes we use in this lab are always ASCII characters with the standard ASCII code. Since we have a 4-bit interface to send an 8 bit character, we first send the 4-msb nibble, and then the 4-lsb nibble.
 
The main steps to do send a data byte to the LCD controller are: (1) Write a data byte to Port K with 4-msb nibble; (2) Create an Enable pulse to latch data into LCD controller (Enable high for > 230 ns, then low); (3) Wait for a specified amount of time for the function in the LCD module to be realized (40 usec - 4.1 msec); (4) Repeat steps (1)-(3) for the data byte having the 4-lsb data nibble. After the data bytes having the 4-msb and 4-lsb nibbles have been sent to the LCD controller, the character should now display.
 
As a detailed example of a data write, the procedure below outlines the steps needed to send the ASCII character 'S' to the LCD. This assumes that the LCD has been initialized and the DD RAM address set to the where we want the 'S' to display. All writes are to port K in the form of a 'movb' command. The steps needed to send a command byte are similar.
 
   1. The data byte we send to the LCD controller has the form: %00nnnnER. The first two bits are always zero (essentially ignored). The middle 4 bits 'nnnn' are either the 4-msb or 4-lsb nibble of the ASCII code - these are highlighted in the code below. The next bit 'E' (bit 1 of 0-7) is the enable bit; we set this to low when we first put the data into port K, then we set it to high to latch the data into the HD44780, and then low again. The last bit 'R' (bit 0) is the RS bit, which is 1 when we send data, or 0 when we send a command.
   2. The ASCII code for 'S' is %01010011 (= $53), which is also the address of 'S' in the CG RAM/ROM. This is the data byte we will send to the CG RAM/ROM, and we must break it up into two writes: first the 4-msb nibble (%0101) and then the 4-lsb nibble (%0011).
   3. Write the first byte containing the 4-msb nibble %0101, E=0, R=1 as: %00010101
   4. We then set enable high, E=1, for > 230 nsec: %00010111
   5. Then set enable low again, E-0, and wait for at least 46 usec: %00010101
   6. We then send the next byte containing the 4-lsb nibble, E=0, R=1: %00001101
   7. Then set the Enable bit high: %00001111
   8. And then set Enable low again and wait for > 46 usec: %00001101.
   9. The character 'S' should now be displayed on the LCD, and the cursor should have advanced to the next character. We then repeat the above steps for the next character to display.
 
 
Part 1 - Create Project, Evaluate and Run 'Go UTEP' Source Code
 
In the section below, we write a program that displays 'Go UTEP' on the LCD. We do this using a brute force technique of writing one character at a time to the LCD. This is not 'production code', but it will give you a good idea of what's involved in writing to the LCD.
 
;****************************************************************
;*
;* UTEP EE3376
;* lcd_demo.asm
;* 20 June 2005
;*
;* This lab demonstrates the basic initialization and use
;* of a Hantronix LCD (Model HDM16216L-5) on the Dragon12 HCS12 board.
;* This is a 16 char x 2 line LCD with a 5x7 dots character format.
;* Uses a HD44780 based controller. The Dragon12 LCD is hardwired to use
;* a 4-bit interface to Port K with pins set up as follows:
;*
;* RS = PK0 (Port K bit 0 to RS Register Selector)
;*   -> RS = 0 selects Instruction Register, RS=1 selects Data Register
;* EN = PK1 (Enable; pulse enable to latch data-write into LCD)
;* R/W = connected to ground (W = 0 since we only write)
;* DB4 = PK2; DB5 = PK3; DB6 = PK4; DB7 = PK5
;* PK6 and PK7 are unused
;*
;* Writes a single character at a time by sending 4-bit nibbles
;* The final display will show "Go" on line 1 and "UTEP" on line 2
;*
;****************************************************************
 
; export symbols
            XDEF Entry            ; export 'Entry' symbol
            ABSENTRY Entry        ; for absolute assembly: mark this as application entry point
 
 
PSEUDO_ROM        EQU        $1000    ; absolute address to place code/constant data
RAM                EQU        $2000    ; absolute address to place variables
STACK            EQU        $3C00    ; top of stack
 
 
; registers
portk             EQU        $32        ;
portk_ddr        EQU        $33        ; data direction register for Port K
 
; masks
enable            EQU        %00000010    ; enable bit for enable on/off pulses
output            EQU        %11111111    ; make a port an output
 
; constants
msec            EQU        $1770    ; 6000 (=$1770) loops = 1 msec in startx loop
 
; LCD initialization strings; msb = most significant bits, lsb = least sig. bits
init_lcd1        EQU        $0C        ; first init string we write (3 times) to LCD  on startup
init_lcd2        EQU        $08        ; set to 4 bit transfer (repeat twice)
init_lcd3        EQU        $08        ; set to 2 lines, 5x7 dots, 4 msb of 8 bits
init_lcd4        EQU        $20        ; set to 2 lines, 5x7 dots, 4 lsb of 8 bits
init_lcd5        EQU        $00        ; display off, 4 msb
init_lcd6        EQU        $20        ; display off, 4 lsb
init_lcd7        EQU        $00        ; display clear, 4 msb
init_lcd8        EQU        $04        ; display clear, 4 lsb
init_lcd9        EQU        $00        ; entry mode 4 msb
init_lcd10        EQU        $18        ; entry mode, 4 lsb (increment DDRAM, cursor moves)
init_lcd11        EQU        $00        ; display on, 4 msb
init_lcd12        EQU        $3C        ; display on, 4 lsb (display on, cursor on, blink on)
 
 
;-----------------------------------------------------
; variable/data section
            ORG RAM
; Insert your data definitions here.
 
 
;------------------------------------------------------
; code section
    ORG    PSEUDO_ROM    ;set PC to $1000
; Insert your code following the label "Entry"
 
Entry:
            ; do some initialization
            lds        #STACK        ; initialize stack pointer
            movb    #output, portk_ddr        ; make Port K an Output
            jsr        LCD_INIT     ; initialize the LCD
 
            ; steps to write 'Go UTEP' to the LCD
 
            ; write the character 'G' to LCD to line 1, RS=1
            movb    #$11, portk    ; 4 msb of ASCII 'G'
            jsr        ENABLE_PULSE
            movb    #$1D, portk    ; 4 lsb of ASCII 'G'
            jsr        ENABLE_PULSE
 
            ; write the character 'o' to LCD to line 1, RS=1
            movb    #$19, portk    ; 4 msb of ASCII 'o'
            jsr        ENABLE_PULSE
            movb    #$3D, portk    ; 4 lsb of ASCII 'o'
            jsr        ENABLE_PULSE
                
            ; set LCD to line 2 at DDRAM address $40, RS=0
            movb    #$30, portk    ; RS=0, %1100 to DB7-DB4
            jsr        ENABLE_PULSE
            movb    #$00, portk    ; RS=0, %0000 to DB3-DB0
            jsr        ENABLE_PULSE
                
            ; write the character 'U' to LCD line 2, RS=1
            movb    #$15, portk    ; 4 msb of ASCII 'U'
            jsr        ENABLE_PULSE
            movb    #$15, portk    ; 4 lsb of ASCII 'U'
            jsr        ENABLE_PULSE
                
            ; write the character 'T' to LCD line 2, RS=1
            movb    #$15, portk    ; 4 msb of ASCII 'T'
            jsr        ENABLE_PULSE
            movb    #$11, portk    ; 4 lsb of ASCII 'T'
            jsr        ENABLE_PULSE
                
            ; write the character 'E' to LCD line 2, RS=1
            movb    #$11, portk    ; 4 msb of ASCII 'E'
            jsr        ENABLE_PULSE
            movb    #$15, portk    ; 4 lsb of ASCII 'E'
            jsr        ENABLE_PULSE
 
            ; write the character 'P' to LCD line 2, RS=1
            movb    #$15, portk    ; 4 msb of ASCII 'P'
            jsr        ENABLE_PULSE
            movb    #$01, portk    ; 4 lsb of ASCII 'P'
            jsr        ENABLE_PULSE
 
            swi            ; end the program
 
;------------------------------------------------------
; Subroutine LCD_INIT
 
LCD_INIT:        ldd        #$14        ; wait 20 msec after power on  (need delay > 15 msec)
            jsr        DELAY
 
            ; write first init string three times with various delays
            movb    #init_lcd1, portk
 
            ; write 1/delay 1
            jsr        ENABLE_PULSE    ; pulse data in portk to write it
            ldd        #$05                ; wait 5 msec (need delay > 4.1 msec)
            jsr        DELAY
                
            ; write 2/delay 2
            jsr        ENABLE_PULSE    ; pulse data in portk to write it
            ldd        #$01                ; wait 1 msec (need delay > 100 usec)
            jsr        DELAY
 
            ; write 3, no delay needed
            jsr        ENABLE_PULSE    ; no delay needed here
 
            ; final initialization writes
            ; wait > 1 msec between each write ( > 40 usec delay needed)
 
            ; set to 4 bit transfers; only one 4-bit write needed
            movb    #init_lcd2, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
 
            ; set to 2 lines, 5x7 dot character display; 2 4-bit writes needed
            movb    #init_lcd3, portk
            jsr        ENABLE_PULSE    
            ldd        #$01
            jsr        DELAY
            movb    #init_lcd4, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
                
            ; display off; 2 4-bit writes needed
            movb    #init_lcd5, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
            movb    #init_lcd6, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
                
            ; display clear; 2 4-bit writes needed
            movb    #init_lcd7, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
            movb    #init_lcd8, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
                
            ; entry mode = increment, cursor move; 2 4-bit writes needed
            movb    #init_lcd9, portk
            jsr        ENABLE_PULSE
            ldd        #$01
            jsr        DELAY
            movb    #init_lcd10, portk
            jsr        ENABLE_PULSE
            ldd        #$01