LCD With Shift Register

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147
Hey Everyone,

A couple weeks ago I worked with LCDs and managed to successfully utilize a 4-bit interface. Shown below!



Now I am trying to implement a shift-register (74HC595) to control it instead, saving I/O pins. For some reason my data will not effectively transfer, do any of you see bugs?

Code:

Rich (BB code):
//**************************************//
//        Program: LCD_2.C                //
//        Author: Austin Schaller            //
//        Date: January 16th, 2011        //
//**************************************//

//******************************************//
//        Configuration of Shift Register        //
//        QA = RS                                //
//        QB = EN                                //
//        QC - QF = DATA                        //
//******************************************//

#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config OSC = INTIO2
#pragma config MCLRE = ON
#pragma config DEBUG = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF

// Define all other config bits so that PICkit2 doesn't moan
#pragma config FSCM = OFF
#pragma config IESO = OFF
#pragma config STVR = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF                                                                                                                              

#include <p18F1320.h>
#include <delays.h>

// The lower 4 bits of this port will be used

#define DATA LATAbits.LATA0         // Data pins QC-QF
#define LATCH LATAbits.LATA1        // ST-CP pin of shift register
#define CLOCK LATAbits.LATA2        // SH-CP pin of shift register

// Commands for Hitachi LCD
#define CLEAR_DISPLAY    0x01
#define FOUR_BIT       0b00101111  // 4-bit Interface
#define EIGHT_BIT      0b00111111  // 8-bit Interface
#define LINE_5X7       0b00110011  // 5x7 characters, single line
#define LINE_5X10      0b00110111  // 5x10 characters
#define LINES_5X7      0b00111011  // 5x7 characters, multiple line

#define DISPLAY_ON  0b00001111  // Display on
#define DISPLAY_OFF 0b00001011  // Display off
#define CURSOR_ON   0b00001111  // Cursor on
#define CURSOR_OFF  0b00001101  // Cursor off
#define BLINK_ON    0b00001111  // Cursor Blink
#define BLINK_OFF   0b00001110  // Cursor No Blink
#define UNDERLINE_ON    0b00001111
#define UNDERLINE_OFF   0b00001101

#define INCREMENT    0b00000111    // Entry mode increment
#define DECREMENT    0b00000101    // Entry mode decrement
#define SHIFT_ON    0b00000111    // Display shift on
#define SHIFT_OFF    0b00000110    // Display shift off

void LCDInit(void);
void LCDCmd(unsigned char);
void LCDChar(unsigned char);
void shiftOut(unsigned char);
void shiftOutRS(void);
void PulseEnable(void);
void outputData(unsigned char);
// TODO: Add all prototypes

void Delay5ms(void)
{
    Delay1KTCYx(1); // Delay of 5ms
    return;
}

void Delay15ms(void)
{
    Delay1KTCYx(3); // Delay of 15ms
    return;
}

void LCDInit(void)
{

    shiftOut(0x00);        // Clear all data
    LCDCmd(FOUR_BIT);
    LCDCmd(FOUR_BIT & LINES_5X7);
    LCDCmd(INCREMENT & SHIFT_OFF);
    LCDCmd(DISPLAY_ON & BLINK_OFF & UNDERLINE_OFF & CURSOR_ON);
    LCDCmd(CLEAR_DISPLAY);
}

void LCDCmd(unsigned char c)
{
    unsigned char DataHigh, DataLow;
    
    DataHigh |= (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY ---> 0000 XXXX)
    outputData(DataHigh);        // Output upper nibble data to shift register
    
    DataLow |= (c & 0x0F);        // Retrieve lower nibble
    outputData(DataLow);        // Output lower nibble data to shift register
    PulseEnable();
    
    Delay5ms();
}

void LCDChar(unsigned char c) 
{
    unsigned char DataHigh, DataLow;
    shiftOutRS();        // Enable RS (QA) for data register mode

    DataHigh |= (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY ---> 0000 XXXX)
    outputData(DataHigh);        // Output upper nibble data to shift register
    
    PulseEnable();

    DataLow |= (c & 0x0F);        // Retrieve lower nibble
    outputData(DataLow);        // Output lower nibble data to shift register
    
    PulseEnable();
    Delay5ms();
}    

