;*********************************************************** ;* ;* LCDDriver.asm - V2.0 ;* ;* Contains the neccessary functions to display text to a ;* 2 x 16 character LCD Display. Additional functions ;* include a conversion routine from an unsigned 8-bit ;* binary number to and ASCII text string. ;* ;* Version 2.0 - Added support for accessing the LCD ;* Display via the serial port. See version 1.0 for ;* accessing a memory mapped LCD display. ;* ;*********************************************************** ;* ;* Author: David Zier ;* Date: March 17, 2003 ;* Company: TekBots(TM), Oregon State University - EECS ;* Version: 2.0 ;* ;*********************************************************** ;* Rev Date Name Description ;*---------------------------------------------------------- ;* - 8/20/02 Zier Initial Creation of Version 1.0 ;* A 3/7/03 Zier V2.0 - Updated for USART LCD ;* ;* ;*********************************************************** ;*********************************************************** ;* Internal Register Definitions and Constants ;* NOTE: A register MUST be named 'mpr' in the Main Code ;* It is recomended to use register r16. ;* WARNING: Register r17-r22 are reserved and cannot be ;* renamed outside of the LCD Driver functions. Doing ;* so will damage the functionality of the LCD Driver ;*********************************************************** .def wait = r17 ; Wait Loop Register .def count = r18 ; Character Counter .def line = r19 ; Line Select Register .def type = r20 ; LCD data type: Command or Text .def q = r21 ; Quotient for div10 .def r = r22 ; Remander for div10 .equ LCDLine1 = $80 ; LCD Line 1 select command .equ LCDLine2 = $c0 ; LCD Line 2 select command .equ LCDClear = $01 ; LCD Clear Command .equ LCDHome = $02 ; LCD Set Cursor Home Command .equ LCDPulse = $08 ; LCD Pulse signal, used to simulate ; write signal .equ LCDCmd = $00 ; Constant used to write a command .equ LCDTxt = $01 ; Constant used to write a text character .equ LCDMaxCnt = 16 ; Maximum number of characters per line .equ LCDLn1Addr = $0100 ; Beginning address for Line 1 data .equ LCDLn2Addr = $0110 ; Beginning address for Line 2 data ;----------------------------------------------------------- ;*********************************************************** ;* Public LCD Driver Suboutines and Functions ;* These functions and subroutines can be called safely ;* from within any program ;*********************************************************** ;----------------------------------------------------------- ;******************************************************* ;* SubRt: LCDInit ;* Desc: Initialize the Serial Port and the Hitachi ;* Display 8 Bit inc DD-RAM ;* Pointer with no features ;* - 2 LInes with 16 characters ;******************************************************* LCDInit: push mpr ; Save the state of machine in mpr, SREG ; Save the SREG push mpr ; push wait ; Save wait ; Setup the Communication Ports ; Port B: Output ; Port D: Input w/ internal pullup resistors ; Port F: Output on Pin 3 ldi mpr, $00 ; Initialize Port B for outputs out PORTB, mpr ; Port B outputs high ldi mpr, $ff ; except for any overrides out DDRB, mpr ; ldi mpr, $00 ; Initialize Port D for inputs out PORTD, mpr ; with Tri-State ldi mpr, $00 ; except for any overrides out DDRD, mpr ; ldi mpr, $00 ; Initialize Port F Pin 3 to sts PORTF, mpr ; output inorder to twiddle the ldi mpr, (1<= 100 brlo B2A_1 ; goto next check ldi count, 3 ; Three chars are written adiw XL, 3 ; Increment X 3 address spaces rjmp B2A_3 ; Continue with program B2A_1: cpi mpr, 10 ; is mpr >= 10 brlo B2A_2 ; Continue with program ldi count, 2 ; Two chars are written adiw XL, 2 ; Increment X 2 address spaces rjmp B2A_3 ; Continue with program B2A_2: adiw XL, 1 ; Increment X 1 address space ldi count, 1 ; One char is written B2A_3: ;Do-While statement that converts Binary to ASCII rcall div10 ; Call the div10 function ldi mpr, '0' ; Set the base ASCII integer value add mpr, r ; Create the ASCII integer value st -X, mpr ; Load ASCII value to memory mov mpr, q ; Set mpr to quotiant value cpi mpr, 0 ; does mpr == 0 brne B2A_3 ; do while (mpr != 0) pop XL ; restore X-pointer pop XH ; pop q ; restore q pop r ; restore r pop mpr ; restore mpr ret ; return from function ;------------------------------------------------------- ;******************************************************* ;* Private LCD Driver Functions and Subroutines ;* NOTE: It is not recommended to call these functions ;* or subroutines, only call the Public ones. ;******************************************************* ;------------------------------------------------------- ;******************************************************* ;* Func: LCDSetLine ;* Desc: Change line to be written to ;******************************************************* LCDSetLine: push mpr ; Save mpr mov mpr,line ; Copy Command Data to mpr rcall LCDWriteCmd ; Write the Command pop mpr ; Restore the mpr ret ; Return from function ;******************************************************* ;* Func: LCDClrLine ;* Desc: Manually clears a single line within an LCD ;* Display and Data Memory by writing 16 ;* consecutive ASCII spaces $20 to both the LCD ;* and the memory. The line to be cleared must ;* first be set in the LCD and the Z pointer is ;* pointing the first element in Data Memory ;******************************************************* LCDClrLine: ldi mpr, ' ' ; The space char to be written ldi count, LCDMaxCnt; The character count LCDClrLine_1: st Z+, mpr ; Clear data memory element rcall LCDWriteChar ; Clear LCD memory element dec count ; Decrement the count brne LCDClrLine_1 ; Continue untill all elements are cleared ret ; Return from function ;******************************************************* ;* Func: LCDWriteLine ;* Desc: Writes a line of text to the LCD Display. ;* This routine takes a data element pointed to ;* by the Z-pointer and copies it to the LCD ;* Display for the duration of the line. The ;* line the Z-pointer must be set prior to the ;* function call. ;******************************************************* LCDWriteLine: ldi count, LCDMaxCnt; The character count LCDWriteLine_1: ld mpr, Z+ ; Get the data element rcall LCDWriteChar ; Write element to LCD Display dec count ; Decrement the count brne LCDWriteLine_1 ; Continue untill all elements are written ret ; Return from function ;******************************************************* ;* Func: LCDWriteCmd ;* Desc: Write command that is in the mpr to LCD ;******************************************************* LCDWriteCmd: push type ; Save type register push wait ; Save wait register ldi type, LCDCmd ; Set type to Command data rcall LCDWriteData ; Write data to LCD push mpr ; Save mpr register ldi mpr, 2 ; Wait approx. 4.1 ms LCDWC_L1: ldi wait, 205 ; Wait 2050 us rcall LCDWait ; dec mpr ; The wait loop cont. brne LCDWC_L1 ; pop mpr ; Restore mpr pop wait ; Restore wait register pop type ; Restore type register ret ; Return from function ;******************************************************* ;* Func: LCDWriteChar ;* Desc: Write character data that is in the mpr ;******************************************************* LCDWriteChar: push type ; Save type register push wait ; Save the wait register ldi type, LCDTxt ; Set type to Text data rcall LCDWriteData ; Write data to LCD ldi wait, 16 ; Delay 160 us rcall LCDWait ; pop wait ; Restore wait register pop type ; Restore type register ret ; Return from function ;******************************************************* ;* Func: LCDWriteData ;* Desc: Write data or command to LCD ;******************************************************* LCDWriteData: out SPDR, type ; Send type to SP ldi wait, 2 ; Wait 2 us rcall LCDWait ; Call Wait function out SPDR,mpr ; Send data to serial port ldi wait, 2 ; Wait 2 us rcall LCDWait ; Call Wait function ldi wait, LCDPulse ; Use wait temporarially to sts PORTF, wait ; to send write pulse to LCD ldi wait, $00 ; sts PORTF, wait ; ret ; Return from function ;******************************************************* ;* Func: LCDWait ;* Desc: A wait loop that is 10 + 159*wait cycles or ;* roughly wait*10us. Just initialize wait ;* for the specific amount of time in 10us ;* intervals. ;******************************************************* LCDWait:push mpr ; Save mpr LCDW_L1:ldi mpr, $49 ; Load with a 10us value LCDW_L2:dec mpr ; Inner Wait Loop brne LCDW_L2 dec wait ; Outer Wait Loop brne LCDW_L1 pop mpr ; Restore mpr ret ; Return from Wait Function ;******************************************************* ;* Bin2ASCII routines that can be used as a psuedo- ;* printf function to convert an 8-bit binary ;* number into the unigned decimal ASCII text ;******************************************************* ;*********************************************************** ;* Func: div10 ;* Desc: Divides the value in the mpr by 10 and ;* puts the remander in the 'r' register and ;* and the quotiant in the 'q' register. ;* DO NOT modify this function, trust me, it does ;* divide by 10 :) ~DZ ;*********************************************************** div10: push r0 ; Save register ; q = mpr / 10 = mpr * 0.000110011001101b mov q, mpr ; q = mpr * 1.0b lsr q ; q >> 2 lsr q ; q = mpr * 0.01b add q, mpr ; q = (q + mpr) >> 1 lsr q ; q = mpr * 0.101b add q, mpr ; q = (q + mpr) >> 3 lsr q lsr q lsr q ; q = mpr * 0.001101b add q, mpr ; q = (q + mpr) >> 1 lsr q ; q = mpr * 0.1001101b add q, mpr ; q = (q + mpr) >> 3 lsr q lsr q lsr q ; q = mpr * 0.0011001101b add q, mpr ; q = (q + mpr) >> 1 lsr q ; q = mpr * 0.10011001101b add q, mpr ; q = (q + mpr) >> 4 lsr q lsr q lsr q lsr q ; q = mpr * 0.000110011001101b ; compute the remainder as r = i - 10 * q ; calculate r = q * 10 = q * 1010b mov r, q ; r = q * 1 lsl r ; r << 2 lsl r ; r = q * 100b add r, q ; r = (r + q) << 1 lsl r ; r = q * 1010b mov r0, r ; r0 = 10 * q mov r, mpr ; r = mpr sub r, r0 ; r = mpr - 10 * q ; Fix any errors that occur div10_1:cpi r, 10 ; Compare with 10 brlo div10_2 ; do nothing if r < 10 inc q ; fix qoutient subi r, 10 ; fix remainder rjmp div10_1 ; Continue until error is corrected div10_2:pop r0 ; Restore registers ret ; Return from function