HD44780 with PIC

Discussion in 'Embedded Systems and Microcontrollers' started by Mcaudle870, Sep 2, 2015.

  1. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    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?
     
  2. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    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?
     
  3. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    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?
     
  4. JohnInTX

    Moderator

    Jun 26, 2012
    2,345
    1,025
    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.
     
  5. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,532
    2,369
    Here is a couple of useful articles on LCD interfacing including how to hardwire test.
    Max.
     
    atferrari and JohnInTX like this.
  6. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    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.
     
  7. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,532
    2,369
  8. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    What micro are you using now?
     
  9. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    I'm using the explorer board with the pic18f8722
     
  10. GopherT

    AAC Fanatic!

    Nov 23, 2012
    6,029
    3,799
    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

     
    ErnieM likes this.
  11. JohnInTX

    Moderator

    Jun 26, 2012
    2,345
    1,025
    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!

    [​IMG]
     
  12. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    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
     
  13. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    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.
     
  14. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    Testing (and tasting) probably made the guy debugging this, very fat (and maybe, happy...).
     
  15. JohnInTX

    Moderator

    Jun 26, 2012
    2,345
    1,025
    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 (ASM):
    1. #include <p18F8722.inc>
    2.  
    3.  
    4.     CONFIG OSC = HS
    5.     CONFIG WDT = OFF
    6.     CONFIG FCMEN = OFF
    7.     CONFIG IESO = OFF
    8.     CONFIG PWRT = OFF
    9.     CONFIG BOREN = OFF
    10.     CONFIG MCLRE = ON
    11.     CONFIG LVP = OFF
    12.     CONFIG XINST = OFF
    13.  
    14. LCD_DATA equ LATB ; LCD data bus
    15. LCD_DATA_READ equ PORTB
    16. #define LCD_D_DIR TRISB    ; LCD data port direction
    17. #define LCD_E LATC,7,       ; LCD E clock
    18. #define LCD_E_DIR TRISC,7    ; LCD E clock direction
    19. #define LCD_RW LATC,6,       ; LCD read/write line
    20. #define LCD_RW_DIR TRISC,6    ; LCD R/W pin direction
    21. #define LCD_RS LATC,5        ; LCD register select line
    22. #define LCD_RS_DIR TRISC,5    ; LCD RS signal direction
    23. #define LCD_cport LATC
    24.  
    25.  
    26. pushr macro arg
    27.     movff arg, POSTINC1
    28.     endm
    29.  
    30. popr macro arg
    31.     movff POSTDEC1, arg
    32.     movff INDF1, arg
    33.     endm
    34.  
    35. alloc_stk macro n
    36.     movlw n
    37.     addwf FSR1L,F,A
    38.     movlw 0x00
    39.     addwfc FSR1H,F,A
    40.     endm
    41.  
    42. dealloc_stk macro n
    43.     movlw n
    44.     subwf FSR1L,F,A
    45.     movlw 0x00
    46.     subwfb FSR1H,F,A
    47.     endm
    48.  
    49. send2LCD macro
    50.     bsf LCD_RS
    51.     bcf LCD_RW
    52.     bsf LCD_E
    53.     movwf LCD_DATA,A
    54.     nop
    55.     nop
    56.     bcf LCD_E
    57.     endm
    58.  
    59.     ;================= PROGRAM STARTS  =========================
    60.     org 0x00
    61.     goto start
    62.     org 0x08
    63.     clrf    INTCON  ; better turn IRQs off if no service routines
    64.     return
    65.     org 0x18
    66.     clrf    INTCON
    67.     return
    68.  
    69. start:
    70.     lfsr FSR1,0xE00
    71.     movlw 0x0E
    72.     movwf ADCON1        ; ,A MPASM will figure this out, not needed
    73.     ;
    74.     ; Need to init the IO! TRIS etc. for each port
    75.     ;
    76.     call LCD_init
    77.  
    78.     movlw upper msg1        ; TBLPTR->msg1
    79.     movwf TBLPTRU
    80.     movlw high msg1
    81.     movwf TBLPTRH
    82.     movlw low msg1
    83.     movwf TBLPTRL
    84.     movlw 0x80            ; LINE1
    85.     call LCD_cmd
    86.     call LCD_putstr        ; put msg 1
    87.  
    88.     movlw upper msg2        ; TBLPTR->msg2
    89.     movwf TBLPTRU
    90.     movlw high msg2
    91.     movwf TBLPTRH
    92.     movlw low msg2
    93.     movwf TBLPTRL
    94.     movlw 0xC0            ; LINE2
    95.     call LCD_cmd
    96.     call LCD_putstr        ; put msg 2
    97.  
    98. forever:
    99.     nop
    100.     goto forever
    101.  
    102.  
    103. LCD_init:
    104.     clrf LCD_cport ; p make sure the LCD control port is low
    105.     bcf LCD_E_DIR ; configure control lines
    106.     bcf LCD_RW_DIR ; directions to output
    107.     bcf LCD_RS_DIR ; "
    108.     ;
    109.     ; missing a lot of LCD init here, see 44780 datasheet
    110.     ; also need delays between commands until can poll 'busy'
    111.     ;
    112.     movlw 0x38        ; g py configure display to 2 x 20
    113.     call LCD_cmd    ; send command to LCD
    114.     movlw 0x0F        ; turn on display and cursor
    115.     call LCD_cmd    ; "
    116.     movlw 0x14        ; shift cursor right
    117.     call LCD_cmd    ; "
    118.     movlw 0x01        ; clear cursor and return to home position
    119.     call LCD_cmd    ; "
    120.     movlw 0x02        ; clear cursor and return to home position
    121.     call LCD_cmd    ; "
    122.     return
    123.  
    124. LCD_cmd:
    125.     pushr WREG        ; save WREG in the stack ; save WREG in the stack
    126.     call LCD_rdy    ; wait until LCD is ready
    127.     bcf LCD_RS        ; select IR register
    128.     bcf LCD_RW        ; Set write mode
    129.     bsf LCD_E        ; Se up o c oc d Setup to clock data
    130.     popr WREG        ; restore WREG
    131.     movwf LCD_DATA  ; send out the command in WREG
    132.     nop            ; small delay to lengthen E pulse
    133.     bcf LCD_E
    134.     return
    135.  
    136. LCD_rdy:
    137.     setf LCD_D_DIR  ; configure LCD data port for input
    138.     bcf LCD_RS        ; select IR register
    139.     bsf LCD_RW        ; setup to read busy flag
    140.     bsf LCD_E        ; pull LCD E-line to high
    141.     nop            ; small delay
    142.     movf LCD_DATA_READ,W,A ; read busy flag and DDRAM address
    143.     nop            ; small delay to lengthen E pulse
    144.     bcf LCD_E        ; pull LCD E-line to low
    145.     btfsc WREG,7    ; is busy flag (BF) cleared
    146.     goto LCD_rdy
    147.  
    148.     clrf LCD_D_DIR  ; configure data pins for output
    149.     return
    150.  
    151. LCD_putc:
    152.     pushr WREG        ; save WREG
    153.     call LCD_rdy    ; wait until LCD is not busy
    154.  
    155.     popr WREG        ; restore data in WREG ; restore data in WREG
    156.     send2LCD        ; write WREG content to LCD
    157.     pushr WREG        ; save WREG data
    158.     call get_DDRAM_addr ; get the DDRAM address in PRODL
    159.     movlw 0x13
    160.     cpfseq PRODL
    161.     goto chk_0x53   ; reach the end of first line
    162.  
    163.     movlw 0xC0
    164.     call LCD_cmd    ; set DDRAM address to 0x40
    165.     call LCD_rdy
    166.     popr WREG
    167.     send2LCD        ; re output the same character
    168.     return
    169.  
    170. chk_0x53:
    171.     movlw 0x53
    172.     cpfseq PRODL
    173.     goto chk_0x27
    174.  
    175.     ; reach the end of the second line
    176.     movlw 0x94        ; ( set DDRAM address to 0x14 (start of
    177.     call LCD_cmd    ; 3rd row)
    178.     call LCD_rdy
    179.     popr WREG
    180.     send2LCD
    181.     return
    182.  
    183.     ; reach the end of the third line
    184. chk_0x27:
    185.     movlw 0x27
    186.     cpfseq PRODL
    187.     return
    188.  
    189.     movlw 0xD4 ; set DDRAM address to 0x54 (start of
    190.     call LCD_cmd ; 4th row)
    191.     call LCD_rdy
    192.     popr WREG
    193.     send2LCD
    194.     return
    195.  
    196. ; 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
    197. LCD_putstr:
    198.     tblrd*+        ; read a character into TABLAT
    199.     movf    TABLAT,W    ; copy to WREG
    200.     tstfsz  WREG    ; test WREG and skip if zero
    201.     bra        send_it    ; BRA shorter than goto here
    202.  
    203.     return
    204.  
    205. send_it:
    206.     call LCD_putc
    207.     goto LCD_putstr
    208.  
    209. get_DDRAM_addr:
    210.     setf LCD_D_DIR    ; confi LCD d t t f i t figure LCD data port for input
    211.     bcf LCD_RS        ; select IR register
    212.     bsf LCD_RW        ; setup to read busy flag
    213.     bsf LCD_E        ; pull LCD E-line to high
    214.     nop            ; add ll d l dd a small delay
    215.     movf LCD_DATA,W    ; read busy flag and DDRAM address
    216.     nop            ; small delay to length E pulse
    217.     bcf LCD_E        ; pull LCD E-line to low
    218.     movwf PRODL
    219.     return
    220.  
    221. msg1 data "HELLO"    ; lets keep it nice, this is a family site
    222. msg2 data "GIMME THE 40"
    223.     end
    224.  
    225.  
    226.  
    Carry on.
     
    Last edited: Sep 3, 2015
  16. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    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.
     
  17. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    And the msg1 was a line from The Town; my favorite movie.
     
  18. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    Hola Mcaudle,

    I could post code that is working right now on my bench. Interested?
     
  19. Mcaudle870

    Thread Starter New Member

    Aug 13, 2015
    12
    0
    Yea absolutely.
     
  20. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    762
    OK. Give me few hours and time to sleep.
     
Loading...