void shiftOutRS(void)        // Shift a '1' to RS (QA)
{
    CLOCK = 0;
    DATA = 1;
    CLOCK = 1;
    LATCH = 1;
    LATCH = 0;
}

void outputData(unsigned char data)
{
    if(0x80>data>0x03)        // Make sure data is within X constraints: 01XX XX11
    {
        data = (data<<2) & 0x3C;        // Shift data two places to the left, since RS and EN are on QA and QB
        shiftOut(data);        // Output data to shift register
    }
}

void shiftOut(unsigned char data)
{ 
   int i;
   for (i=7;i>=0;i--)
   { 
      CLOCK = 0;        // Clear Clock
      if (data & (1<<i))            // Make bit positive while incrementing with the counting variable 'i'
      {
         DATA = 1; 
      }
      else 
      {
         DATA = 0; 
      }
      CLOCK = 1;        // Delineate between bits through clock transitions
   } 
   LATCH = 1;        // Latch data to execute data
   LATCH = 0; 
}

void PulseEnable()
{
    shiftOut(0x02);
    Delay5ms();
    shiftOut(0x00);
    return;
}

void main(void)
{
    OSCCON = 0x52;  // Set internal oscillator at 2 MHz
    ADCON0 = 0x00;  // Disable internal ADC
    ADCON1 = 0x7F;  // Set all PORTB to digital I/O
    INTCON2bits.RBPU = 0;
    TRISA = 0xF8;

    LCDInit();
    Delay15ms();
    
    LCDChar('I');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('m');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('l');
    LCDChar('i');
    LCDChar('v');
    LCDChar('e');
    LCDChar('!');
    LCDChar(' ');
    LCDChar('=');
    LCDChar(')');
        
    while(1);
}
Let me know if you have any questions!

Thanks,

Austin
 

Attachments

spinnaker

Joined Oct 29, 2009
7,830
Hey Everyone,

A couple weeks ago I worked with LCDs and managed to successfully utilize a 4-bit interface. Shown below!



Now I am trying to implement a shift-register (74HC595) to control it instead, saving I/O pins. For some reason my data will not effectively transfer, do any of you see bugs?

Code:

Rich (BB code):
//**************************************//
//        Program: LCD_2.C                //
//        Author: Austin Schaller            //
//        Date: January 16th, 2011        //
//**************************************//

//******************************************//
//        Configuration of Shift Register        //
//        QA = RS                                //
//        QB = EN                                //
//        QC - QF = DATA                        //
//******************************************//

#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config OSC = INTIO2
#pragma config MCLRE = ON
#pragma config DEBUG = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF

// Define all other config bits so that PICkit2 doesn't moan
#pragma config FSCM = OFF
#pragma config IESO = OFF
#pragma config STVR = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF                                                                                                                              

#include <p18F1320.h>
#include <delays.h>

// The lower 4 bits of this port will be used

#define DATA LATAbits.LATA0         // Data pins QC-QF
#define LATCH LATAbits.LATA1        // ST-CP pin of shift register
#define CLOCK LATAbits.LATA2        // SH-CP pin of shift register

// Commands for Hitachi LCD
#define CLEAR_DISPLAY    0x01
#define FOUR_BIT       0b00101111  // 4-bit Interface
#define EIGHT_BIT      0b00111111  // 8-bit Interface
#define LINE_5X7       0b00110011  // 5x7 characters, single line
#define LINE_5X10      0b00110111  // 5x10 characters
#define LINES_5X7      0b00111011  // 5x7 characters, multiple line

#define DISPLAY_ON  0b00001111  // Display on
#define DISPLAY_OFF 0b00001011  // Display off
#define CURSOR_ON   0b00001111  // Cursor on
#define CURSOR_OFF  0b00001101  // Cursor off
#define BLINK_ON    0b00001111  // Cursor Blink
#define BLINK_OFF   0b00001110  // Cursor No Blink
#define UNDERLINE_ON    0b00001111
#define UNDERLINE_OFF   0b00001101

#define INCREMENT    0b00000111    // Entry mode increment
#define DECREMENT    0b00000101    // Entry mode decrement
#define SHIFT_ON    0b00000111    // Display shift on
#define SHIFT_OFF    0b00000110    // Display shift off

