Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

16F84A LCD code - won't work

Status
Not open for further replies.
Hello all.

1) I am trying to get an HD4470U-type LCD display to work in 4-bit mode with a PIC 16F84A.

2) The project builds correctly in MPLAB and burns onto the PIC, but the LCD remains dead: no response at all from the LCD.

3) Can anyone tell me what to do to get it working, please?

I am using the code provided in "Microcontroller Programming the Microchip PIC", 2007, Sanchez and Canton, CRC Press, pp.327-338.


The code specifies an RW line: my development board does not have one, so I have made the following connections, per the circuit diagram (p.305):

E - RA1

RS - RA2

D4 - RB4

D5 - RB5

D6 - RB6

D7 - RB7


Here is the code, copied from MPLAB:

; File name: LCDTest3.asm
; Date: April 16, 2006
; Author: Julio Sanchez
; Processor: 16F84A
;
; Description:
; Program to exercises 4-bit PIC-to-LCD interface.
; Code assumes that LCD is driven by Hitachi HD44780
; controller and that the display supports two lines
; each one with 16 characters. The wiring and base
; address of each display line is stored in #define
; statements. These statements can be edited to
; accomodate a different set-up.
; Program uses delay loops for interface timing.
; WARNING:
; Code assumes 4Mhz clock. Delay routines must be
; edited for faster clock
;
; Displays: Minnesota State, Mankato
;
;===========================
; switches
;===========================
; Switches used in __config directive:
; _CP_ON Code protection ON/OFF
; * _CP_OFF
; * _PWRTE_ON Power-up timer ON/OFF
; _PWRTE_OFF
; _WDT_ON Watchdog timer ON/OFF
; * _WDT_OFF
; _LP_OSC Low power crystal occilator
; * _XT_OSC External parallel resonator/crystal ocillator
; _HS_OSC High speed crystal resonator (8 to 10 MHz)
; Resonator: Murate Erie CSA8.00MG = 8 MHz
; _RC_OSC Resistor/capacitor ocillator
; | (simplest, 20% error)
; |
; |_____ * indicates setup values presently selected

;=========================
; setup and configuration
;=========================
processor 16f84A
include <p16f84A.inc>
__config _XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF



;=====================================================
; constant definitions
; for PIC-to-LCD pin wiring and LCD line addresses
;=====================================================
#define E_line 1 ;|
#define RS_line 2 ;| -- from wiring diagram
#define RW_line 3 ;|
; LCD line addresses (from LCD data sheet)
#define LCD_1 0x80 ; First LCD line constant
#define LCD_2 0xc0 ; Second LCD line constant
; Note: The constant that define the LCD display line
; addresses have the high-order bit set in
; order to faciliate the controller command
;
;=====================================================
; variables in PIC RAM
;=====================================================
; Reserve 16 bytes for string buffer
cblock 0x0c
strData
endc
; Leave 16 bytes and Continue with local variables
cblock 0x1d ; Start of block
count1 ; Counter # 1
count2 ; Counter # 2
count3 ; Counter # 3
pic_ad ; Storage for start of text area
; (labeled strData) in PIC RAM
J ; counter J
K ; counter K
index ; Index into text table (also used
; for auxiliary storage)
store1 ; Local temporary storage
store2 ; Storage # 2
endc

;=========================================================
; program
;=========================================================
org 0 ; start at address
goto main
; Space for interrupt handlers
org 0x08

main:
movlw b'00000000' ; All lines to output
tris PORTA ; in port A
tris PORTB ; and port B
movlw b'00000000' ; All outputs ports low
movwf PORTA
movwf PORTB
; Wait and initialize HD44780
call delay_5 ; Allow LCD time to initialize
; itself
call delay_5
call initLCD ; Then do forced initialization
call delay_5 ; Wait again
; Store base address of text buffer in PIC RAM

movlw 0x0c ; Start address for buffer
movwf pic_ad ; to local variable
;======================
; first LCD line
;======================
; Store 16 blanks in PIC RAM, starting at address stored
; in variable pic_ad
call blank16
; Call procedure to store ASCII characters for message
; in text buffer
movlw d'3' ; Offset into buffer
call storeMSU
; Set DDRAM address to start of first line
call line1
; Call procedure to display 16 characters in LCD
call display16
;========================
; second LCD line
;========================
call delay_5 ; Wait for termination
call blank16 ; Blank buffer
; Call procedure to store ASCII characters for message
; in text buffer
movlw d'1' ; Offset into buffer
call storeUniv
call line2 ; DDRAM address of LCD line 2
call display16
;=======================
; done!
;=======================
loopHere:
goto loopHere ;done

