character LCD 16F684 help

Thread Starter

adrenalina

Joined Jan 4, 2011
78
Hello everybody. I am trying to interface my LCD to a 16F684. I am following the tutorial on this page: http://joshuagalloway.com/lcd.html

I wrote the program, but I can't get anything on the display. I am programming the chip using assembly.

I went step by step following the tutorial to write the code. I wrote a routine to write a command to the LCD (LCD_WriteC) and a routine to write data to the lcd (LCD_WriteD). Can anybody help me out to see why I can't get anything on the display?
I already checked my code with the instructions on how to initialize the lcd and how to write data/commands and according to me they do what the website says.

edit: forgot to mention that I am trying to interface using 4-bit mode

Rich (BB code):
list        p=16F684
#include    <p16F684.inc>

errorlevel    -302
errorlevel    -312

EXTERN        delay10


;***** CONFIGURATION
            ; ext rest, no code protect, no brownout detect,
            ; no watchdog, power-up timer, int clock with I/O,
            ; no failsafe clock monitor, two-speed start-up disabled
__CONFIG    _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOD_OFF & _WDT_OFF & _PWRTE_ON & _INTOSCIO & _FCMEN_OFF & _IESO_OFF


;***** PIN ASSIGNMENTS

#define        LCD_EN    PORTA,5        ; LCD enable pin
#define        LCD_RS    PORTA,4        ; LCD RS pin
    

;***** VARIABLE DEFINITIONS
CONTEXT     UDATA_SHR            ; variables for context saving
cs_W        res    1
cs_STATUS    res    1

SHADOW        UDATA_SHR
sPORTA        res    1                ; shadow registers
sPORTC        res    1            

GENVAR        UDATA_SHR            ; general variables
LCD_DATA    res    1                ;variable to store data to be written to lcd    

DELAY        UDATA
counter        res    1

;*******************************************************************
RESET    CODE    0x0000            ; processor reset vector
        pagesel    Start
        goto    Start

;***** INTERRUPT SERVICE ROUTINE
ISR        CODE    0x0004
        ; *** Save context
        movwf    cs_W            ; save W
        movf    STATUS,w        ; save STATUS
        movwf    cs_STATUS

        ; isr code goes here
    

isr_end
        movf    cs_STATUS,w        ; restore STATUS
        movwf    STATUS
        swapf    cs_W,f            ; restore W
        swapf    cs_W,w
        retfie
    
;***** MAIN PROGRAM
MAIN    CODE

;***** Initialization
Start    ;configure ports
        movlw    b'00001111'        ; set RC<3:0> as digital IO
        banksel    ANSEL
        movwf    ANSEL
        
        banksel    TRISA            ; configure PORTA and PORTC
        movlw    b'110000'        ; set RC<3:0> as outputs
        movwf    TRISC
        movlw    b'001111'        ; set RC<5:4> as outputs
        movwf    TRISA
        
Initialize_LCD
        call    delay5ms        ; wait 20ms for LCD to power up
        call    delay5ms
        call    delay5ms
        call    delay5ms

        banksel    PORTA
        bcf        LCD_RS
        movlw    0x03
        movwf    PORTC            ; write 0x03 with RS = 0
        call    delay5ms        ; wait 5ms

        movwf    PORTC            ; write 0x03 with RS = 0 again
        call    delay200us                ; wait 200us

        movwf    PORTC            ; write 0x03 with RS = 0 one more time
        call    delay200us            ; wait 200us

        movlw    0x02            ; write 0x02 to enable four-bit mode
        movwf    PORTC
        call    delay5ms        ; wait 5ms
        
        movlw    0x28            ; write command "Set interface"
        movwf    LCD_DATA
        call    LCD_WriteC

        movlw    0x08            ; write command "Enable display/cursor"
        movwf    LCD_DATA
        call    LCD_WriteC
        
        movlw    0x01            ; write command "Clear and Home"
        movwf    LCD_DATA        
        call    LCD_WriteC
        
        movlw    0x06            ; write comamand "Set cursor move direction (right)
        movwf    LCD_DATA
        call    LCD_WriteC
        
        movlw    0x0C            ; turn on display
        movwf    LCD_DATA
        call    LCD_WriteC
        
        
            
;***** Main loop
mainloop
        call    delay5ms
        movlw    'A'
        movwf    LCD_DATA
        call    LCD_WriteD
        goto    $

LCD_WriteD
        banksel    PORTA
        bcf        LCD_EN            ; clear LCD_EN (low)
        nop
        bsf        LCD_RS            ; set LCD_RS (data = 1)
        swapf    LCD_DATA,w        ; put high nibble of data on d7-4
        movwf    PORTC
        bsf        LCD_EN            ; set LCD_EN (high)
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN (low)
        call    delay200us                ; wait 200 us for data writes
        movf    LCD_DATA,w
        movwf    PORTC            ; put the low nibble of data on d7-4
        bcf        LCD_EN             ;clear LCD_EN
        call    delay200us                ;wait 200us for data writes
        return