void LCDInit(void);
void LCDCmd(unsigned char);
void LCDChar(unsigned char);
void shiftOut(unsigned char);
void shiftOutRS(void);
void PulseEnable(void);
void outputData(unsigned char);
// TODO: Add all prototypes

void Delay5ms(void)
{
    Delay1KTCYx(1); // Delay of 5ms
    return;
}

void Delay15ms(void)
{
    Delay1KTCYx(3); // Delay of 15ms
    return;
}

void LCDInit(void)
{

    shiftOut(0x00);        // Clear all data
    LCDCmd(FOUR_BIT);
    LCDCmd(FOUR_BIT & LINES_5X7);
    LCDCmd(INCREMENT & SHIFT_OFF);
    LCDCmd(DISPLAY_ON & BLINK_OFF & UNDERLINE_OFF & CURSOR_ON);
    LCDCmd(CLEAR_DISPLAY);
}

void LCDCmd(unsigned char c)
{
    unsigned char DataHigh, DataLow;
    
    DataHigh |= (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY ---> 0000 XXXX)
    outputData(DataHigh);        // Output upper nibble data to shift register
    
    DataLow |= (c & 0x0F);        // Retrieve lower nibble
    outputData(DataLow);        // Output lower nibble data to shift register
    PulseEnable();
    
    Delay5ms();
}

void LCDChar(unsigned char c) 
{
    unsigned char DataHigh, DataLow;
    shiftOutRS();        // Enable RS (QA) for data register mode

    DataHigh |= (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY ---> 0000 XXXX)
    outputData(DataHigh);        // Output upper nibble data to shift register
    
    PulseEnable();

    DataLow |= (c & 0x0F);        // Retrieve lower nibble
    outputData(DataLow);        // Output lower nibble data to shift register
    
    PulseEnable();
    Delay5ms();
}    

void shiftOutRS(void)        // Shift a '1' to RS (QA)
{
    CLOCK = 0;
    DATA = 1;
    CLOCK = 1;
    LATCH = 1;
    LATCH = 0;
}

void outputData(unsigned char data)
{
    if(0x80>data>0x03)        // Make sure data is within X constraints: 01XX XX11
    {
        data = (data<<2) & 0x3C;        // Shift data two places to the left, since RS and EN are on QA and QB
        shiftOut(data);        // Output data to shift register
    }
}

void shiftOut(unsigned char data)
{ 
   int i;
   for (i=7;i>=0;i--)
   { 
      CLOCK = 0;        // Clear Clock
      if (data & (1<<i))            // Make bit positive while incrementing with the counting variable 'i'
      {
         DATA = 1; 
      }
      else 
      {
         DATA = 0; 
      }
      CLOCK = 1;        // Delineate between bits through clock transitions
   } 
   LATCH = 1;        // Latch data to execute data
   LATCH = 0; 
}

void PulseEnable()
{
    shiftOut(0x02);
    Delay5ms();
    shiftOut(0x00);
    return;
}

void main(void)
{
    OSCCON = 0x52;  // Set internal oscillator at 2 MHz
    ADCON0 = 0x00;  // Disable internal ADC
    ADCON1 = 0x7F;  // Set all PORTB to digital I/O
    INTCON2bits.RBPU = 0;
    TRISA = 0xF8;

    LCDInit();
    Delay15ms();
    
    LCDChar('I');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('m');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('l');
    LCDChar('i');
    LCDChar('v');
    LCDChar('e');
    LCDChar('!');
    LCDChar(' ');
    LCDChar('=');
    LCDChar(')');
        
    while(1);
}
Let me know if you have any questions!

Thanks,

Austin
What do you mean "the data will not effectively transfer"?

Have you done any troubleshooting? Can you see your "clock", "serial out" and latch being toggled?

Is the shift register parallel out actually working and outputting expected data to pins on the LCD?
 

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147
The LCD is clear, and when I do a build it remains that way. Only when I re-connect power, however, do I see the cursor blink (which I never intended in the first place).

This is mostly code I composed myself, so I'm wondering if the logic and algorithms seem adequate?

I am inclined to think that it is a problem with my data being sent to the LCD from the shift register. Perhaps the data I send out the DATA port (LATAbits.LATA0) isn't correct either, I'm not entirely sure.
 