;============================================================
; initialize LCD for 4-bit mode
;============================================================
initLCD:
; Initialization for Densitron LCD module as follows:
; 4-bit interface
; 2 display lines of 16 characters each
; cursor on
; left-to-right increment
; cursor shift right
; no display shift
;=======================|
; set command mode |
;=======================|
bcf PORTA,E_line ; E line low
bcf PORTA,RS_line ; RS line low
bcf PORTA,RW_line ; Write mode
call delay_125 ; delay 125 microseconds
;***********************|
; FUNCTION SET |
;***********************|
movlw 0x28 ; 0 0 1 0 1 0 0 0 (FUNCTION SET)
; | | | |__ font select:
; | | | 1 = 5x10 in 1/8 or 1/11 dc
; | | | 0 = 1/16 dc
; | | |___ Duty cycle select
; | | 0 = 1/8 or 1/11
; | | 1 = 1/16 (multiple lines)
; | |___ Interface width
; | 0 = 4 bits
; | 1 = 8 bits
; |___ FUNCTION SET COMMAND
call send8 ; 4-bit send routine

; Set 4-bit mode command must be repeated
movlw 0x28
call send8

;***********************|
; DISPLAY AND CURSOR ON |
;***********************|
movlw 0x0e ; 0 0 0 0 1 1 1 0 (DISPLAY ON/OFF)
; | | | |___ Blink character at cursor
; | | | 1 = on, 0 = off
; | | |___ Curson on/off
; | | 1 = on, 0 = off
; | |____ Display on/off
; | 1 = on, 0 = off
; |____ COMMAND BIT
call send8
;***********************|
; set entry mode |
;***********************|
movlw 0x06 ; 0 0 0 0 0 1 1 0 (ENTRY MODE SET)
; | | |___ display shift
; | | 1 = shift
; | | 0 = no shift
; | |____ cursor increment mode
; | 1 = left-to-right
; | 0 = right-to-left
; |___ COMMAND BIT
call send8

;***********************|
; cursor/display shift |
;***********************|
movlw 0x14 ; 0 0 0 1 0 1 0 0 (CURSOR/DISPLAY SHIFT)
; | | | |_|___ don't care
; | |_|__ cursor/display shift
; | 00 = cursor shift left
; | 01 = cursor shift right
; | 10 = cursor and display
; | shifted left
; | 11 = cursor and display
; | shifted right
; |___ COMMAND BIT
call send8
;***********************|
; clear display |
;***********************|
movlw 0x01 ; 0 0 0 0 0 0 0 1 (CLEAR DISPLAY)
; |___ COMMAND BIT
call send8
; Per documentation
call delay_5 ; Test for busy
return

;=======================
; Procedure to delay
; 42 microseconds
;=======================
delay_125
movlw D'42' ; Repeat 42 machine cycles
movwf count1 ; Store value in counter
repeat
decfsz count1,f ; Decrement counter
goto repeat ; Continue if not 0
return ; End of delay

;=======================
; Procedure to delay
; 5 milliseconds
;=======================
delay_5
movlw D'41' ; Counter = 41
movwf count2 ; Store in variable
delay
call delay_125 ; Delay
decfsz count2,f ; 40 times = 5 milliseconds
goto delay
return ; End of delay
;========================
; pulse E line
;========================
pulseE
bsf PORTA,E_line ; Pulse E line
nop
bcf PORTA,E_line
return

;=============================
; long delay sub-routine
; (for debugging)
;=============================
long_delay
movlw D'200' ; w = 200 decimal
movwf J ; J = w
jloop: movwf K ; K = w
kloop: decfsz K,f ; K = K-1, skip next if zero
goto kloop
decfsz J,f ; J = J-1, skip next if zero
goto jloop
return
;=============================
; LCD display procedure
;=============================
; Sends 16 characters from PIC buffer with address stored
; in variable pic_ad to LCD line previously selected
display16
call delay_5 ; Make sure not busy
; Set up for data
bcf PORTA,E_line ; E line low
bsf PORTA,RS_line ; RS line high for data
; Set up counter for 16 characters
movlw D'16' ; Counter = 16
movwf count3
; Get display address from local variable pic_ad
movf pic_ad,w ; First display RAM address to W
movwf FSR ; W to FSR
getchar
movf INDF,w ; get character from display RAM
; location pointed to by file select
; register
call send8 ; 4-bit interface routine
; Test for 16 characters displayed
decfsz count3,f ; Decrement counter
goto nextchar ; Skipped if done
return
nextchar:
incf FSR,f ; Bump pointer
goto getchar

