HD44780 with PIC

Thread Starter

Mcaudle870

Joined Aug 13, 2015
12
I bought a hd44780 lcd and soldered pigtails to all 16 leads. After not getting anywhere with the micro (PIC18f8722) i tried just manipulating inputs with a breadboard, but I cant even get anything with that.

I have my backlight and contrast working properly, but as soon as I put my VDD to 5vdc, every other input to the lcd goes to 5vdc. Is this normal and I am just misunderstanding this conceptually, or is the lcd damaged?
 

atferrari

Joined Jan 6, 2004
4,442
What about the micro?
Who told the micro what to do?
How the micro was told what to do?
Have you managed to use that micro doing something even simple?
 

Thread Starter

Mcaudle870

Joined Aug 13, 2015
12
Yes, I have a basic grasp of the micro. I have since been able to manipulate the lcd with a set of dip switches. All the pins on the lcd are high, and you have to drive them low. It was counterintuitive for me at first.


My issue is that when I clear a bit like the one I have controlling the enable, it does not drive the corresponding pin on the LCD low. Does this mean I need pull down resistors? And if so, won't that end up being problematic when trying to read the busy flag?
 

JohnInTX

Joined Jun 26, 2012
4,564
My issue is that when I clear a bit like the one I have controlling the enable, it does not drive the corresponding pin on the LCD low. Does this mean I need pull down resistors?
You don't need pull downs. It sounds like you don't have the port pins configured correctly. Be sure your any ports which are analog inputs have been set to DIGITAL (see ADCON1). Note that RA4 is an open drain output. Set pins to outputs by setting their TRIS bits to 0. Visit the port pin listing to ensure that you have turned off any peripheral that may share pins with the LCD pins. Be sure to do any bcf/bsf to LATx not PORTx.

I would use a light pulldown on E so that when the PIC is coming out of reset, you don't generate spurious commands to the LCD.

There are many threads here with code in C and assembler that cover LCD modules. Search away. Post a schematic and your code if you need more help.

Good luck.
 

Thread Starter

Mcaudle870

Joined Aug 13, 2015
12
My only issue with that is that I only have had a few microcontroller classes and I only know assembly language. I know I need to learn c, but between work, going to school at night, and every other side project I have, I just haven't hAd the time. It seems like there's not much assembly language tutorials out there.
 

GopherT

Joined Nov 23, 2012
8,012
Your question is timely - I just saw a simple 1602 LCD display on the world's greatest machine today.
Presumably, the LCD was driven by a HD44780. The bottom row uses a full block moving down 14 positions to show where my prizes were in the manufacturing process

 

JohnInTX

Joined Jun 26, 2012
4,564
If you are using this board, the LCD bus is driven via SPI IO expander (U8, MCP23S17). The user's guide says that you can disable this chip but does not mention that it has a way to recover the LCD bus/ctrl lines to the IO ports. Unless you have a compelling reason to insert SPI between the PIC and LCD, I'd disable or remove U8 and reroute the LCD wires to a port. The board documentation includes source code in C. Even if you don't know C you can probably get enough out of reading it to see what they did.

Good luck!

 

Thread Starter

Mcaudle870

Joined Aug 13, 2015
12
No i am just using the ports to control a separate hd44780. I changed my PORTXs to LATXs and everything works as its supposed to, except it outputting the wrong characters. However, all the characters are all 4 columns off on the character table. This is my code, im sure that there is a setting that is off, but im very much a novice at this and am not sure where to start looking. Here is my code:

#include <p18F8722.inc>


CONFIG OSC = HS
CONFIG WDT = OFF
CONFIG FCMEN = OFF
CONFIG IESO = OFF
CONFIG PWRT = OFF
CONFIG BOREN = OFF
CONFIG MCLRE = ON
CONFIG LVP = OFF
CONFIG XINST = OFF



LCD_DATA equ LATB ; LCD data bus
LCD_DATA_READ equ PORTB
#define LCD_D_DIR TRISB,A ; LCD data port direction
#define LCD_E LATC,7,A ; LCD E clock
#define LCD_E_DIR TRISC,7,A ; LCD E clock direction
#define LCD_RW LATC,6,A ; LCD read/write line
#define LCD_RW_DIR TRISC,6,A ; LCD R/W pin direction
#define LCD_RS LATC,5,A ; LCD register select line
#define LCD_RS_DIR TRISC,5,A ; LCD RS signal direction
#define LCD_cport LATC,A


pushr macro arg
movff arg, POSTINC1
endm

popr macro arg
movff POSTDEC1, arg
movff INDF1, arg
endm

alloc_stk macro n
movlw n
addwf FSR1L,F,A
movlw 0x00
addwfc FSR1H,F,A
endm