spinnaker

Joined Oct 29, 2009
7,830
Can you answer my other questions?


I think you need to handle enable separately like it was done here I don't know how he get by with only using 3 data bits.

But using a separate enable I would think you could tie latch high on the S/R and use that PIC pin for enable instead. But I have not tried this wiht the 74HC595 as the datasheet calls for a transition to latch the data.
 
Last edited:

t06afre

Joined May 11, 2009
5,934
If you have PICKIT 2 it can be used as a logical analyser. It may help you some. I would have chosen a larger PIC. But you are perhaps doing this just to to see if it can be done.
 

thatoneguy

Joined Feb 19, 2009
6,359
Don't push enable out of the shift register.

There needs to be a 20uS delay between data ready (input) and enable going high to read that data. It's a timing issue, so you'll need to use another port for toggling enable 1-2 instructions after the shift register is dumped.
 

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147

So you actually have 4 pins on the PIC used, correct? This is the schematic diagram you have.

EDIT: I see now what you did, you have pulse enable on the PIC, but SH_CP and ST_CP are wired together.

Here is my updated code, but it still won't work!

Rich (BB code):
//******************************************//
//        Program: LCD_2.C                    //
//        Author: Austin Schaller                //
//        Date: February 1st, 2011            //
//******************************************//

//******************************************//
//        Configuration of Shift Register        //
//        QA = RS                                //
//        QB = EN                                //
//        QC - QF = DATA                        //
//******************************************//

#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config OSC = INTIO2
#pragma config MCLRE = ON
#pragma config DEBUG = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF

// Define all other config bits so that PICkit2 doesn't moan
#pragma config FSCM = OFF
#pragma config IESO = OFF
#pragma config STVR = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF                                                                                                                              

#include <p18F1320.h>
#include <delays.h>

// The lower 4 bits of this port will be used

#define DATA LATAbits.LATA0         // Data pins QC-QF
#define LATCH LATAbits.LATA1        // ST-CP pin of shift register
#define CLOCK LATAbits.LATA2        // SH-CP pin of shift register

// Commands for Hitachi LCD
#define CLEAR_DISPLAY    0x01
#define FOUR_BIT       0b00101111  // 4-bit Interface
#define EIGHT_BIT      0b00111111  // 8-bit Interface
#define LINE_5X7       0b00110011  // 5x7 characters, single line
#define LINE_5X10      0b00110111  // 5x10 characters
#define LINES_5X7      0b00111011  // 5x7 characters, multiple line

#define DISPLAY_ON  0b00001111  // Display on
#define DISPLAY_OFF 0b00001011  // Display off
#define CURSOR_ON   0b00001111  // Cursor on
#define CURSOR_OFF  0b00001101  // Cursor off
#define BLINK_ON    0b00001111  // Cursor Blink
#define BLINK_OFF   0b00001110  // Cursor No Blink
#define UNDERLINE_ON    0b00001111
#define UNDERLINE_OFF   0b00001101

#define INCREMENT    0b00000111    // Entry mode increment
#define DECREMENT    0b00000101    // Entry mode decrement
#define SHIFT_ON    0b00000111    // Display shift on
#define SHIFT_OFF    0b00000110    // Display shift off

void LCDInit(void);
void LCDCmd(unsigned char);
void LCDChar(unsigned char);
void shiftOut(unsigned char);
void Delay5us(void);
// TODO: Add all prototypes

void Delay5us()
{
    Nop();
}

void Delay5ms(void)
{
    Delay1KTCYx(1); // Delay of 5ms
    return;
}

void Delay15ms(void)
{
    Delay1KTCYx(3); // Delay of 15ms
    return;
}

void LCDInit(void)
{
    shiftOut(0x00);
    LCDCmd(FOUR_BIT);
    LCDCmd(FOUR_BIT & LINES_5X7);
    LCDCmd(INCREMENT & SHIFT_OFF);
    LCDCmd(DISPLAY_ON & BLINK_OFF & UNDERLINE_OFF & CURSOR_ON);
    LCDCmd(CLEAR_DISPLAY);
}