LCD_WriteC
        banksel    PORTA
        bcf        LCD_EN            ; clear LCD_EN (low)
        nop
        bcf        LCD_RS            ; clear LCD_RS (command = 0)
        swapf    LCD_DATA,w        ; put high nibble of data on d7-4
        movwf    PORTC
        bsf        LCD_EN            ; set LCD_EN (high)
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN (low)
        call    delay5ms                ; wait 5ms for command writes
        movf    LCD_DATA,w
        movwf    PORTC            ; put the low nibble of data on d7-4
        bcf        LCD_EN             ;clear LCD_EN
        call    delay5ms                ;wait 5ms for command writes
        return

delay5ms
        clrf    counter            ; delay = 5.124 ms    with 4Mhz clock
delayn    goto    $+1                
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        nop
        
        decfsz    counter
        goto    delayn
        return

delay200us
        movlw    .67                ; delay = 206 us with 4Mhz clock
        movwf    counter
decc    decfsz    counter
        goto    decc
        return


        END
 
Last edited:

Thread Starter

adrenalina

Joined Jan 4, 2011
78
Finally found the problem. In the initialization part for the display I forgot to clear LCD_EN before moving the data to portc and after moving the data I forgot to set LCD_EN.
Another problem was in the tutorial. In the section that says 4-bit write sequence:

After put the LOW byte of the data/command on d7-4 and before the wait at least 450 ns instruction there is supposed to be another instruction which is set "en" (EN=1 or high). if not the low nibble never gets written to the lcd. So if anybody is following the tutorial on that website don't forget to set "EN" after putting the lower nibble on the data pins.

final code:

Rich (BB code):
list        p=16F684
#include    <p16F684.inc>

errorlevel    -302
errorlevel    -312

EXTERN        delay10


;***** CONFIGURATION
            ; ext rest, no code protect, no brownout detect,
            ; no watchdog, power-up timer, int clock with I/O,
            ; no failsafe clock monitor, two-speed start-up disabled
__CONFIG    _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOD_OFF & _WDT_OFF & _PWRTE_ON & _INTOSCIO & _FCMEN_OFF & _IESO_OFF


;***** PIN ASSIGNMENTS

#define        LCD_EN    PORTA,5        ; LCD enable pin
#define        LCD_RS    PORTA,4        ; LCD RS pin
    



;***** VARIABLE DEFINITIONS
CONTEXT     UDATA_SHR            ; variables for context saving
cs_W        res    1
cs_STATUS    res    1

SHADOW        UDATA_SHR
sPORTA        res    1                ; shadow registers
sPORTC        res    1            

GENVAR        UDATA_SHR            ; general variables
LCD_DATA    res    1                ;variable to store data to be written to lcd    
counter        res    1

;*******************************************************************
RESET    CODE    0x0000            ; processor reset vector
        pagesel    Start
        goto    Start

;***** INTERRUPT SERVICE ROUTINE
ISR        CODE    0x0004
        ; *** Save context
        movwf    cs_W            ; save W
        movf    STATUS,w        ; save STATUS
        movwf    cs_STATUS

        ; isr code goes here
    

isr_end
        movf    cs_STATUS,w        ; restore STATUS
        movwf    STATUS
        swapf    cs_W,f            ; restore W
        swapf    cs_W,w
        retfie
    
;***** MAIN PROGRAM
MAIN    CODE

;***** Initialization
Start    ;configure ports
        movlw    b'00001111'        ; set RC<3:0> as digital IO
        banksel    ANSEL
        movwf    ANSEL
        
        banksel    TRISA            ; configure PORTA and PORTC
        movlw    b'110000'        ; set RC<3:0> as outputs
        movwf    TRISC
        movlw    b'001111'        ; set RC<5:4> as outputs
        movwf    TRISA
        
Initialize_LCD
        call    delay5ms        ; wait 20ms for LCD to power up
        call    delay5ms
        call    delay5ms
        call    delay5ms

        banksel    PORTA
        bcf        LCD_RS
        movlw    0x03
        bcf        LCD_EN
        movwf    PORTC            ; write 0x03 with RS = 0
        bsf        LCD_EN
        call    delay5ms        ; wait 5ms

        bcf        LCD_EN
        movwf    PORTC            ; write 0x03 with RS = 0 again
        bsf        LCD_EN
        call    delay200us                ; wait 200us

        bcf        LCD_EN
        movwf    PORTC            ; write 0x03 with RS = 0 one more time
        bsf        LCD_EN
        call    delay200us            ; wait 200us

        bcf        LCD_EN
        movlw    0x02            ; write 0x02 to enable four-bit mode    
        movwf    PORTC
        bsf        LCD_EN
        call    delay5ms        ; wait 5ms
    
            
        movlw    0x28            ; write command "Set interface"
        movwf    LCD_DATA
        call    LCD_WriteC

        movlw    0x08            ; write command "Enable display/cursor"
        movwf    LCD_DATA
        call    LCD_WriteC
        
        movlw    0x01            ; write command "Clear and Home"
        movwf    LCD_DATA        
        call    LCD_WriteC
        
        movlw    0x06            ; write command "Set cursor move direction (right)
        movwf    LCD_DATA
        call    LCD_WriteC
        
        movlw    0x0C            ; turn on display
        movwf    LCD_DATA
        call    LCD_WriteC
        
        
            
