;********************************************************************* ; * ; LCD Serializer Project * ; * ; Written by Tom Coonan * ; * ; This PIC code is for the LCD Serializer Board. This * ; code accepts serial data (either 9600 or 2400) that * ; is passed on to an LCD module. Small text-based * ; LCD modules are often based on several Hitachi * ; controllers. LCD modules that'll work, are usually * ; the ones with 14-pin connectors. * ; * ; Here's what this code does: * ; * ; 1. Two jumpers are read. JP1 (or "Lines") is one * ; of the LCD initializing parameters. JP2 is the baud * ; rate. Jumper in sets the port for 9600, jumper out * ; for 2400. * ; * ; 2. The Hitachi controller is initialized. * ; The sequence is best documented in the Microchip * ; App notes. The string "OK." is then printed on * ; the screen. * ; * ; 3. The code listens for serial characters. The special * ; character 0xFE signals that the next character is * ; a "command" byte, and is sent as such to the LCD * ; modules (see LCD module notes regarding what commands * ; there are, etc.) Otherwise, serial characters are * ; passed as is to the LCD. * ; * ; 4. Bytes written to the LCD are put on the appropriate * ; lines, and the necessary strobes and enables are wiggled. * ; * ; That's it. * ; * ;********************************************************************* ; PORT MAP ; ; PORTA ; 0 Input Serial ; 1 Output Power to LCD ; 2 Output LCD R/S ; 3 Output LCD E ; ; PORTB ; 0:7 Output LCD Data ; 4 Input {on power up} Baud Rate strap, LO=2400, HI=9600 ; 7 Input {on power up} Lines strap, LO=1 line, HI=2 lines STATUS equ 3 ; Status register ZEROBIT equ 2 ; Zero flag bit within Status register PORTA equ 5 ; Both Input/Output PORTB equ 6 ; Output only to LCD BAUD_STRAP_BIT equ 4 LINES_STRAP_BIT equ 7 SERIAL_BIT equ 0 ; Bit 0 on PORTA LCD_POWER_BIT equ 1 ; Bit 1 on PORTA LCD_RS_BIT equ 2 ; Bit 2 on PORTA LCD_E_BIT equ 3 ; Bit 3 on PORTA ;**** Application's Memory Variables INCHAR equ 8 ; Input character CMD_MODE equ 9 ; Command Mode flag ARG1 equ 10 ; Subroutine argument #1 ARG2 equ 11 ; Subroutine argument #1 COUNTHI equ 12 ; Counter for Delay routine (MSB) COUNTLO equ 13 ; Counter for Delay routine (LSB) DELAYHI equ 14 ; Argument for DELAY subroutine (MSB) DELAYLO equ 15 ; Argument for DELAY subroutine (LSB) MASK equ 16 ; Mask register for each serial bit input BAUD_9600 equ 17 ; Flag is non-zero if Baud is Jumpered (9600) TWO_LINES equ 18 ; Flag is non-zero if [TWO] Lines option is jumpered. TEMP equ 19 ; ; After the baud rates are established through the jumpers, the right ; delay constants are written into here. These are the constants ; used in all the run-time delays for serial. ; DELAY_HALF_SERIAL_HI equ 20 ; DELAY_HALF_SERIAL_LO equ 21 ; DELAY_FULL_SERIAL_HI equ 22 ; DELAY_FULL_SERIAL_LO equ 23 ; ;*** End MEMORY Registers ********************************** ; DELAY Constants. ; ; Delays were set emperically - using a Logic Analyzer and ; looking at peek signals for bit timing. ASSUMES the ; current design's 4MHz crystal, of course. DELAY_FULL_9600_HI equ 1 DELAY_FULL_9600_LO equ 25 DELAY_HALF_9600_HI equ 1 DELAY_HALF_9600_LO equ 12 DELAY_FULL_2400_HI equ 1 DELAY_FULL_2400_LO equ 128 DELAY_HALF_2400_HI equ 1 DELAY_HALF_2400_LO equ 64 DELAY_50MS_HI equ 0x34 DELAY_50MS_LO equ 0x95 org 0x1FF ; Reset Vector goto Start ; Main entry point of program ;*** Start all subroutines in forst page.. org 0x000 ; The main line code starts here ;**************************************** ; * ; Start Up Initializtion Stuff * ; * ;**************************************** ; Read strapping jumpers on the board GetBaud btfss PORTB, BAUD_STRAP_BIT goto GetBaud9600 ; Bit is LO - Jumper is IN goto GetBaud2400 ; Bit is HI - Jumper is OUT GetBaud9600 ; Jumper IN = 9600 movlw 1 movwf BAUD_9600 retlw 0 GetBaud2400 ; Jumper OUT = 2400 movlw 0 movwf BAUD_9600 retlw 0 GetLines btfss PORTB, LINES_STRAP_BIT goto GetLines2 ; Bit is LO - Jumper is IN goto GetLines1 ; Bit is HI - Jumper is OUT GetLines2 ; Jumper IN = 2 Lines movlw 1 movwf TWO_LINES retlw 0 GetLines1 ; Jumper OUT = 1 Line movlw 0 movwf TWO_LINES retlw 0 ; ; Remember, INCHAR started out as all zeros.. ; Therefore, we only need to SET bits (when PORTA:0 = 0V) ; Also, remember, serial is inverted: ; 0V on PORTA:0 indicates a logic '1' ; 5V on PORTA:0 indicates a logic '0' ; ; The MASK variable is maintained by caller. ; ; GetSerialBit ; for testing! Change these to NOP later, bit keep for consistent timing ; I use these two lines to generate little pulses that I put on ; a Logic Analyser. These pulses show where I sample the serial bits. ; If you decide to change supported bauds, or change crystals, etc. ; you will have to play with this. ; bsf PORTB, 7 ; bcf PORTB, 7 nop nop btfsc PORTA, SERIAL_BIT ; Get PORTA with a 'POSITIVE' serial bit on it retlw 0 ; Do nothing if we got 5V, or logic '0' GetSerialBitIsClr movf MASK, W iorwf INCHAR ; SET the bit retlw 0 ;************************************************************************ ; ; LCD Write Commands ; ; ; SENDLCDCOMMAND Sends a byte as a command to the LCD. ; SendLCDCommand bcf PORTA, LCD_RS_BIT ; Commands mean RS bit is LO movf ARG1, W movwf PORTB ; OK. Data is on the LCD data pins bsf PORTA, LCD_E_BIT ; Pulse the E bit bcf PORTA, LCD_E_BIT retlw 0 ; SENDLCDDATA Sends a byte as a data to the LCD. ; SendLCDData bsf PORTA, LCD_RS_BIT ; Data means RS bit is HI movf ARG1, W movwf PORTB ; OK. Data is on the LCD data pins bsf PORTA, LCD_E_BIT ; Pulse the E bit bcf PORTA, LCD_E_BIT retlw 0 ;************************************************************************ ;*********************************************************** ; * ; Specific Delay Functions. These are specific delays * ; which load specific delay counts. These routines then * ; call the generic DELAY subroutine. * ; * ;*********************************************************** DelayHalfSerial movf DELAY_HALF_SERIAL_HI, W movwf DELAYHI movf DELAY_HALF_SERIAL_LO, W movwf DELAYLO retlw 0 DelayFullSerial ; Baud rate is 9600, so set up accorsingly movf DELAY_FULL_SERIAL_HI, W movwf DELAYHI movf DELAY_FULL_SERIAL_LO, W movwf DELAYLO retlw 0 Delay50MS movlw DELAY_50MS_HI movwf DELAYHI movlw DELAY_50MS_LO movwf DELAYLO retlw 0 InitDelay200MS call Delay50MS call Delay call Delay50MS call Delay call Delay50MS call Delay call Delay50MS call Delay retlw 0 ; General DELAY routine ; Inputs: ; DELAYHI - Initial Countdown value MSB ; DELAYLO - Initial Countdown value LSB ; Uses: ; COUNTHI - Counter value MSB ; COUNTLO - Counter value LSB Delay movf DELAYHI, W movwf COUNTHI movf DELAYLO, W movwf COUNTLO DelayLoop decfsz COUNTLO goto DelayLoop decfsz COUNTHI goto DelaySetup2 goto DelayDone DelaySetup2 movf DELAYLO, W movwf COUNTLO goto DelayLoop DelayDone retlw 0 ;***** Main Program Entry !!! Start clrf PORTA clrf PORTB clrf CMD_MODE ; Start with CLD_MODE flag set to false clrf BAUD_9600 ; Default is 2400 Baud clrf TWO_LINES ; Default is 1 line movlw 11110001B ; Bits 3,2,1 are outputs, Bit 0 is an input tris PORTA ; Power up the LCD so that we can read the stapping inputs. ; The LCD module needs to be on so that DB4 and DB7 are truly inputs. bsf PORTA, LCD_POWER_BIT ; this is power to the LCD movlw 10010000B ; all pins are outputs EXCEPT bits 7 & 4 tris PORTB call Delay50MS ; Wait for tristate to settle...?... call Delay call GetBaud ; Sample PORTA:4 jumper for 2400 or 9600 option call GetLines ; Sample PORTA:7 jumper for lines option movlw 00000000B ; Now, ALL pins are outputs tris PORTB ;** Set up Baud Constants, based on BAUD jumper. ; ; btfss BAUD_9600, 0 goto Setup2400 goto Setup9600 Setup9600 movlw DELAY_FULL_9600_HI movwf DELAY_FULL_SERIAL_HI movlw DELAY_FULL_9600_LO movwf DELAY_FULL_SERIAL_LO movlw DELAY_HALF_9600_HI movwf DELAY_HALF_SERIAL_HI movlw DELAY_HALF_9600_LO movwf DELAY_HALF_SERIAL_LO goto DoneBaudSetup Setup2400 movlw DELAY_FULL_2400_HI movwf DELAY_FULL_SERIAL_HI movlw DELAY_FULL_2400_LO movwf DELAY_FULL_SERIAL_LO movlw DELAY_HALF_2400_HI movwf DELAY_HALF_SERIAL_HI movlw DELAY_HALF_2400_LO movwf DELAY_HALF_SERIAL_LO DoneBaudSetup call InitDelay200MS ; Initialize the LCD as follows: ; InitLCD ; Send 0x30 (1 Line) or 0x38 (2 line) movlw 0x30 ; default is 1 Line btfsc TWO_LINES, 0 movlw 0x38 ; TWO Lines option InitLCD_Cont1 movwf ARG1 call SendLCDCommand call InitDelay200MS ; Send 0x0C (or 0x0F) movlw 0x0C movwf ARG1 call SendLCDCommand call InitDelay200MS ; Send 0x03 movlw 0x03 movwf ARG1 call SendLCDCommand call InitDelay200MS ; Send 0x01 ** Clear movlw 0x01 movwf ARG1 call SendLCDCommand ; Delay 100ms call Delay50MS call Delay call Delay50MS call Delay ; Send 0x06 ** Set AUTOINC, and NO Shifting movlw 0x06 movwf ARG1 call SendLCDCommand call InitDelay200MS ; Send 0x02 ** (HOME) movlw 0x02 movwf ARG1 call SendLCDCommand call InitDelay200MS ;******* START Main Loop Here ************** ; This is where we should insert any little startup LCD messages... ; Print "OK." on startup... movlw 0x4F movwf ARG1 call SendLCDData call InitDelay200MS movlw 0x4B movwf ARG1 call SendLCDData call InitDelay200MS movlw 0x2E movwf ARG1 call SendLCDData call InitDelay200MS WaitChar ; Wait for start bit clrf INCHAR ; movlw 00000001B movwf MASK btfss PORTA, 0 ; If we get a '1' or a START bit, skip! goto WaitChar ; Nope.. no start bit. Keep looking ; Got an edge! ; Output a PULSE for testing. Replace these with NOPs later. ;bsf PORTA, 3 ;bcf PORTA, 3 nop nop call DelayHalfSerial ; Delay half a bit call Delay btfss PORTA,0 ; Serial bit *should* be zero. goto WaitChar ; False alarm.. ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #0 movlw 00000010B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #1 movlw 00000100B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #2 movlw 00001000B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #3 movlw 00010000B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #4 movlw 00100000B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #5 movlw 01000000B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #6 movlw 10000000B movwf MASK ; Delay one bit time call DelayFullSerial call Delay call GetSerialBit ; Get Bit #7 ; Delay one bit time for the STOP bit call DelayFullSerial call Delay ;goto WaitChar goto GotSerialChar ; Test: When we get a serial char, simply write it to PORTB. movf INCHAR, W movwf PORTB goto WaitChar GotSerialChar ; ; OK! Got a character. ; If CMD_MODE is true mode then ; Send this character as an Command byte to the LCD ; Clear the CMD_MODE flag ; Else if char is oxFE then ; Set the CMD_MODE flag ; Else ; Send this character as a Data byte to the LCD ; End If ; movf CMD_MODE, W xorlw 1 skpz goto SendData goto SendCommand ; We are in CMD_MODE, send Command SendCommand movf INCHAR, W movwf ARG1 call SendLCDCommand clrf CMD_MODE goto WaitChar ; We are NOT in CMD_MODE, send Data SendData ; If character is 0xFE then this is special Instruction Escape code movf INCHAR, W xorlw 0xFE skpz goto SendData2 movlw 1 ; Set CMD_MODE movwf CMD_MODE goto WaitChar SendData2 ; Now, really send the data.. movf INCHAR, W movwf ARG1 call SendLCDData clrf CMD_MODE goto WaitChar ;***** END Main Program end