PIC16F1459 & LCD PC1602LRS config

Discussion in 'Embedded Systems and Microcontrollers' started by Jswale, Jul 16, 2015.

  1. Jswale

    Thread Starter Member

    Jun 30, 2015
    Hi All,

    I am trying to wire up and program an LCD with a PIC16F1459. I can not see any errors in the code or the wiring.

    Any help would be appreciated.

    The Header File;

    Code (Text):
    1. //E on RC5, RS on RC4, D4-7 on RC0-RC3
    2. #include <pic16f1459.h>
    3. #include <xc.h>
    4. #define _XTAL_FREQ 31250
    6. #define CLEAR_SCREEN    0b00000001
    7. #define FOUR_BIT        0b00101100
    8. #define LINES_5X8        0b00111000
    9. #define CURSOR_BLINK    0b00001111
    10. #define CURSOR_RIGHT    0b00000110
    11. #define DISPLAY_ON        0b00001111
    12. #define DISPLAY_OFF        0b00001011
    13. #define CURSOR_ON        0b00001111
    14. #define CURSOR_OFF        0b00001101
    15. #define BLINK_ON        0b00001111
    16. #define BLINK_OFF        0b00001110
    17. #define DATA_PORT        PORTC
    18. #define RS_PIN            PORTCbits.RC4
    19. #define E_PIN            PORTCbits.RC5
    21. void Delay2ms()
    22. {
    23. __delay_ms(2.5);
    24. }
    26. void SetAddr(unsigned char DDaddr)    //Sets Character Address of Screen
    27. {
    28. DATA_PORT &=0xf0;        //Write upper nibble
    29. DATA_PORT |=((DDaddr|0b10000000>>4)&0x0f);
    30. RS_PIN=0;                //Set control bit
    31. Delay2ms();
    32. E_PIN=1;                //Clock the cmd and address in
    33. Delay2ms();
    34. E_PIN=0;
    35. DATA_PORT&=0xf0;        //Write lower nibble
    36. DATA_PORT|=(DDaddr&0x0f);
    37. Delay2ms();
    38. E_PIN=1;                //Clock the cmd and address in
    39. Delay2ms();
    40. E_PIN=0;
    41. }
    43. void WriteCmd(unsigned char cmd)    //Writing a Command to the Display
    44. {
    45. DATA_PORT&=0xf0;
    46. DATA_PORT|=(cmd>>4)&0x0f;
    47. RS_PIN=0;                //Set control signals for command
    48. Delay2ms();
    49. E_PIN=1;                //Clock Command in
    50. Delay2ms();
    51. E_PIN=0;
    53. DATA_PORT&=0xf0;
    54. DATA_PORT|=cmd&0xf0;
    55. Delay2ms();
    56. E_PIN=1;                //Clock Command in
    57. Delay2ms();
    58. E_PIN=0;
    59. }
    61. void WriteChar(char data)    //Writing a character to the screen
    62. {
    63. DATA_PORT&=0xf0;
    64. DATA_PORT|=((data>>4)&0x0f);
    65. RS_PIN=1;                //Set Control bits
    66. Delay2ms();
    67. E_PIN=1;                //Clock nibble into LCD
    68. Delay2ms();
    69. E_PIN=0;
    70. DATA_PORT&=0xf0;        //Lower interface nibble
    71. DATA_PORT|=(data&0x0f);
    72. Delay2ms();
    73. E_PIN=1;                //Clock nibble into LCD
    74. Delay2ms();
    75. E_PIN=0;
    76. }
    78. void WriteString(const char *buffer)    //'rom' <Taken Out     Writing a string of characters to the screen
    79. {
    80. while(*buffer)
    81. {
    82. Delay2ms();
    83. WriteChar( *buffer);
    84. buffer++;
    85. }
    86. return;
    87. }
    The Source File;

    Code (Text):
    1. #include <pic16f1459.h>
    2. #include <xc.h>
    3. #include <LCD.h>
    6. void main (void)
    7. {
    8.         //Configures display
    9.         //SET UP
    10.         WriteCmd(0x02);                            //Sets 4 bit Display
    11.         WriteCmd(FOUR_BIT & LINES_5X8);
    12.         WriteCmd(CURSOR_BLINK);  
    13.         WriteCmd(CURSOR_RIGHT);
    15.         TRISC=0b00000000;
    16.         PORTC=0b00000000;
    17.         ANSELC=0b00000000;
    20.     SetAddr (0x82);
    21.     WriteChar('H');
    22.     WriteChar('E');
    23.     WriteChar('L');
    24.     WriteChar('L');
    25.     WriteChar('O');
    26.     while(1);
    28. }
    The wiring has the following connections:
    Vo----------1.2K resistor to GND

  2. nerdegutta


    Dec 15, 2009
    Does it compile without errors?

    How did you come up with that _XTAL_FREQ?

    Where/how are your configuration bits?

    What seems to be the problem?
  3. Jswale

    Thread Starter Member

    Jun 30, 2015
    It compiles without errors and I can see on a scope that data is being transferred via the Data line.

    The _XTAL_FREQ is set within the datasheet because the internal Oscillator is used and is 31.25KHz.

    The configuration bits are set up in MPLAB and not within the code, I have pasted a screenshot of them below.

    The problem is I see nothing on the LCD at all.

  4. JohnInTX


    Jun 26, 2012
    It looks like you are not initializing the LCD correctly (assuming functions like WriteCMD etc. are working).
    First, you need to wait 40ms or more for power up.
    Next you have to send 3h - just the 4 bits on DB7-DB4, not with WriteCMD- three times with delays between them to set up the 4 bit interface before sending the various other configurations.
    It wouldn't hurt to have a pot on the contrast pin to ensure that you can set the contrast to something usable. Different displays need different settings on contrast.

    You should always include the CONFIG bits in the source code.

    See pp46 of the attached datasheet.

    Good luck.
    Last edited: Jul 16, 2015
  5. Jswale

    Thread Starter Member

    Jun 30, 2015
    Added in the following prior to the rest of the setup:

    Code (Text):
    1. void main (void)
    2. {
    3.         //Configures display
    4.         //SET UP
    5.         __delay_ms(40);
    6.         for(int count=0; count<4; count++)
    7. {        WriteCmd(0x03);
    8.         __delay_ms(10);
    9. }
    With no luck.

    Where did you find all this info on Config?

  6. Jswale

    Thread Starter Member

    Jun 30, 2015
    Sending 3h using Writechar?

    I find in general LCD datasheets are very useless.

    Also, in terms of the config bits, point taken on board,

    In terms of the POT on the contrast pin, I have connect Vo to both VSS and VDD with no change. I atleast expected to see Blocks when connected to VSS (GND)

  7. JohnInTX


    Jun 26, 2012
    Your WriteCmd writes an 8 bit value in two 4 bit chunks as it should for a 4 bit interface. The 44780 LCD controller requires a specific init sequence that is different. It wants 0011 on DB7-4, RS=0 written 3 times with delays between them. Then and ONLY then can you use your 'WriteCmd/WriteChar.

    In the 'useless' datasheet on pp46.

    Some (not many) LCDs require the contrast voltage to be some negative value. That item would be in the LCDs (not the Hitachi) datasheet. I'd RTFM and use a pot. The contrast pin is the supply voltage to the LCD glass. GND may be not enough and Vcc too much.

    EDIT: FWIW I've attached the datasheet for the ST7066U LCD controller. It's compatible with the Hitachi 44780 with the same init requirements. The datasheet for your? display is attached as well. It IS a bit light on info but does refer you to the datasheet for the ST7066 controller.

    Good luck.
    Last edited: Jul 16, 2015
  8. Jswale

    Thread Starter Member

    Jun 30, 2015
    Gone for a new tactic,

    But the once programmed, all output pins on PORTC are LOW constantly, everything seems fine in the simulation.

    Any advice?

    Code (Text):
    1. #include <pic16f1459.h>
    2. #include <xc.h>
    3. #define _XTAL_FREQ 31250
    6. //The pins used are same as explained earlier
    7. #define lcd_port    PORTC
    8. //LCD Registers addresses
    9. #define LCD_EN      0x20
    10. #define LCD_RS      0x10
    11. void lcd_cmd (char cmd)
    12. {
    13.     lcd_port = ((cmd >>4) & 0x0F)|LCD_EN;
    14.     lcd_port = ((cmd >>4) & 0x0F);
    15.      lcd_port = (cmd & 0x0F)|LCD_EN;
    16.     lcd_port = (cmd & 0x0F);
    17.      __delay_us(200);
    18.     __delay_us(200);
    19. }
    21. void lcd_reset()
    22. {
    23.     lcd_port = 0xFF;
    24.     __delay_ms(20);
    25.     lcd_port = 0x03+LCD_EN;
    26.     lcd_port = 0x03;
    27.     __delay_ms(10);
    28.     lcd_port = 0x03+LCD_EN;
    29.     lcd_port = 0x03;
    30.     __delay_ms(1);
    31.     lcd_port = 0x03+LCD_EN;
    32.     lcd_port = 0x03;
    33.     __delay_ms(1);
    34.     lcd_port = 0x02+LCD_EN;
    35.     lcd_port = 0x02;
    36.     __delay_ms(1);
    37. }
    38. void lcd_init ()
    39. {
    40.     lcd_reset();         // Call LCD reset
    41.     lcd_cmd(0x28);       // 4-bit mode - 2 line - 5x7 font.
    42.     lcd_cmd(0x0C);       // Display no cursor - no blink.
    43.     lcd_cmd(0x06);       // Automatic Increment - No Display shift.
    44.     lcd_cmd(0x80);       // Address DDRAM with 0 offset 80h.
    45. }
    46. void lcd_data (unsigned char dat)
    47. {
    48.     lcd_port = (((dat >>4) & 0x0F)|LCD_EN|LCD_RS);
    49.     lcd_port = (((dat >>4) & 0x0F)|LCD_RS);
    50.     lcd_port = ((dat & 0x0F)|LCD_EN|LCD_RS);
    51.     lcd_port = ((dat & 0x0F)|LCD_RS);
    52.      __delay_us(200);
    53.     __delay_us(200);
    54. }
    Code (Text):
    1. #include <pic16f1459.h>
    2. #include <xc.h>
    3. #include <LCD.h>
    6. void main ()
    7. {
    8. TRISC=0b00000000;
    9. PORTC=0b00000000;
    10. ANSELC=0b00000000;
    13. lcd_init();
    14. lcd_data('H');
    15. lcd_data('E');
    16. lcd_data('L');
    17. lcd_data('L');
    18. lcd_data('O');
    20. }

    Still using PIC16F1459 with the same CONFIG bits.

  9. JohnInTX


    Jun 26, 2012
    @Jswale Several things:
    I'm not usually a fan of blowing something up and starting with something completely new.. If the original design is valid, its usually better to fix that. But now that it is blown up..

    You need to visit OSCCON to set the oscillator how you want it. The default is not 32KHz. I would write a simple program that initialized the PIC and flash an output at 1 sec intervals or temporarily output the clock for scoping. If you get that, you know you have the oscillator etc. running.
    I don't know why the port is 'stuck at 0' unless its because of the continuous looping, or the PIC is not running.
    Always put the config bits in the code. In MPLABX, just open a Configuration Bits window, set it up and click Generate Source Code to output. Copy/paste that into your source. We can't know what you have otherwise.
    Writes go to LATx, reads from PORTx

    Header files should not contain executable code. If you want to split code into multiple files, do it and provide function prototypes in header files.
    With MPLABX all you need to do is include xc.h and specify the PIC in your project. You don't need to provide a Pxxx.h header.
    You DO need to specify in the source comments which PIC you are using so that others can build your project.
    When you have something that you do often i.e. strobe E on the LCD, make it a function so that its maintainable.
    Comments, comments, comments.
    After your code executes, it immediately loops and re-inits everything. You won't see much that way.

    Your method of writing to the LCD i.e. lcd_port = ((cmd >>4) & 0x0F)|LCD_EN; is imaginative but problematic. The LCD requires that DB7-4 be stable before E is strobed. Doing it all at once isn't valid and may not work.
    Its more complex and error prone to restate calculations to change one bit. Put the data on the port then strobe it.
    Add a power up delay to the code to give the LCD time to come up.

    I've scratched together some code that approximates how I would do it. Its more structured and (I think) easier to follow. It looks right in MPSIM but you'll have to see on the display. Delays are approximate and should be tuned when it works.

    Good luck!

    Code (C):
    2. #define _XTAL_FREQ 31250
    4. // PIC16F1459 Configuration Bit Settings
    6. // 'C' source line config statements
    8. #include <xc.h>
    10. // #pragma config statements should precede project file includes.
    11. // Use project enums instead of #define for ON and OFF.
    13. // CONFIG1
    14. #pragma config FOSC = INTOSC    // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
    15. #pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
    16. #pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
    17. #pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
    18. #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
    19. #pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
    20. #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
    21. #pragma config IESO = ON        // Internal/External Switchover Mode (Internal/External Switchover Mode is enabled)
    22. #pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
    24. // CONFIG2
    25. #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
    26. #pragma config CPUDIV = CLKDIV6 // CPU System Clock Selection Bit (CPU system clock divided by 6)
    27. #pragma config USBLSCLK = 48MHz // USB Low SPeed Clock Selection bit (System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8.)
    28. #pragma config PLLMULT = 3x     // PLL Multipler Selection Bit (3x Output Frequency Selected)
    29. #pragma config PLLEN = ENABLED  // PLL Enable Bit (3x or 4x PLL Enabled)
    30. #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
    31. #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
    32. #pragma config LPBOR = OFF      // Low-Power Brown Out Reset (Low-Power BOR is disabled)
    33. #pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
    35. //The pins used are same as explained earlier - Lets explain them NOW
    36. #define lcd_port  LATC        // write to LAT, read from PORT
    37. // DB7-DB4 are RB3-RB0 respectively
    39. #define LCD_RSout LATC4  // LCD RS: 0 = command, 1=data
    40. #define LCD_ENout LATC5 // LCD E(nable), active high
    41. // LCD R/W is tied to 'WRITE'
    43. //--------------------- STROBE LCD ---------------------------
    44. // Pulses E line on LCD to write
    45. int strobeLCD(void)
    46. {
    47. LCD_ENout = 1;
    48. __delay_us(1);
    49. LCD_ENout = 0;
    50. }
    52. //--------------------- WRITE 8 BIT DATA TO LCD  -----------------
    53. // Assumes LCD is ready and RS is set to correct value
    55. void writeLCD(unsigned char dat)
    56. {
    57.     lcd_port = ((dat >>4) & 0x0F);  // write upper nibble
    58.     strobeLCD();
    60.     lcd_port = (dat & 0x0F);        // write lower nibble
    61.     strobeLCD();
    63.     __delay_us(200);                // wait for display to process
    64. }
    66. //-------------------- WRITE LCD COMMAND  --------------------
    67. // Assumes E is low and display is NOT busy
    68. void lcd_cmd (unsigned char cmd)
    69. {
    70.     LCD_RSout = 0;       // select command register
    71.     writeLCD(cmd);
    72. }
    74. //---------------------- WRITE LCD DATA  --------------------------
    76. void lcd_data (unsigned char dat)
    77. {
    78.     LCD_RSout = 1;       // select data register
    79.     writeLCD(dat);
    80. }
    82. //-------------------- RESET/CONFIGURE LCD  -------------------------
    83. // Delays are generous, trim when able
    84. void lcd_reset()
    85. {
    86.     lcd_port = 0x03;        // direct data to LCD DB7-4
    87.     LCD_RSout = 0;
    89.     strobeLCD();        // write 3h, wait 10ms
    90.     __delay_ms(10);
    92.     strobeLCD();        // write 3h, wait..
    93.     __delay_ms(10);
    95.      strobeLCD();       // write 3h
    96.     __delay_ms(10);
    97. }
    99. //------------------------ INIT AND CONFIGURE LCD  ---------------------
    100. void lcd_init ()
    101. {
    102.     lcd_reset();         // LCD reset
    103.     lcd_cmd(0x28);       // Funciton Set: 4-bit mode - 2 line - 5x7 font.
    104.     lcd_cmd(0x0C);       // Display no cursor - no blink.
    105.     lcd_cmd(0x06);       // Automatic Increment - No Display shift.
    106.     lcd_cmd(0x80);       // Address DDRAM with 0 offset 80h.
    107. }
    109. //======================= MAIN =====================================
    110. void main ()
    111. {
    112.     TRISC=0b00000000;
    113.     PORTC=0b00000000;
    114.     ANSELC=0b00000000;
    116.     // what about the other ports?????
    118.     __delay_ms(300);         // power up delay for LCD
    120.     lcd_init();
    121.     lcd_data('H');
    122.     lcd_data('E');
    123.     lcd_data('L');
    124.     lcd_data('L');
    125.     lcd_data('O');
    127.     while(1);       // wait forever
    128. }
    Last edited: Jul 19, 2015
  10. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    The PIC sounds like it is bricked or that it has no functions. Check that it is truly running by wiggling any and all ports. This is oft due to the oscillator not running, either hardware or software (config bits). If your PIC has an internal oscillator use that at first as the hardware problems are eliminated.

    Do read the very useful data sheets on your LCD display to learn what and where some very vital delays are required. It will simply not work without them. And do make sure you have that pot installed.
    Last edited: Jul 20, 2015
  11. Jswale

    Thread Starter Member

    Jun 30, 2015
    Why would the other ports need setting up when they are not used?

    JohnInTX, your code is very easy to follow and makes me rethink my approach.

    EDIT: Tried the code in the simulator and all looks to work fine. However, when transferred to the circuit all pins on the PIC read 0 and I have tried multiple PICs

    EDIT: I think there are problems with the internal oscillator because I have changed the way the oscillator is selected by setting the SCS bits in the OSCCON register to select the internal oscillator and the frequency to be 31KHZ (OSCCON=0Ah).
    I now have outputs on my PIC pins! However at this moment no characters on my display

    Last edited: Jul 20, 2015
  12. Jswale

    Thread Starter Member

    Jun 30, 2015
    I'm looking at the enable port with a DMM and on the scope and its at a steady 0.17V, seems strange when it should be changing between high (5V) and low (0V).

    EDIT: Steady pin values;

    EN-------> 0.16V
    RS-------> 0.15V
    DB4-----> 1.39V
    DB5-----> 1.29V
    DB6-----> 0.47V
    DB7-----> 1.14V

    The datasheet states anything up to 0.6V is LOW and anything over 4.3V (assuming VDD is 5V) is HIGH.
    Last edited: Jul 20, 2015
  13. JohnInTX


    Jun 26, 2012
    I believe Vih(min) is .7 * 5Vdd = 3.5V. That's good because Voh(min) of the PIC is Vdd-.7 =4.3V. You wouldn't want to cut it that close. So.. that's OK.

    What's not OK is the voltage on DB4,5, and 7. Those voltages are not valid logic levels. Possible causes include:
    Port not configured correctly (looks OK to me, though). Visit table 12-8 in the datasheet for alternate pin functions for PORTC and specifically disable any peripheral that uses them i.e. USART, CCP, PWM etc. Don't rely on power up defaults.
    Pin contention (two or more things driving the node). Is R/W- on the display strapped LOW? If not, the display could think it's supposed to read out data and drive the pin when the PIC is trying to).
    Bad wiring or display. Review the wiring. If OK, disconnect the display and see if the levels get better. If they do, its a bad LCD or pin contention happening.

    Did you resolve the contrast issue? You should be able to vary pin 3 on the display (Vo) and get boxes even with no data activity. If not, you don't know if what you are writing is going to display. Make sure that is happening.

    Since you have a scope, make sure the PIC clock is actually running at the speed you think it is. You can temporarily mod the code to just flash a pin at a calculated 10msec on/off and make sure you've sorted out all of the clock/osc/config stuff. The USB PICs are not trivial in this regard. Once you have the pin flasher working you know that the PIC is actually running and at the correct speed. Also, scope E and the databus to be sure that timings and levels make sense.
    Curious, why are you running the OSC so slow?

    Are you using PICkit for programming/debugging?

    Easy to follow is the goal. Most experienced programmers will agree that clear, structured code that breaks things down into smaller and smaller blocks is the way to go. For example, strobing E is done in one place. Once that works, it works for anything. Comments and formatting help as well. It helps us get a handle on what you are trying to do but more importantly, it helps you organize your thoughts towards solving your problem, will help you find errors in your thinking and if you have to revisit or reuse the code down the road will tell you were thinking when you wrote it.

    Have fun.
  14. Jswale

    Thread Starter Member

    Jun 30, 2015
    @JohnInTX Thanks for the help so far.

    I have installed a 50K POT across VDD, VSS and Vo and I power the LCD up and turn the POT to each extreme and the contrast changes like it should.

    The pins show as follows when the LCD IS connected,


    EDIT: Although the voltage on the pins is determined by the contrast pin. Everything is just always high. Perhaps an increase in the oscillator frequency?

    But nothing is displayed. I have looked on a scope when the system is powered on the values go straight to the ones above. So I cant see any changes on the scope.....this makes me think that either I'm missing it or someone is wrong because PORTC when it displays 'O' just before the while(1) loop at the end should read PORTC=0x0F.

    Last edited: Jul 21, 2015
  15. Jswale

    Thread Starter Member

    Jun 30, 2015
    I am looping 'HELLO' over and over again and have taken a photo of the Enable pin and the DB7 pin on the scope.

    This looks like this from the datasheet.... (response time.pdf).....any ideas?

    Last edited: Jul 21, 2015
  16. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    There is something seriously wrong in your wiring to get the image you see. Wrong in either lines are shorted together, or the display is powering off the input pins, or something equally as bad.

    I would start by either using an ohmmeter to check what is shorted or by removing the LCD (hope it just unplugs) and driving each line (E, RS, DB4-7) line high then back low in turn to see if any are shorted, then re-insert the display and repeat that test.

    Do check each and every line on the display for it's proper voltage. The unused DB0-3 can float but all other pins need be connected, including R/W wich should be grounded.
  17. Jswale

    Thread Starter Member

    Jun 30, 2015
    Let me draw up a diagram, re-wire my circuit + test it now and I will get back to you.


    Please see PDF of setup
    Last edited: Jul 21, 2015
  18. Jswale

    Thread Starter Member

    Jun 30, 2015
    I have done some tests and everything works as expected IF THE LCD IS REMOVED, once it is reconnected pin values become steady in between logic levels, e.g 1.38V
  19. JohnInTX


    Jun 26, 2012
    The basic circuit as shown looks OK. Are you sure no errors in wiring - especially RS? As Ernie says, no shorts between data / control pins? The scope shots look like bus contention to me.
    Is Vdd solid with the LCD load? Disconnect the backlight for now.
    By 'works as expected' does that mean solid logic levels without the intermediate steps?
    You don't have 3V3_USB (pin 17) hooked up. I don't know what that does.

    A side point, you are using different colored supply rails for Vdd/Vss. That can lead to errors. Use red for Vdd and blue for GND consistently. Its easy to make mistakes - I originally thought the display power was backwards..
  20. JohnInTX


    Jun 26, 2012
    That is not right. Is the pot wired correctly?