;***** Main loop
mainloop
        call    delay5ms
        movlw    'A'
        movwf    LCD_DATA
        call    LCD_WriteD
        goto    $

LCD_WriteD
        banksel    PORTA
        bcf        LCD_EN            ; clear LCD_EN (low)
        nop
        bsf        LCD_RS            ; set LCD_RS (data = 1)
        swapf    LCD_DATA,w        ; put high nibble of data on d7-4
        movwf    PORTC
        bsf        LCD_EN            ; set LCD_EN (high)
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN (low)
        call    delay200us        ; wait 200 us for data writes
        movf    LCD_DATA,w
        movwf    PORTC            ; put the low nibble of data on d7-4
        bsf        LCD_EN             ; set LCD_EN
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN
        call    delay200us                ;wait 200us for data writes
        return

LCD_WriteC
        banksel    PORTA
        bcf        LCD_EN            ; clear LCD_EN (low)
        nop
        bcf        LCD_RS            ; clear LCD_RS (command = 0)
        swapf    LCD_DATA,w        ; put high nibble of data on d7-4
        movwf    PORTC
        bsf        LCD_EN            ; set LCD_EN (high)
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN (low)
        call    delay5ms                ; wait 5ms for command writes
        movf    LCD_DATA,w
        movwf    PORTC            ; put the low nibble of data on d7-4
        bsf        LCD_EN             ; set LCD_EN
        nop                        ; wait at least 450 ns
        bcf        LCD_EN            ; clear LCD_EN
        call    delay5ms                ;wait 5ms for command writes
        return

delay5ms
        
        clrf    counter            ; delay = 5.124 ms    with 4Mhz clock
delayn    goto    $+1                
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        goto    $+1
        nop
        
        decfsz    counter
        goto    delayn
        return

delay200us
        movlw    .67                ; delay = 206 us with 4Mhz clock
        movwf    counter
decc  decfsz    counter
        goto      decc
        return
 

Thread Starter

adrenalina

Joined Jan 4, 2011
78
never mind it doesn't work well it works on a simulator, but when I built the circuit and programmed the chip it doesn't display anything on the lcd. I already checked my connections to make sure everything is connected correctly and it is. if it works on the simulator it should work physically.
This is what happens. The display turns on and the 1st line is filled with black squares. Then I pull the reset button on the pic high and the lcd has both lines filled with light black squares, not as dark as it was on the first line.

I thought the lcd might be bad, so I got my arduino and tested it with an example program and it works perfectly so its not the lcd.
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
That sounds like the LCD is at least initialising ok.

Have you checked the contrast pot voltage adjustment? It is very easy to get caught out with that. :)

I noticed on bootup your PORTA pins are in an undefined state, you might try setting the RS pin HI and the E pin LO immediately after setting TRISA. That will hold those pins in a more desirable state when the LCD does the 20mS initial delay before bootup.

Also check the R/W pin has been connected to ground!
 

Thread Starter

adrenalina

Joined Jan 4, 2011
78
so I did everything you suggested and well unfortunately it didn't work R/W pin is grounded put the RS High and E low and also checked the pot.

Then I remembered reading about read modify write problem with bsf and bcf. In the code I was doing for example bcf LCD_RS and then immediately bsf LD_EN and that could cause problem. So I used a shadow register. instead bsf and bcf on the port A, I changed it so that it does it on the shadow register then copy the shadow to the port.

part of the code:
Rich (BB code):
#define        LCD_EN    sPORTA,5        ; LCD enable pin
#define        LCD_RS    sPORTA,4        ; LCD RS pin


        bcf        LCD_RS
        nop
        bcf        LCD_EN
        call    shadow_A
        movlw    0x03
        movwf    PORTC            ; write 0x03 with RS = 0
        bsf        LCD_EN
        call    shadow_A
        nop
        bcf        LCD_EN
        call    shadow_A
        call    delay5ms        ; wait 5ms


shadow_A
        movf    sPORTA,w
        movwf    PORTA
        return
and finally after making those changes the LCD displayed the text!!! I guess that is why it only worked on the simulator for the simulator having to bsf or bcf instructions one after another is not a problem, but for the chip it is.
 

ErnieM

Joined Apr 24, 2011
8,377
Congratulations. Everyone stumbles getting these things working the first time.

A bcf/bsf can cause a problem as the output may not have changed by the time the bsf is executed, and as this instruction does "read, make change, write" it can read the wrong data, thus negating the initial bcf.

Sometimes a nop will fix the problem, but a nop may not work on a faster PIC or slower hardware. A shadow register will always work.

There are a few other weird things the MPLAB simulator gets wrong, but generally (I don't know of an exception) if it works in MPLAB it words in the chip.

Now I have seen MPLAB get a different incorrect answer then the chip would get, but both are bugs and fixing the bug fixed both sim and chip. I don't remember the example where I saw this, it was something like a port with an analog function and the analog was not disabled for digital use.
 
Top