PIC 18f4550 Basic 2x8 LCD Character Display Help

Discussion in 'Embedded Systems and Microcontrollers' started by Micro9000, Oct 8, 2011.

  1. Micro9000

    Thread Starter New Member

    Sep 22, 2009
    10
    0
    This is my first time programming for a character LCD, and I am having a bit of trouble getting this 2x8 LCD to work with my PIC18f4550. I am using MPLAB v8.76 with C18 Lite compiler (for programming in C).

    I will be programming the LCD in 4-bits at a time (Info about LCD below).

    Here is a diagram of the connections:

    [​IMG]

    The connections are as follows:
    RB5 --> RS
    RB4 --> R/W
    RB3 --> DB7
    RB2 --> DB6
    RB1 --> DB5
    RB0 --> DB4
    VDD --> VDD
    VSS --> VSS
    RD7 --> E

    Here is the data sheet for the LCD I am using: http://www.newhavendisplay.com/specs/NHD-0208BZ-RN-GBW.pdf

    It is a 2x8 14pin (no backlight) character LCD display

    The data sheet provides me with the following code layout for the initialization:

    Code ( (Unknown Language)):
    1.  
    2. 4-bit Initialization:
    3. /**********************************************************/
    4. void command(char i)
    5. {
    6.       P1 = i;                       //put data on output Port
    7.       D_I =0;                       //D/I=LOW : send instruction
    8.       R_W =0;                       //R/W=LOW : Write      
    9.       Nybble();                     //Send lower 4 bits
    10.       i = i<<4;                     //Shift over by 4 bits
    11.       P1 = i;                       //put data on output Port
    12.       Nybble();                     //Send upper 4 bits
    13. }
    14. /**********************************************************/
    15. void write(char i)
    16. {
    17.       P1 = i;                       //put data on output Port
    18.       D_I =1;                       //D/I=HIGH : send data
    19.       R_W =0;                       //R/W=LOW : Write    
    20.       Nybble();                     //Clock lower 4 bits
    21.       i = i<<4;                     //Shift over by 4 bits
    22.       P1 = i;                       //put data on output Port
    23.       Nybble();                     //Clock upper 4 bits
    24. }
    25. /**********************************************************/
    26. void Nybble()
    27. {
    28.       E = 1;
    29.       Delay(1);                     //enable pulse width  >= 300ns
    30.       E = 0;                        //Clock enable: falling edge
    31. }
    32. /**********************************************************/
    33. void init()
    34. {
    35.       P1 = 0;
    36.       P3 = 0;
    37.       Delay(100);                   //Wait >15 msec after power is applied
    38.       P1 = 0x30;                    //put 0x30 on the output port
    39.       Delay(30);                    //must wait 5ms, busy flag not available
    40.       Nybble();                     //command 0x30 = Wake up  
    41.       Delay(10);                    //must wait 160us, busy flag not available
    42.       Nybble();                     //command 0x30 = Wake up #2
    43.       Delay(10);                    //must wait 160us, busy flag not available
    44.       Nybble();                     //command 0x30 = Wake up #3
    45.       Delay(10);                    //can check busy flag now instead of delay
    46.       P1= 0x20;                     //put 0x20 on the output port
    47.       Nybble();                     //Function set: 4-bit interface
    48.       command(0x28);                //Function set: 4-bit/2-line
    49.       command(0x10);                //Set cursor
    50.       command(0x0F);                //Display ON; Blinking cursor
    51.       command(0x06);                //Entry Mode set
    52. }
    53. /**********************************************************/
    I have modified the above code to:

    Code ( (Unknown Language)):
    1. //turns watch dog timer off, turn low voltage programming off
    2. #pragma config WDT=OFF, LVP=OFF, DEBUG=ON
    3.  
    4. //Internal oscillator, port function on RA6, EC used by USB
    5. #pragma config FOSC = INTOSCIO_EC
    6. #include <p18f4550.h>
    7. #include <delays.h>
    8.  
    9. //LCD (Port B and 1 pin PortD)
    10. #define  RS  LATBbits.LATB5 //Define LCD pinout RS
    11. #define  R_W LATBbits.LATB4 //Define LCD pinout R/W
    12. #define  DB7 LATBbits.LATB3 //Define LCD pinout DB7
    13. #define  DB6 LATBbits.LATB2 //Define LCD pinout DB6
    14. #define  DB5 LATBbits.LATB1 //Define LCD pinout DB5
    15. #define  DB4 LATBbits.LATB0 //Define LCD pinout DB4
    16. #define  E   LATDbits.LATD7 //Define LCD pinout E
    17.  
    18. void command(char);
    19. void write(char);
    20. void Nybble(void);
    21. void init(void);
    22.  
    23. void main()
    24. {
    25.  
    26.     while(!OSCCONbits.IOFS);      //wait for osc stable
    27.     ADCON1 = 0x0F;                //make RA0 digital
    28.  
    29.         //data direction register all 0's mean all pins are set to output
    30.     //all 1's mean all pins are set to operate as inputs
    31.  
    32.     TRISB = 0x00;          
    33.  
    34.     //main program loop
    35.     while(1)
    36.     {
    37.  
    38.         init();
    39.         //after initialization call function for outputting character to LCD with appropriate command
    40.  
    41.        
    42.     }    
    43. }
    44.  
    45. /**********************************************************/
    46. //4-bit methods for LCD
    47. /**********************************************************/
    48. void command(char i)
    49. {
    50. //  P1 = i; //put data on output Port
    51. //  D_I =0; //D/I=LOW : send instruction
    52.    
    53.    
    54.     //send lower bits of char (masking bits)
    55.     DB7 = i&08;
    56.     DB6 = i&04;
    57.     DB5 = i&02;
    58.     DB4 = i&01;
    59.  
    60.     R_W =0; //R/W=LOW : Write
    61.     Nybble(); //Send lower 4 bits
    62.     i = i<<4; //Shift over by 4 bits
    63.  
    64.         //since sifted sending upper bits of char
    65.     DB7 = i&08;
    66.     DB6 = i&04;
    67.     DB5 = i&02;
    68.     DB4 = i&01;
    69.  
    70.     Nybble(); //Send upper 4 bits
    71. }
    72.  
    73. void write(char i)
    74. {
    75.     //P1 = i; //put data on output Port
    76.     //D_I =1; //D/I=HIGH : send data
    77.     DB7 = i&08;
    78.     DB6 = i&04;
    79.     DB5 = i&02;
    80.     DB4 = i&01;
    81.  
    82.     R_W =0; //R/W=LOW : Write
    83.     Nybble(); //Clock lower 4 bits
    84.     i = i<<4; //Shift over by 4 bits
    85. //  P1 = i; //put data on output Port
    86.     DB7 = i&08;
    87.     DB6 = i&04;
    88.     DB5 = i&02;
    89.     DB4 = i&01;
    90.  
    91.     Nybble(); //Clock upper 4 bits
    92. }
    93.  
    94. /**********************************************************/
    95. void Nybble(void)
    96. {
    97.     E = 1;
    98.     Delay1TCY(); //enable pulse width >= 300ns (used 4uS)
    99.     E = 0; //Clock enable: falling edge
    100. }
    101.  
    102. /**********************************************************/
    103. void init(void)
    104. {
    105.     //P1 = 0;
    106.     //P3 = 0;
    107.     DB7 = 0;
    108.     DB6 = 0;
    109.     DB5 = 0;
    110.     DB4 = 0;
    111.  
    112.     Delay1KTCYx(5); //Wait >15 msec after power is applied (used 20mS)
    113.     //P1 = 0x30; //put 0x30 on the output port
    114.    
    115.     DB7 = 0;
    116.     DB6 = 0;
    117.     DB5 = 1;
    118.     DB4 = 1;
    119.  
    120.    
    121.     Delay1KTCYx(1.25); //must wait 5ms, busy flag not available (used 5ms)
    122.  
    123.     Nybble(); //command 0x30 = Wake up
    124.  
    125.     Delay10TCYx(5); //must wait 160us, busy flag not available (used 160uS)
    126.  
    127.     Nybble(); //command 0x30 = Wake up #2
    128.  
    129.     Delay10TCYx(5); //must wait 160us, busy flag not available (used 160uS)
    130.  
    131.     Nybble(); //command 0x30 = Wake up #3
    132.  
    133.     Delay10TCYx(5); //can check busy flag now instead of delay
    134.  
    135.     //P1= 0x20; //put 0x20 on the output port
    136.     DB7 = 0;
    137.     DB6 = 0;
    138.     DB5 = 1;
    139.     DB4 = 0;
    140.  
    141.     Nybble(); //Function set: 4-bit interface
    142.     command(0x28); //Function set: 4-bit/2-line
    143.     command(0x10); //Set cursor
    144.     command(0x0F); //Display ON; Blinking cursor
    145.     command(0x06); //Entry Mode set
    146. }
    147. /**********************************************************/
    148. //End methods for LCD
    149. /**********************************************************/
    I have looked at a few other tutorials, but I am still left a bit confused. Even without looking at my modified code how should those functions look like when translated into code the PIC18F can work with and understand?

    I understand the delay part, but I am a bit confused about the lines involving "P1 and D_I" commands. I just want to get to the point where I can at least display the letter "A" anywhere. Any help or suggestions is greatly appreciated. Thanks in advance for your time and assistance.
     
    Last edited: Oct 8, 2011
  2. CraigHB

    Member

    Aug 12, 2011
    127
    15
    You probably wouldn't be able to just plug in the code provided by the data sheet. They tend to leave out the delays required to keep comm timings within spec.

    I believe D_1 and P1 are simply referring to the Register Select and Enable pins. Would be nice if they just used RS and E.

    I'd have to simulate your code to troubleshoot it so it's I can't say off-hand what your problem is. Maybe someone else can tell you exactly.

    One time, I was pulling my hair out over an LCD for some time until I realized I forgot the command to turn the damn thing on. Another time, I couldn't figure out why I was getting a partially blank screen. It was because I was failing to set the LCD's DDRAM address (or cursor) properly and the controller had unused DDRAM.

    Timings can be a source of trouble. What I typically do is simulate my LCD routines and then watch the control bits in the simulator to verify timings. You can run comms too fast for LCDs even when using the MCU's default speed.

    I've generally found the data sheets provided by Newhaven to be lacking. I use their displays a lot. You'll need the data sheet for your LCD controller to verify timings are within spec. Looks like the link is no good in the Newhaven data sheet, but I usually can find them with Google. The controller specification will also give you more and better information on LCD initialization.

    Well, sorry I can't tell you exactly the problem, but hopefully that will give you some ideas.
     
  3. Micro9000

    Thread Starter New Member

    Sep 22, 2009
    10
    0
    I found the data sheet for the controllers:

    SPLC80D
    http://www.newhavendisplay.com/app_notes/SPLC780D.pdf
    (Timing info for 4-bit operation on pg. 11)

    ST7066U:
    http://www.newhavendisplay.com/app_notes/ST7066U.pdf
    (pg. 25/42 shows more timing information for 4-bit operation mode)

    I also updated my code.

    Code ( (Unknown Language)):
    1.  
    2. //turns watch dog timer off, turn low voltage programming off
    3. #pragma config WDT=OFF, LVP=OFF, DEBUG=ON
    4.  
    5. //Internal oscillator, port function on RA6, EC used by USB
    6. #pragma config FOSC = INTOSCIO_EC
    7. #include <p18f4550.h>
    8. #include <delays.h>
    9.  
    10. //Power LED (PortA)
    11. #define LEDPin LATAbits.LATA0 //Define LEDPin as PORT A Pin 1
    12.  
    13. //LCD (Port B and 1 pin PortD)
    14. #define  RS  LATBbits.LATB5 //Define LCD pinout RS
    15. #define  R_W LATBbits.LATB4 //Define LCD pinout R/W
    16. #define  DB7 LATBbits.LATB3 //Define LCD pinout DB7
    17. #define  DB6 LATBbits.LATB2 //Define LCD pinout DB6
    18. #define  DB5 LATBbits.LATB1 //Define LCD pinout DB5
    19. #define  DB4 LATBbits.LATB0 //Define LCD pinout DB4
    20. #define  E   LATDbits.LATD7 //Define LCD pinout E
    21.  
    22. void command(char);
    23. void write(char);
    24. void Nybble(void);
    25. void init(void);
    26.  
    27. void main()
    28. {
    29.     unsigned int count=1;
    30.     while(!OSCCONbits.IOFS);      //wait for osc stable
    31.     ADCON1 = 0x0F;                //make RA0 digital
    32.  
    33.     //data direction register all 0's mean all pins are set to output
    34.     //all 1's mean all pins are set to operate as inputs
    35.     TRISA = 0x00;  
    36.     TRISB = 0x00;
    37.     TRISD = 0x00;          
    38.  
    39.     //main program loop
    40.     while(1)
    41.     {      
    42.         if(count == 1)
    43.         {
    44.             LEDPin = 1; //output high Power LED indicator
    45.             init();
    46.             count++;
    47.             command(0x01); //command to clear screen
    48.             wrtie(0x30); //display number 0 hopefully in first postion top row
    49.         }
    50.         break;
    51.  
    52.        
    53.     }    
    54. }
    55.  
    56. /**********************************************************/
    57. //4-bit methods for LCD
    58. /**********************************************************/
    59. void command(char i)
    60. {
    61. //  P1 = i; //put data on output Port
    62. //  D_I =0; //D/I=LOW : send instruction
    63.  
    64.     R_W =0;   //R/W=LOW : Write
    65.  
    66.     DB7 = ((i>>7)&'1');
    67.     DB6 = ((i>>6)&'1');
    68.     DB5 = ((i>>5)&'1');
    69.     DB4 = ((i>>4)&'1');
    70.  
    71.     R_W =0;   //R/W=LOW : Write
    72.     Nybble(); //Send lower 4 bits
    73.  
    74.     DB7 = ((i>>3)&'1');
    75.     DB6 = ((i>>2)&'1');
    76.     DB5 = ((i>>1)&'1');
    77.     DB4 = i&1;
    78.  
    79.  
    80.     Nybble(); //Send upper 4 bits
    81. }
    82.  
    83. void write(char i)
    84. {
    85.     //P1 = i; //put data on output Port
    86.     //D_I =1; //D/I=HIGH : send data
    87.     DB7 = ((i>>7)&'1');
    88.     DB6 = ((i>>6)&'1');
    89.     DB5 = ((i>>5)&'1');
    90.     DB4 = ((i>>4)&'1');
    91.  
    92.     R_W =0; //R/W=LOW : Write
    93.     Nybble(); //Clock lower 4 bits
    94.  
    95. //  P1 = i; //put data on output Port
    96.     DB7 = ((i>>3)&'1');
    97.     DB6 = ((i>>2)&'1');
    98.     DB5 = ((i>>1)&'1');
    99.     DB4 = i&'1';
    100.  
    101.     Nybble(); //Clock upper 4 bits
    102. }
    103.  
    104. /**********************************************************/
    105. void Nybble(void)
    106. {
    107.     E = 1;
    108.     Delay1TCY(); //enable pulse width >= 300ns (used 4uS)
    109.     E = 0; //Clock enable: falling edge
    110. }
    111.  
    112. /**********************************************************/
    113. void init(void)
    114. {
    115.     //P1 = 0;
    116.     //P3 = 0;
    117.     command(0x00);
    118.  
    119.     Delay1KTCYx(5); //Wait >15 msec after power is applied (used 20mS)
    120.  
    121.     //P1 = 0x30; //put 0x30 on the output port
    122.    
    123.         DB7 = 0;
    124.         DB6 = 0;
    125.         DB5 = 1;
    126.         DB4 = 1;
    127.  
    128.    
    129.     Delay1KTCYx(1.25);     //must wait 5ms, busy flag not available (used 5ms)
    130.     Nybble();           //command 0x30 = Wake up
    131.     Delay10TCYx(4);             //must wait 160us, busy flag not available (used 160uS)
    132.     Nybble();           //command 0x30 = Wake up #2
    133.     Delay10TCYx(4);             //must wait 160us, busy flag not available (used 160uS)
    134.     Nybble();           //command 0x30 = Wake up #3
    135.     Delay10TCYx(4);     //can check busy flag now instead of delay
    136.     //P1= 0x20;         //put 0x20 on the output port
    137.  
    138.         DB7 = 0;
    139.         DB6 = 0;
    140.         DB5 = 1;
    141.         DB4 = 0;
    142.  
    143.     Nybble();           //Function set: 4-bit interface
    144.     command(0x28); //Function set: 4-bit/2-line
    145.     command(0x10); //Set cursor
    146.     command(0x0F); //Display ON; Blinking cursor
    147.     command(0x06); //Entry Mode set
    148. }
    149. /**********************************************************/
    150. //End methods for LCD
    151. /**********************************************************/
    So I am using the following to handle the output for the write and command function by masking the bits:
    //Upper bits:
    ...
    DB7 = ((i>>7)&'1');
    DB6 = ((i>>6)&'1');
    DB5 = ((i>>5)&'1');
    DB4 = ((i>>4)&'1');
    ...
    //Lower bits:
    DB7 = ((i>>3)&'1');
    DB6 = ((i>>2)&'1');
    DB5 = ((i>>1)&'1');
    DB4 = i&'1';
    ...

    So far, when I run the program the entire top row of the LCD has black squares, while the bottom row shows nothing. If you look in the main portion of the updated code I am trying to clear the screen and output the number 0 after the initialization. But, it doesn't work and the screen still has black squares across the top row :( .

    I believe I have the delays correct, at least as far as the correct value from the datasheet. The 18F should be oscillating at 1MHz. I checked it with the LED I have connected by using the following code:

    Code ( (Unknown Language)):
    1.  
    2. #define LEDPin LATAbits.LATA0 //Define LEDPin as PORT A Pin 1
    3. ...
    4. while(1)
    5. {
    6.      //Delay 250K cycles (1 second at 1MHz since each instruction takes 4 cycles)
    7.      Delay10KTCYx(25);
    8.      LEDPin = ~LEDPin; //constantly inverts output
    9. }
    10. ...
    11.  
    I've tried switching the order in which I feed the upper and lower nibbles into the LCD. But, it doesn't appear to have an effect on the display. Only changing the bottoms commands in the initialize function makes the LCD behave oddly.

    ___________
    EDIT:

    Here is a video showing the problem:

    http://www.youtube.com/watch?v=q4QzkPm7BR0

    (Sometimes it just stays with the black rows across the top. So, I can't say for sure what the problem is)
     
    Last edited: Oct 9, 2011
  4. nigelwright7557

    Senior Member

    May 10, 2008
    487
    71
    You need to follow the datasheet code example closely.
    The LCD commands need certain delays after them or the LCD wont reset.
     
Loading...