void LCDCmd(unsigned char c)
{
    unsigned char UpperNibble, LowerNibble, bitsHigh, bitsLow;
    
    // Upper Nibble
    UpperNibble = (c >> 4) & 0xF0;        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    bitsHigh = (UpperNibble<<2) & 0x3C;
    shiftOut(bitsHigh);        // Data
    
    bitsHigh = ((UpperNibble<<2) | 0x02) & 0x3C;
    shiftOut(bitsHigh);        // Data + EN
    Delay5us();
    
    bitsHigh = (UpperNibble<<2) & 0x3C;
    shiftOut(bitsHigh);        // Data
    Delay5ms();
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    bitsLow = (LowerNibble<<2) & 0x3C;
    shiftOut(bitsLow);        // Data
    
    bitsLow = ((LowerNibble<<2) | 0x02) & 0x3C;
    shiftOut(bitsLow);        // Data + EN
    Delay5us();
    
    bitsLow = (LowerNibble<<2) & 0x3C;
    shiftOut(bitsLow);        // Data
    Delay5ms();
}

void LCDChar(unsigned char c) 
{
    unsigned char UpperNibble, LowerNibble, bitsHigh, bitsLow;
    
    // Upper Nibble
    UpperNibble = (c >> 4) & 0xF0;        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    bitsHigh = ((UpperNibble<<2) | 0x01) & 0x3C;
    shiftOut(bitsHigh);        // Data + RS
    
    bitsHigh = ((UpperNibble<<2) | 0x03) & 0x3C;
    shiftOut(bitsHigh);        // Data + RS + EN
    Delay5us();
    
    bitsHigh = ((UpperNibble<<2) | 0x01) & 0x3C;
    shiftOut(bitsHigh);        // Data + RS
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    bitsLow = ((LowerNibble<<2) | 0x01) & 0x3C;
    shiftOut(bitsLow);        // Data + RS
    
    bitsLow = ((LowerNibble<<2) | 0x03) & 0x3C;
    shiftOut(bitsLow);        // Data + RS + EN
    Delay5us();
    
    bitsLow = ((LowerNibble<<2) | 0x01) & 0x3C;
    shiftOut(bitsLow);        // Data + RS
    Delay5us();
}    

void shiftOut(unsigned char c)
{ 
   int i;
   for (i=7;i>=0;i--)
   { 
      CLOCK = 0;        // Clear Clock
      if (c & (1<<i))            // Make bit positive while incrementing with the counting variable 'i'
      {
         DATA = 1; 
      }
      else 
      {
         DATA = 0; 
      }
      CLOCK = 1;        // Delineate between bits through clock transitions
   } 
   LATCH = 1;        // Latch data to execute data
   LATCH = 0; 
}

void main(void)
{
    OSCCON = 0x52;  // Set internal oscillator at 2 MHz
    ADCON0 = 0x00;  // Disable internal ADC
    ADCON1 = 0x7F;  // Set all PORTB to digital I/O
    INTCON2bits.RBPU = 0;
    TRISA = 0xF8;

    LCDInit();
    Delay15ms();
    
    LCDChar('I');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('m');
    LCDChar(' ');
    LCDChar('a');
    LCDChar('l');
    LCDChar('i');
    LCDChar('v');
    LCDChar('e');
    LCDChar('!');
    LCDChar(' ');
    LCDChar('=');
    LCDChar(')');
        
    while(1);
}
Notice now that I have the following patterns to output commands or data:

To send Data Nibble:
Rich (BB code):
shiftOut(0b00dddd01);    // RS=1 (data), E=0  
shiftOut(0b00dddd11);    // RS=1 (data), E=1 - clock data to LCD  
shiftOut(0b00dddd01);    // RS=1 (data), E=0  
// possible delay to allow LCD command to complete
To send Command Nibble:
Rich (BB code):
shiftOut(0b00cccc00);    // RS=0 (cmd), E=0  
shiftOut(0b00cccc10);    // RS=0 (cmd), E=1 - clock command to LCD  
shiftOut(0b00cccc00);    // RS=0 (cmd), E=0  
// possible delay to allow LCD command to complete
I have no idea why it won't work, any further thoughts?
 
Last edited:

thatoneguy

Joined Feb 19, 2009
6,359
Try:
20mS from power on to first command to LCD
5mS between commands (can be less for some cursor movements)
200uS between each display character.