dealloc_stk macro n
movlw n
subwf FSR1L,F,A
movlw 0x00
subwfb FSR1H,F,A
endm

send2LCD macro
bsf LCD_RS
bcf LCD_RW
bsf LCD_E
movwf LCD_DATA,A
nop
nop
bcf LCD_E
endm

org 0x00
goto start
org 0x08
retfie
org 0x18
retfie
start lfsr FSR1,0xE00
movlw 0x0E
movwf ADCON1,A
call LCD_init
movlw upper msg1
movwf TBLPTRU,A
movlw high msg1
movwf TBLPTRH,A
movlw low msg1
movwf TBLPTRL,A
movlw 0x80
call LCD_cmd
call LCD_putstr
movlw upper msg2
movwf TBLPTRU,A
movlw high msg2
movwf TBLPTRH,A
movlw low msg2
movwf TBLPTRL,A
movlw 0xC0
call LCD_cmd
call LCD_putstr
forever nop
goto forever


LCD_init clrf LCD_cport ; p make sure the LCD control port is low
bcf LCD_E_DIR ; configure control lines
bcf LCD_RW_DIR ; directions to output
bcf LCD_RS_DIR ; "
movlw 0x38 ; g py configure display to 2 x 20
call LCD_cmd ; send command to LCD
movlw 0x0F ; turn on display and cursor
call LCD_cmd ; "
movlw 0x14 ; shift cursor right
call LCD_cmd ; "
movlw 0x01 ; clear cursor and return to home position
call LCD_cmd ; "
movlw 0x02 ; clear cursor and return to home position
call LCD_cmd ; "
return

LCD_cmd pushr WREG ; save WREG in the stack ; save WREG in the stack
call LCD_rdy ; wait until LCD is ready
bcf LCD_RS ; select IR register
bcf LCD_RW ; Set write mode
bsf LCD_E ; Se up o c oc d Setup to clock data
popr WREG ; restore WREG
movwf LCD_DATA,A ; send out the command in WREG
nop ; small delay to lengthen E pulse
bcf LCD_E
return

LCD_rdy setf LCD_D_DIR ; configure LCD data port for input
bcf LCD_RS ; select IR register
bsf LCD_RW ; setup to read busy flag
bsf LCD_E ; pull LCD E-line to high
nop ; small delay
movf LCD_DATA_READ,W,A ; read busy flag and DDRAM address
nop ; small delay to lengthen E pulse
bcf LCD_E ; pull LCD E-line to low
btfsc WREG,7,A ; is busy flag (BF) cleared
goto LCD_rdy
clrf LCD_D_DIR ; configure data pins for output
return

LCD_putc pushr WREG ; save WREG
call LCD_rdy ; wait until LCD is not busy
popr WREG ; restore data in WREG ; restore data in WREG
send2LCD ; write WREG content to LCD
pushr WREG ; save WREG data
call get_DDRAM_addr ; get the DDRAM address in PRODL
movlw 0x13
cpfseq PRODL,A
goto chk_0x53 ; reach the end of first line
movlw 0xC0
call LCD_cmd ; set DDRAM address to 0x40
call LCD_rdy
popr WREG
send2LCD ; re output the same character
return