;========================
; send 2 nibbles in
; 4-bit mode
;========================
; Procedure to send two 4-bit values to port B lines
; 7, 6, 5, and 4. High-order nibble is sent first
; ON ENTRY:
; w register holds 8-bit value to send
send8:
movwf store1 ; Save original value
call merge4 ; Merge with port B
; Now w has merged byte
movwf PORTB ; w to port B
call pulseE ; Send data to LCD
; High nibble is sent
movf store1,w ; Recover byte into w
swapf store1,w ; Swap nibbles in w
call merge4
movwf PORTB
call pulseE ; Send data to LCD
call delay_125
return
;=================
; merge bits
;=================
; Routine to merge the 4 high-order bits of the
; value to send with the contents of port B
; so as to preserve the 4 low-bits in port B
; Logic:
; AND value with 1111 0000 mask
; AND port B with 0000 1111 mask
; Now low nibble in value and high nibble in
; port B are al 0 bits:
; value = vvvv 0000
; port B = 0000 bbbb
; OR value and port B resulting in:
; vvvv bbbb
; ON ENTRY:
; w contain value bits
; ON EXIT:
; w contains merged bits
merge4:
andlw b'11110000' ; ANDing with 0 clears the
; bit. ANDing with 1 preserves
; the original value
movwf store2 ; Save result in variable
movf PORTB,w ; port B to w register
andlw b'00001111' ; Clear high nibble in port b
; and preserve low nibble
iorwf store2,w ; OR two operands in w
return

;========================
; blank buffer
;========================
; Procedure to store 16 blank characters in PIC RAM
; buffer starting at address stored in the variable
; pic_ad
blank16
movlw D'16' ; Setup counter
movwf count1
movf pic_ad,w ; First PIC RAM address
movwf FSR ; Indexed addressing
movlw 0x20 ; ASCII space character
storeit
movwf INDF ; Store blank character in PIC RAM
; buffer using FSR register
decfsz count1,f ; Done?
goto incfsr ; no
return ; yes
incfsr
incf FSR,f ; Bump FSR to next buffer space
goto storeit

;========================
; Set address register
; to LCD line 1
;========================
; ON ENTRY:
; Address of LCD line 1 in constant LCD_1
line1:
bcf PORTA,E_line ; E line low
bcf PORTA,RS_line ; RS line low, set up for control
call delay_5 ; busy?
; Set to second display line
movlw LCD_1 ; Address and command bit
call send8 ; 4-bit routine
; Set RS line for data
bsf PORTA,RS_line ; Setup for data
call delay_5 ; Busy?
return
;========================
; Set address register
; to LCD line 2
;========================
; ON ENTRY:
; Address of LCD line 2 in constant LCD_2
line2:
bcf PORTA,E_line ; E line low
bcf PORTA,RS_line ; RS line low, setup for control
call delay_5 ; Busy?
; Set to second display line
movlw LCD_2 ; Address with high-bit set
call send8
; Set RS line for data
bsf PORTA,RS_line ; RS = 1 for data
call delay_5 ; Busy?
return

;===============================
; first text string procedure
;===============================
storeMSU:
; Procedure to store in PIC RAM buffer the message
; contained in the code area labeled msg1
; ON ENTRY:
; variable pic_ad holds address of text buffer
; in PIC RAM
; w register hold offset into storage area
; msg1 is routine that returns the string characters
; an a zero terminator
; index is local variable that hold offset into
; text table. This variable is also used for
; temporary storage of offset into buffer
; ON EXIT:
; Text message stored in buffer
;
; Store offset into text buffer (passed in the w register)
; in temporary variable
movwf index ; Store w in index
; Store base address of text buffer in FSR
movf pic_ad,w ; first display RAM address to W
addwf index,w ; Add offset to address
movwf FSR ; W to FSR
; Initialize index for text string access
movlw 0 ; Start at 0
movwf index ; Store index in variable
; w still = 0
get_msg_char:
call msg1 ; Get character from table
; Test for zero terminator
andlw 0x0ff
btfsc STATUS,Z ; Test zero flag
goto endstr1 ; End of string
; ASSERT: valid string character in w
; store character in text buffer (by FSR)
movwf INDF ; store in buffer by FSR
incf FSR,f ; increment buffer pointer
; Restore table character counter from variable
movf index,w ; Get value into w
addlw 1 ; Bump to next character
movwf index ; Store table index in variable
goto get_msg_char ; Continue
endstr1:
return

; Routine for returning message stored in program area
msg1:
addwf PCL,f ; Access table
retlw 'M'
retlw 'i'
retlw 'n'
retlw 'n'
retlw 'e'
retlw 's'
retlw 'o'
retlw 't'
retlw 'a'
retlw 0

;=================================
; second text string procedure
;=================================
storeUniv:
; Processing identical to procedure StoreMSU
movwf index ; Store w in index
; Store base address of text buffer in FSR
movf pic_ad,0 ; first display RAM address to W
addwf index,0 ; Add offset to address
movwf FSR ; W to FSR
; Initialize index for text string access
movlw 0 ; Start at 0
movwf index ; Store index in variable
; w still = 0
get_msg_char2:
call msg2 ; Get character from table
; Test for zero terminator
andlw 0x0ff
btfsc STATUS,Z ; Test zero flag
goto endstr2 ; End of string
; ASSERT: valid string character in w
; store character in text buffer (by FSR)
movwf INDF ; Store in buffer by FSR
incf FSR,f ; Increment buffer pointer
; Restore table character counter from variable
movf index,w ; Get value into w
addlw 1 ; Bump to next character
movwf index ; Store table index in variable
goto get_msg_char2 ; Continue
endstr2:
return