Keep the enable a port on the PIC, rather than in the shift register, and allow at least 200uS "settling time" for the shift register outputs to be ready on the LCD inputs, then bring the enable high.
 

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147
Well I followed the algorithms that rajbex mentioned in his tutorial, and was able to get it working in C18! :)

Final Code:

Rich (BB code):
//******************************************//
//        Program: LCD_2.C                    //
//        Author: Austin Schaller                //
//        Date: February 1st, 2011            //
//******************************************//

//******************************************//
//        Configuration of Shift Register        //
//        QA = RS                                //
//        QB = EN                                //
//        QC - QF = DATA                        //
//******************************************//

#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config OSC = INTIO2
#pragma config MCLRE = ON
#pragma config DEBUG = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF

// Define all other config bits so that PICkit2 doesn't moan
#pragma config FSCM = OFF
#pragma config IESO = OFF
#pragma config STVR = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF                                                                                                                              

#include <p18F1320.h>
#include <delays.h>

// The lower 4 bits of this port will be used

#define DATA LATAbits.LATA0         // Data pins QC-QF
#define LATCH_CLOCK LATAbits.LATA1
#define EN LATAbits.LATA2        // Pulse enable pin

// Commands for Hitachi LCD
#define CLEAR_DISPLAY    0x01
#define FOUR_BIT       0b00101111  // 4-bit Interface
#define EIGHT_BIT      0b00111111  // 8-bit Interface
#define LINE_5X7       0b00110011  // 5x7 characters, single line
#define LINE_5X10      0b00110111  // 5x10 characters
#define LINES_5X7      0b00111011  // 5x7 characters, multiple line

#define DISPLAY_ON  0b00001111  // Display on
#define DISPLAY_OFF 0b00001011  // Display off
#define CURSOR_ON   0b00001111  // Cursor on
#define CURSOR_OFF  0b00001101  // Cursor off
#define BLINK_ON    0b00001111  // Cursor Blink
#define BLINK_OFF   0b00001110  // Cursor No Blink
#define UNDERLINE_ON    0b00001111
#define UNDERLINE_OFF   0b00001101

#define INCREMENT    0b00000111    // Entry mode increment
#define DECREMENT    0b00000101    // Entry mode decrement
#define SHIFT_ON    0b00000111    // Display shift on
#define SHIFT_OFF    0b00000110    // Display shift off
#define PulseEnable { EN = 1; Nop(); Nop(); EN = 0; }

void LCDInit(void);
void LCDCmd(unsigned char);
void LCDChar(unsigned char);
void shiftOut(unsigned char);
void Delay5us(void);
// TODO: Add all prototypes

void Delay5us()
{
    Nop();
    Nop();
}

void Delay5ms(void)
{
    Delay1KTCYx(1); // Delay of 5ms
    return;
}

void Delay15ms(void)
{
    Delay1KTCYx(3); // Delay of 15ms
    return;
}

void LCDInit(void)
{
    shiftOut(0x00);
    LCDCmd(FOUR_BIT);
    LCDCmd(FOUR_BIT & LINES_5X7);
    LCDCmd(INCREMENT & SHIFT_OFF);
    LCDCmd(DISPLAY_ON & BLINK_OFF & UNDERLINE_OFF & CURSOR_ON);
    LCDCmd(CLEAR_DISPLAY);
}