chk_0x53 movlw 0x53
cpfseq PRODL,A
goto chk_0x27
; reach the end of the second line
movlw 0x94 ; ( set DDRAM address to 0x14 (start of
call LCD_cmd ; 3rd row)
call LCD_rdy
popr WREG
send2LCD
return
; reach the end of the third line
chk_0x27 movlw 0x27
cpfseq PRODL,A
return
movlw 0xD4 ; set DDRAM address to 0x54 (start of
call LCD_cmd ; 4th row)
call LCD_rdy
popr WREG
send2LCD
return

; output a string (pointed to by TBLPTR) in program memory to the LCD ; output a string (pointed to by TBLPTR) in program memory to the LCD
LCD_putstr tblrd*+ ; read a character into TABLAT
movf TABLAT,W,A ; copy to WREG
tstfsz WREG,A ; test WREG and skip if zero
goto send_it
return

send_it call LCD_putc
goto LCD_putstr

get_DDRAM_addr setf LCD_D_DIR ; confi LCD d t t f i t figure LCD data port for input
bcf LCD_RS ; select IR register
bsf LCD_RW ; setup to read busy flag
bsf LCD_E ; pull LCD E-line to high
nop ; add ll d l dd a small delay
movf LCD_DATA,W,A ; read busy flag and DDRAM address
nop ; small delay to length E pulse
bcf LCD_E ; pull LCD E-line to low
movwf PRODL,A
return

msg1 data "FACK YOU"
msg2 data "GIMME THE 40"


end
 

atferrari

Joined Jan 6, 2004
4,442
Msg1 is for "facx" maybe? Yes, reading somebody else code is not easy.

Maybe you need to add H'30' to the code for the char to be displayed properly. As my ex insisted, always, I could be dead wrong.
 

atferrari

Joined Jan 6, 2004
4,442
Your question is timely - I just saw a simple 1602 LCD display on the world's greatest machine today.
Presumably, the LCD was driven by a HD44780. The bottom row uses a full block moving down 14 positions to show where my prizes were in the manufacturing process
Testing (and tasting) probably made the guy debugging this, very fat (and maybe, happy...).
 

JohnInTX

Joined Jun 26, 2012
4,564
How about a little formatting?? I put the code into CODE=asm tags but also formatted the source a bit.
Follow labels with a colon and put them on a separate line to indicate the start of a code block. I also like to put a blank line after a branch to indicate a possible change in code flow. You don't need ,A (ACCESS) after instructions, MPASM will figure that out for you. You DO need, and have done, to specify the destination registers as ,F or ,W - don't use the defaults. Its easier to read - especially when you want others to read it..

I also shut down the interrupts in the non-functional interrupt routines. If you ever got one, it would hang the system.

It looks like you are missing some of the LCD init.. See the 44780 datasheet. You also need some delays between the initial LCD commands until you can poll the busy line.

I kind of like the macro-based parameter stack. I would use a symbol instead of a literal to init the pointer (FSR1) and point that to to end of a cblock so that it would be apparent that you've allocated a block of RAM for a stack.. These things are really hard to debug if you don't realize what the code does. A block of comments to say what and why you did it that way is essential. I know what you are doing, but I'd have to look at the code to see which way the pushes went and if it ran into other RAM.. Why would I want to do that?

I wouldn't use the dedicated product registers (PRODL et al) unless it was a result of a hardware multiply. Hard to tell without comments but move it if it isn't.

Not bad though!
Have fun.
Code:
#include <p18F8722.inc>


    CONFIG OSC = HS
    CONFIG WDT = OFF
    CONFIG FCMEN = OFF
    CONFIG IESO = OFF
    CONFIG PWRT = OFF
    CONFIG BOREN = OFF
    CONFIG MCLRE = ON
    CONFIG LVP = OFF
    CONFIG XINST = OFF

LCD_DATA equ LATB ; LCD data bus
LCD_DATA_READ equ PORTB
#define LCD_D_DIR TRISB    ; LCD data port direction
#define LCD_E LATC,7,       ; LCD E clock
#define LCD_E_DIR TRISC,7    ; LCD E clock direction
#define LCD_RW LATC,6,       ; LCD read/write line
#define LCD_RW_DIR TRISC,6    ; LCD R/W pin direction
#define LCD_RS LATC,5        ; LCD register select line
#define LCD_RS_DIR TRISC,5    ; LCD RS signal direction
#define LCD_cport LATC


pushr macro arg
    movff arg, POSTINC1
    endm

popr macro arg
    movff POSTDEC1, arg
    movff INDF1, arg
    endm

alloc_stk macro n
    movlw n
    addwf FSR1L,F,A
    movlw 0x00
    addwfc FSR1H,F,A
    endm

dealloc_stk macro n
    movlw n
    subwf FSR1L,F,A
    movlw 0x00
    subwfb FSR1H,F,A
    endm

send2LCD macro
    bsf LCD_RS
    bcf LCD_RW
    bsf LCD_E
    movwf LCD_DATA,A
    nop
    nop
    bcf LCD_E
    endm

    ;================= PROGRAM STARTS  =========================
    org 0x00
    goto start
    org 0x08
    clrf    INTCON  ; better turn IRQs off if no service routines
    return
    org 0x18
    clrf    INTCON
    return

start:
    lfsr FSR1,0xE00
    movlw 0x0E
    movwf ADCON1        ; ,A MPASM will figure this out, not needed
    ;
    ; Need to init the IO! TRIS etc. for each port
    ;
    call LCD_init

    movlw upper msg1        ; TBLPTR->msg1
    movwf TBLPTRU
    movlw high msg1
    movwf TBLPTRH
    movlw low msg1
    movwf TBLPTRL
    movlw 0x80            ; LINE1
    call LCD_cmd
    call LCD_putstr        ; put msg 1

    movlw upper msg2        ; TBLPTR->msg2
    movwf TBLPTRU
    movlw high msg2
    movwf TBLPTRH
    movlw low msg2
    movwf TBLPTRL
    movlw 0xC0            ; LINE2
    call LCD_cmd
    call LCD_putstr        ; put msg 2

forever:
    nop
    goto forever


LCD_init:
    clrf LCD_cport ; p make sure the LCD control port is low
    bcf LCD_E_DIR ; configure control lines
    bcf LCD_RW_DIR ; directions to output
    bcf LCD_RS_DIR ; "
    ;
    ; missing a lot of LCD init here, see 44780 datasheet
    ; also need delays between commands until can poll 'busy'
    ;
    movlw 0x38        ; g py configure display to 2 x 20
    call LCD_cmd    ; send command to LCD
    movlw 0x0F        ; turn on display and cursor
    call LCD_cmd    ; "
    movlw 0x14        ; shift cursor right
    call LCD_cmd    ; "
    movlw 0x01        ; clear cursor and return to home position
    call LCD_cmd    ; "
    movlw 0x02        ; clear cursor and return to home position
    call LCD_cmd    ; "
    return

LCD_cmd:
    pushr WREG        ; save WREG in the stack ; save WREG in the stack
    call LCD_rdy    ; wait until LCD is ready
    bcf LCD_RS        ; select IR register
    bcf LCD_RW        ; Set write mode
    bsf LCD_E        ; Se up o c oc d Setup to clock data
    popr WREG        ; restore WREG
    movwf LCD_DATA  ; send out the command in WREG
    nop            ; small delay to lengthen E pulse
    bcf LCD_E
    return

LCD_rdy:
    setf LCD_D_DIR  ; configure LCD data port for input
    bcf LCD_RS        ; select IR register
    bsf LCD_RW        ; setup to read busy flag
    bsf LCD_E        ; pull LCD E-line to high
    nop            ; small delay
    movf LCD_DATA_READ,W,A ; read busy flag and DDRAM address
    nop            ; small delay to lengthen E pulse
    bcf LCD_E        ; pull LCD E-line to low
    btfsc WREG,7    ; is busy flag (BF) cleared
    goto LCD_rdy

    clrf LCD_D_DIR  ; configure data pins for output
    return

LCD_putc:
    pushr WREG        ; save WREG
    call LCD_rdy    ; wait until LCD is not busy

    popr WREG        ; restore data in WREG ; restore data in WREG
    send2LCD        ; write WREG content to LCD
    pushr WREG        ; save WREG data
    call get_DDRAM_addr ; get the DDRAM address in PRODL
    movlw 0x13
    cpfseq PRODL
    goto chk_0x53   ; reach the end of first line

    movlw 0xC0
    call LCD_cmd    ; set DDRAM address to 0x40
    call LCD_rdy
    popr WREG
    send2LCD        ; re output the same character
    return

chk_0x53:
    movlw 0x53
    cpfseq PRODL
    goto chk_0x27

    ; reach the end of the second line
    movlw 0x94        ; ( set DDRAM address to 0x14 (start of
    call LCD_cmd    ; 3rd row)
    call LCD_rdy
    popr WREG
    send2LCD
    return

    ; reach the end of the third line
chk_0x27:
    movlw 0x27
    cpfseq PRODL
    return

    movlw 0xD4 ; set DDRAM address to 0x54 (start of
    call LCD_cmd ; 4th row)
    call LCD_rdy
    popr WREG
    send2LCD
    return

; output a string (pointed to by TBLPTR) in program memory to the LCD ; output a string (pointed to by TBLPTR) in program memory to the LCD
LCD_putstr:
    tblrd*+        ; read a character into TABLAT
    movf    TABLAT,W    ; copy to WREG
    tstfsz  WREG    ; test WREG and skip if zero
    bra        send_it    ; BRA shorter than goto here

    return

send_it:
    call LCD_putc
    goto LCD_putstr

get_DDRAM_addr:
    setf LCD_D_DIR    ; confi LCD d t t f i t figure LCD data port for input
    bcf LCD_RS        ; select IR register
    bsf LCD_RW        ; setup to read busy flag
    bsf LCD_E        ; pull LCD E-line to high
    nop            ; add ll d l dd a small delay
    movf LCD_DATA,W    ; read busy flag and DDRAM address
    nop            ; small delay to length E pulse
    bcf LCD_E        ; pull LCD E-line to low
    movwf PRODL
    return

msg1 data "HELLO"    ; lets keep it nice, this is a family site
msg2 data "GIMME THE 40"
    end
Carry on.
 

Attachments

Last edited:

Thread Starter

Mcaudle870

Joined Aug 13, 2015
12
Thanks a lot man, I really appreciate you taking the time. I think the copy and paste made it look much messier than it is, but I will definitely be reading through your reply and tweaking. I'm just getting my feet wet so any help is awesome.
 
Top