; Routine for returning message stored in program area
msg2:
addwf PCL,f ; Access table
retlw 'S'
retlw 't'
retlw 'a'
retlw 't'
retlw 'e'
retlw ','
retlw 0x20
retlw 'M'
retlw 'a'
retlw 'n'
retlw 'k'
retlw 'a'
retlw 't'
retlw 'o'
retlw 0

end



Thanks for your advice.

Analogue.
 
Last edited:
hi,
If the program uses the RW line to the LCD, you must somehow connect it to your development board PIC.

The only other way is to connect the RW line at the LCD to 0V and rewrite the LCD subroutines to remove the BUSY line test [D7] and add Delay routines to wait until the LCD is ready, not BUSY.
Its not going to be an easy task.!
I would choose the 1st option...
 
Have you tried connecting RW to RA3?

";================================================= ====
; constant definitions
; for PIC-to-LCD pin wiring and LCD line addresses
;================================================= ====
#define E_line 1 ;|
#define RS_line 2 ;| -- from wiring diagram
#define RW_line 3 ;|
; LCD line addresses (from LCD data sheet)
#define LCD_1 0x80 ; First LCD line constant
#define LCD_2 0xc0 ; Second LCD line constant
; Note: The constant that define the LCD display line
; addresses have the high-order bit set in
; order to faciliate the controller command
;
 
@ ericgibbs and Mickster:

Thanks for the assistance, guys.

- I've worked out what the problem was: the code specifies a 4 Mhz crystal, but the one in my development board was 32Khz. I replaced it with the appropriate 4 Mhz component and the program functioned correctly.

But there's something odd. - As an experiment, I edited out all references to the RW line and burned the program onto a PIC: it still functioned correctly despite the RW line not being connected / referred to in software.

I suspect that the program doesn't actually check the busy flag, it must work by delaying the display to give it time to perform its functions.

- Analogue.
 
@ ericgibbs and Mickster:

Thanks for the assistance, guys.

- I've worked out what the problem was: the code specifies a 4 Mhz crystal, but the one in my development board was 32Khz. I replaced it with the appropriate 4 Mhz component and the program functioned correctly.

But there's something odd. - As an experiment, I edited out all references to the RW line and burned the program onto a PIC: it still functioned correctly despite the RW line not being connected / referred to in software.

I suspect that the program doesn't actually check the busy flag, it must work by delaying the display to give it time to perform its functions.

- Analogue.
hi,
Out of interest I have scanned thru that code posted for occurrences of RW, the only ones that show up are in the definitions, its NOT used anywhere in the code.!
Your solution of the 4mHz xtal [ refer the notes in the program] is the correct one, it uses 'delays' no BUSY test.
Why the author defined PORTA RW is unclear...:rolleyes:
 
I must admit to not looking any further than the definitions.

Wonder if there is a subsequent tutorial that does utilise the RW line?
 
Leftover from a previous version?

Why the author defined PORTA RW is unclear...:rolleyes:

Maybe, after improving the software / hardware, by replacing the busy check with a delay, he forgot to eliminate the definition. Been there, done that.
 
Maybe, after improving the software / hardware, by replacing the busy check with a delay, he forgot to eliminate the definition. Been there, done that.

hi Agustín.
I would agree he has forgotten to remove the definition, but would disagree that dropping the BUSY test for delays is an improvement.
Overall a program with BUSY, D7 testing runs faster and also can be written to accommodate different crystal frequencies.
[which the OP has discovered]

Eric
 
hi Agustín.
I would agree he has forgotten to remove the definition, but would disagree that dropping the BUSY test for delays is an improvement.
Overall a program with BUSY, D7 testing runs faster and also can be written to accommodate different crystal frequencies.
[which the OP has discovered] Eric

For me, it was an improvement (and a personal discovery!!). But, strictly speaking, you are right, Eric.

Just as a comment now (and openly digressing): I always wondered what could be if for any reason my LCD display stops working and my whole program hangs forever waiting for a busy flag that refuses to clear. (?) That is what WDT is for, isn't it?
 
@ ericgibbs and Mickster. - There is another LCD program, earlier in the chapter, where the RW line is used. Interestingly, the author writes that it uses both busy checking and delays:

"Program uses the busy flag to synchronize processor access, although delay loops are still required in some cases."

Analogue.
 
Status
Not open for further replies.

Latest threads

Back
Top