void LCDCmd(unsigned char c)
{
    unsigned char UpperNibble, LowerNibble;
    
    // Upper Nibble
    UpperNibble = (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    Delay15ms();
    shiftOut(UpperNibble);        // Data
    PulseEnable;
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    Delay15ms();
    shiftOut(LowerNibble);        // Data
    PulseEnable;
    
    Delay5ms();
}

void LCDChar(unsigned char c) 
{
    unsigned char UpperNibble, LowerNibble, bitsHigh, bitsLow;
    
    // Upper Nibble
    UpperNibble = (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    bitsHigh = UpperNibble | 0x10;        // RS + Data
    shiftOut(bitsHigh);
    Delay15ms();
    PulseEnable;
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    bitsLow = LowerNibble | 0x10;        // RS + Data
    Delay15ms();
    shiftOut(bitsLow);
    PulseEnable;
    
    Delay5ms();
}    

void shiftOut(unsigned char c)
{ 
   int i;
   for (i=7;i>=0;i--)
   { 
      LATCH_CLOCK = 0;        // Clear Clock
      if (c & (1<<i))            // Make bit positive while incrementing with the counting variable 'i'
      {
         DATA = 1; 
      }
      else 
      {
         DATA = 0; 
      }
      LATCH_CLOCK = 0;
      LATCH_CLOCK = 1;
   } 
   LATCH_CLOCK = 0;
   LATCH_CLOCK = 1; 
}

void main(void)
{
    int i;
    OSCCON = 0x52;  // Set internal oscillator at 2 MHz
    ADCON0 = 0x00;  // Disable internal ADC
    ADCON1 = 0x7F;  // Set all PORTB to digital I/O
    INTCON2bits.RBPU = 0;
    TRISA = 0xF8;

    LCDInit();
    Delay15ms();
    
    /*for(i=0;i<256;i++)
    {
        shiftOut(i);
        Delay10KTCYx(15);
    }*/
    
    
    LCDChar('I');
    LCDChar('t');
    LCDChar(' ');
    LCDChar('W');
    LCDChar('o');
    LCDChar('r');
    LCDChar('k');
    LCDChar('s');
    LCDChar('!');
    Delay15ms();
        
    while(1);
}
I appreciate everyone's assistance regarding the matter!

I will post pictures tomorrow!
 

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147
I was just doing this for practice, really.

Here is a picture:



Here is the final code:

Rich (BB code):
//******************************************//
//        Program: LCD_2.C                    //
//        Author: Austin Schaller                //
//        Date: February 2st, 2011            //
//******************************************//

//******************************************//
//        Configuration of Shift Register        //
//        QA - QD = DATA                        //
//        QE = RS                                //
//******************************************//

#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config OSC = INTIO2
#pragma config MCLRE = ON
#pragma config DEBUG = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF

// Define all other config bits so that PICkit2 doesn't moan
#pragma config FSCM = OFF
#pragma config IESO = OFF
#pragma config STVR = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF                                                                                                                              

#include <p18F1320.h>
#include <delays.h>

// The lower 4 bits of this port will be used

#define DATA LATAbits.LATA0         // Data pins QC-QF
#define LATCH_CLOCK LATAbits.LATA1
#define EN LATAbits.LATA2        // Pulse enable pin

// Commands for Hitachi LCD
#define CLEAR_DISPLAY    0x01
#define FOUR_BIT       0b00101111  // 4-bit Interface
#define EIGHT_BIT      0b00111111  // 8-bit Interface
#define LINE_5X7       0b00110011  // 5x7 characters, single line
#define LINE_5X10      0b00110111  // 5x10 characters
#define LINES_5X7      0b00111011  // 5x7 characters, multiple line

#define DISPLAY_ON  0b00001111  // Display on
#define DISPLAY_OFF 0b00001011  // Display off
#define CURSOR_ON   0b00001111  // Cursor on
#define CURSOR_OFF  0b00001101  // Cursor off
#define BLINK_ON    0b00001111  // Cursor Blink
#define BLINK_OFF   0b00001110  // Cursor No Blink
#define UNDERLINE_ON    0b00001111
#define UNDERLINE_OFF   0b00001101

#define INCREMENT    0b00000111    // Entry mode increment
#define DECREMENT    0b00000101    // Entry mode decrement
#define SHIFT_ON    0b00000111    // Display shift on
#define SHIFT_OFF    0b00000110    // Display shift off
#define PulseEnable { EN = 1; Nop(); Nop(); EN = 0; }

void LCDInit(void);
void LCDCmd(unsigned char);
void LCDChar(unsigned char);
void shiftOut(unsigned char);
// TODO: Add all prototypes

void Delay5ms(void)
{
    Delay1KTCYx(10); // Delay of 5ms
    return;
}

void Delay15ms(void)
{
    Delay1KTCYx(30); // Delay of 15ms
    return;
}

void LCDInit(void)
{
    shiftOut(0x00);
    LCDCmd(FOUR_BIT);
    LCDCmd(FOUR_BIT & LINES_5X7);
    LCDCmd(INCREMENT & SHIFT_OFF);
    LCDCmd(DISPLAY_ON & BLINK_OFF & UNDERLINE_OFF & CURSOR_ON);
    LCDCmd(CLEAR_DISPLAY);
}

void LCDCmd(unsigned char c)
{
    unsigned char UpperNibble, LowerNibble;
    
    // Upper Nibble
    UpperNibble = (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    Delay15ms();
    shiftOut(UpperNibble);        // Data
    PulseEnable;
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    Delay15ms();
    shiftOut(LowerNibble);        // Data
    PulseEnable;
    
    Delay5ms();
}

void LCDChar(unsigned char c) 
{
    unsigned char UpperNibble, LowerNibble, bitsHigh, bitsLow;
    
    // Upper Nibble
    UpperNibble = (c >> 4);        // Shift upper nibble to take the place of low-side nibble (XXXX YYYY --> 0000 XXXX)
    bitsHigh = UpperNibble | 0x10;        // RS + Data
    shiftOut(bitsHigh);
    Delay15ms();
    PulseEnable;
    
    // Lower Nibble
    LowerNibble = (c & 0x0F);
    bitsLow = LowerNibble | 0x10;        // RS + Data
    Delay15ms();
    shiftOut(bitsLow);
    PulseEnable;
    
    Delay5ms();
}    

void shiftOut(unsigned char c)
{ 
   int i;
   for (i=7;i>=0;i--)
   { 
      LATCH_CLOCK = 0;        // Clear Clock
      if (c & (1<<i))            // Make bit positive while incrementing with the counting variable 'i'
      {
         DATA = 1; 
      }
      else 
      {
         DATA = 0; 
      }
      LATCH_CLOCK = 0;
      LATCH_CLOCK = 1;
   } 
   LATCH_CLOCK = 0;
   LATCH_CLOCK = 1; 
}

void main(void)
{
    OSCCON = 0x72;  // Set internal oscillator at 8 MHz
    ADCON0 = 0x00;  // Disable internal ADC
    ADCON1 = 0x7F;  // Set all PORTB to digital I/O
    INTCON2bits.RBPU = 0;
    TRISA = 0xF8;

    LCDInit();
    Delay15ms();
    
    
    LCDChar('I');
    LCDChar('t');
    LCDChar(' ');
    LCDChar('W');
    LCDChar('o');
    LCDChar('r');
    LCDChar('k');
    LCDChar('s');
    LCDChar('!');
    
    Delay15ms();
        
    while(1);
}
 

thatoneguy

Joined Feb 19, 2009
6,359
I don't see any 0.1uF caps along your power rails. ;)

Seriously they will help if you add more ICs to the board, if things act funny, add those first. It's cheap and easy to do, I put a 0.1uF mylar about every 15 pins across the power rails on both sides of the breadboard.

If you have a breadboard that only has one power rail down the side, it gets a bit more difficult, use a jumper line and use 0.22uF caps (non-electrolytic).

Now that that's covered.. Congrats! I'm sure this thread will be useful for others starting out trying to talk to an LCD (PIC or AVR useful!)
 

Thread Starter

ELECTRONERD

Joined May 26, 2009
1,147
I don't see any 0.1uF caps along your power rails. ;)

Seriously they will help if you add more ICs to the board, if things act funny, add those first. It's cheap and easy to do, I put a 0.1uF mylar about every 15 pins across the power rails on both sides of the breadboard.

If you have a breadboard that only has one power rail down the side, it gets a bit more difficult, use a jumper line and use 0.22uF caps (non-electrolytic).

Now that that's covered.. Congrats! I'm sure this thread will be useful for others starting out trying to talk to an LCD (PIC or AVR useful!)
I will implement those caps from now on, thanks.


Cool! How did you do it? Do you control Enable separately?
Yes, I have the pulse enable pin controlled with the μC and I combine the ST-CP and SH-CP pins of the shift register instead of using them individually.
 

be80be

Joined Jul 5, 2008
2,072
How many pins on that bread board over 1000 ? let's say 1000 divide by 5 = 200 caps LOL

Thats about how the bread board acts as capacitance. You can get by without them most times the real head ache comes,
when you make a PCB and forget them LOL.
 
Top