;------------------------------------------------------------------------------ ; Source code driver for Optrex DMC type LCD Character Displays (4-bit interface). ; Rev 1.2, 11-15-00 ; Kevin Nichols, CSE466 ; ; Code based on Motorola AN1745/D "Interfacing the HC705C8A to an LCD Module" ; by Mark Glenwinkel, Consumer Systems Group, Austin TX ; ; Note: Timing loops are based on using a 32.0 MHz crystal. ; ; Register Usage: ; A: General purpose usage ; R0: Timing subroutine used for delay ; R1: Timing subroutine - User sets to control delay ; DPTR: Used to access message string ; ; Port Usage: ; P0.0, P0.1: Used for control lines to the LCD ; P1: High nibble (P1.4 - P1.7) used for data lines. Will have to ; modify program to not destroy contents of P1.0 - P1.3. ; ; RAM Usame: ; One 8-bit RAM location used to hold the current index into the string table ; ; Stack Usage ; Uses approx 10 bytes of stack space for CALL return addresses ; ; Other Usages: ; No interrupts are used. No timers are used ; ;------------------------------------------------------------------------------ $NOMOD51 ; disable predefined 8051 registers $INCLUDE (At89c55.INC) ; include CPU definition file for Atmel 89C55 ;------------------------------------------------------------------------------ ; Module name ;------------------------------------------------------------------------------ NAME Optrex_Driver ;------------------------------------------------------------------------------ ; Segment and variable declarations ;------------------------------------------------------------------------------ ; ;------------------------------------------------------------------------------ ; Put the STACK segment in the main module. ;------------------------------------------------------------------------------ STACK1 SEGMENT IDATA ; ?STACK goes into IDATA RAM. RSEG STACK1 ; switch to ?STACK segment. DS 10 ; reserve your stack space ; 5 bytes in this example. ;------------------------------------------------------------------------------ ; DATA SEGMENT--Reserves space in DATA RAM-- ;------------------------------------------------------------------------------ VARS SEGMENT DATA ; segment for DATA RAM. RSEG VARS ; switch to this data segment index: DS 1 ; Holds current index into char string ;------------------------------------------------------------------------------ ; CONSTANTS (typeless) ;------------------------------------------------------------------------------ e_line EQU P0_0 ; Port P0.0 Enable line (1 = enable) rs EQU P0_1 ; Port P0.1 RS line (1 = data, 0 = instructions) lcd_data EQU P1 ; Port P1 (P1.4 - P1.7) are LCD data lines ;--------------------------------------------------------------------------- ; ASCII Message Lookup Table ;--------------------------------------------------------------------------- CONST SEGMENT CODE RSEG CONST msg1: DB 'Test Message!' DB 0 ;Null terminated strings! ;------------------------------------------------------------------------------ ; LJMP to "start" ;------------------------------------------------------------------------------ CSEG AT 0 ; absolute Segment at Address 0 LJMP start ; reset location (jump to start) ;------------------------------------------------------------------------------ ; CODE SEGMENT--Reserves space in CODE ROM for assembler instructions. ;------------------------------------------------------------------------------ code_seg_name SEGMENT CODE RSEG code_seg_name ; switch to this code segment USING 0 ; Use register bank 0 start: MOV SP,#STACK1-1 ; assign stack at beginning CLR e_line CLR rs CLR P1_4 CLR P1_5 CLR P1_6 CLR P1_7 ; Initialize the LCD Display ; Wait for 15ms MOV R1,#150 ; Set delay time CALL var_delay ; Send init command MOV lcd_data,#0x30 SETB e_line NOP ; Min cycle time is 500ns CLR e_line ; Wait for 4.1ms MOV R1,#41 CALL var_delay ; Send init command MOV lcd_data,#0x30 SETB e_line NOP ; Min cycle time is 500ns CLR e_line ; Wait for 100us MOV R1,1 CALL var_delay ; Send init command MOV A,#0x30 CALL lcd_write ; Send function set command ; 4-bit bus, 2 rows, 5x7 dots MOV A,#0x20 CALL lcd_write MOV A,#0x20 CALL lcd_write MOV A,#0x80 CALL lcd_write ; Send display control command ; Display on, cursor off, no blinking MOV A,#0x00 CALL lcd_write MOV A,#0xC0 CALL lcd_write ; Send clear display command ; Clear display, Cursor address = 0 MOV A,#0x00 CALL lcd_write MOV R1,#16 CALL var_delay MOV A,#0x10 CALL lcd_write MOV R1,#16 CALL var_delay ; Send entry mode command ; Increment, no display shift MOV A,0x00 CALL lcd_write MOV A,#0x60 CALL lcd_write ; Send the message to the display ; Set the address, send the data CALL test_msg ; Busy wait (for test). Normally would return to calling program busy_wait: SJMP busy_wait $EJECT ;------------------------------------------------------------------------------ ; SUBROUTINES ;------------------------------------------------------------------------------ sub_segment SEGMENT CODE ; segment for interrupt function RSEG sub_segment ; switch to this code segment USING 0 ; register bank for subroutines; ;var_delay routine. ;-------------------------- ; To use: Load variable R1 with number of tenths of milliseconds you want to delay, ; then call this subroutine. Routine will return after specified amount of time. ; Min. delay time (R1 = 1) is 100us, max. delay time (R1 = 0) is 25.6ms. ; Assumes a 32MHz crystal, for an internal cycle time of 375ns (internal divide-by-12) ; Formula is delay = R1 * 100us (if R1 = 1, get a 100us delay, if R1 = 10, get a 1ms delay) ; The timing is close, but not exact. See following chart: ; R1 = 1, Delay Error = +0.875us (100.875us) ; R1 = 10, Delay Error = +1.25us (1001.25us) ; R1 = 100, Delay Error = +13.3us (10.0133ms) ; R1 = 0, Delay Error = +32.75us (25.63275ms) ; Number of cycles for each instruction shown in comments var_delay: MOV R0,#132 ;1 = 375ns L1: DJNZ R0,L1 ;2 = 750ns (do this 132 times) DJNZ R1,var_delay ;2 = 750ns (do this R1 times) RET ;2 = 750ns ;lcd_write routine - Sends data to the LCD lcd_write: MOV lcd_data,A ; Upper 4 bits connect to LCD SETB e_line ; Toggle enable line NOP ; 500ns minimum cycle time CLR e_line MOV R0,#53 ;Simple 40us delay loop for LCD L2: DJNZ R0,L2 ; RET ;lcd_addr - Routine to send LCD address for start of characters lcd_addr: CLR rs ; Put LCD in command mode MOV lcd_data,A SETB e_line ; Clock in the data NOP ; 500ns minimum cycle time CLR e_line MOV R0,#53 ;Simple 40us delay loop for LCD L4: DJNZ R0,L4 ; SETB rs ; Put LCD in data mode RET ;test_message - Simple routine to send a pre-defined message to the LCD test_msg: MOV A,#80H ; Addr = 04H MSB (remember, we send 4 bits at a time!) CALL lcd_addr ; (See Optrex spec sheet for address format) MOV A,#40H ; Addr = 04H LSB CALL lcd_addr MOV DPTR,#msg1 ; Point to start of ASCII message table MOV index,0x00 ; Initialize index to zero (offset into table) L3: MOV A,index ; Get current index into table MOVC A,@A+DPTR ; Get character (for high nibble) JZ outmsg ; Are we done displaying message? CALL lcd_write ANL A,#0x0F ; Mask high nibble, so rotate works like shift RL A RL A RL A RL A CALL lcd_write INC index ; Increment table index to point to next character SJMP L3 outmsg: RET ;------------------------------------------------------------------------------ ; The END directive is ALWAYS required. ;------------------------------------------------------------------------------ END ; End Of File