lcd4bit_4lines.c - Demonstrates a 4-line 20x4 Character LCD

This program uses the industry standard parallel interface in 4-bit mode. Assumes a 5V LCD; digital-only IO pins are assumed to be used for the four-bit data bus because they are 5V input tolerant.

Tested with Hantronix HDM20416L-M.

 
#include "pic24_all.h"


#define RS_HIGH()        (_LATB9 = 1)
#define RS_LOW()         (_LATB9 = 0)
#define CONFIG_RS()      CONFIG_RB9_AS_DIG_OUTPUT()

#define RW_HIGH()        (_LATB13 = 1)
#define RW_LOW()         (_LATB13 = 0)
#define CONFIG_RW()      CONFIG_RB13_AS_DIG_OUTPUT()

#define E_HIGH()         (_LATB14 = 1)
#define E_LOW()          (_LATB14 = 0)
#define CONFIG_E()       CONFIG_RB14_AS_DIG_OUTPUT()

#define LCD4O          (_LATB5)
#define LCD5O          (_LATB6)
#define LCD6O          (_LATB7)
#define LCD7O          (_LATB8)
#define LCD7I          (_RB8)

#define CONFIG_LCD4_AS_INPUT() CONFIG_RB5_AS_DIG_INPUT()
#define CONFIG_LCD5_AS_INPUT() CONFIG_RB6_AS_DIG_INPUT()
#define CONFIG_LCD6_AS_INPUT() CONFIG_RB7_AS_DIG_INPUT()
#define CONFIG_LCD7_AS_INPUT() CONFIG_RB8_AS_DIG_INPUT()

#define CONFIG_LCD4_AS_OUTPUT() CONFIG_RB5_AS_DIG_OUTPUT()
#define CONFIG_LCD5_AS_OUTPUT() CONFIG_RB6_AS_DIG_OUTPUT()
#define CONFIG_LCD6_AS_OUTPUT() CONFIG_RB7_AS_DIG_OUTPUT()
#define CONFIG_LCD7_AS_OUTPUT() CONFIG_RB8_AS_DIG_OUTPUT()

#define GET_BUSY_FLAG()  (LCD7I)



/**
 Functions above this line must be redefined for
 your particular PICmicro-to-LCD interface
*/

//Configure 4-bit data bus for output
void configBusAsOutLCD(void) {
  RW_LOW();                  //RW=0 to stop LCD from driving pins
  CONFIG_LCD4_AS_OUTPUT();   //D4
  CONFIG_LCD5_AS_OUTPUT();   //D5
  CONFIG_LCD6_AS_OUTPUT();   //D6
  CONFIG_LCD7_AS_OUTPUT();   //D7
}

//Configure 4-bit data bus for input
void configBusAsInLCD(void) {
  CONFIG_LCD4_AS_INPUT();   //D4
  CONFIG_LCD5_AS_INPUT();   //D5
  CONFIG_LCD6_AS_INPUT();   //D6
  CONFIG_LCD7_AS_INPUT();   //D7
  RW_HIGH();                   // R/W = 1, for read
}

//Output lower 4-bits of u8_c to LCD data lines
void outputToBusLCD(uint8_t u8_c) {
  LCD4O = u8_c & 0x01;          //D4
  LCD5O = (u8_c >> 1)& 0x01;    //D5
  LCD6O = (u8_c >> 2)& 0x01;    //D6
  LCD7O = (u8_c >> 3)& 0x01;    //D7
}

//Configure the control lines for the LCD
void configControlLCD(void) {
  CONFIG_RS();     //RS
  CONFIG_RW();     //RW
  CONFIG_E();      //E
  RW_LOW();
  E_LOW();
  RS_LOW();
}

//Pulse the E clock, 1 us delay around edges for
//setup/hold times
void pulseE(void) {
  DELAY_US(1);
  E_HIGH();
  DELAY_US(1);
  E_LOW();
  DELAY_US(1);
}
 

Write a byte (u8_Cmd) to the LCD. u8_DataFlag is ‘1’ if data byte, ‘0’ if command byte u8_CheckBusy is ‘1’ if must poll busy bit before write, else simply delay before write u8_Send8Bits is ‘1’ if must send all 8 bits, else send only upper 4-bits

void writeLCD(uint8_t u8_Cmd, uint8_t u8_DataFlag,
              uint8_t u8_CheckBusy, uint8_t u8_Send8Bits) {

  uint8_t u8_BusyFlag;
  uint8_t u8_wdtState;
  if (u8_CheckBusy) {
    RS_LOW();            //RS = 0 to check busy

check busy

    configBusAsInLCD();  //set data pins all inputs
    u8_wdtState = _SWDTEN;  //save WDT enable state
    CLRWDT();          //clear the WDT timer
    _SWDTEN = 1;            //enable WDT to escape infinite wait
    do {
      E_HIGH();
      DELAY_US(1);  // read upper 4 bits
      u8_BusyFlag = GET_BUSY_FLAG();
      E_LOW();
      DELAY_US(1);
      pulseE();              //pulse again for lower 4-bits
    } while (u8_BusyFlag);
    _SWDTEN = u8_wdtState;   //restore WDT enable state
  } else {
    DELAY_MS(10); // don't use busy, just delay
  }
  configBusAsOutLCD();
  if (u8_DataFlag) RS_HIGH();   // RS=1, data byte
  else    RS_LOW();             // RS=0, command byte
  outputToBusLCD(u8_Cmd >> 4);  // send upper 4 bits
  pulseE();
  if (u8_Send8Bits) {
    outputToBusLCD(u8_Cmd);     // send lower 4 bits
    pulseE();
  }
}

//These definitions are for a Hantronix 20x4 LCD
#define GOTO_LINE1() writeLCD(0x80,0,1,1)
#define GOTO_LINE2() writeLCD(0xC0,0,1,1)
#define GOTO_LINE3() writeLCD(0x94,0,1,1)
#define GOTO_LINE4() writeLCD(0xD4,0,1,1)
 

Initialize the LCD, modify to suit your application and LCD

void initLCD() {
  DELAY_MS(50);          //wait for device to settle
  writeLCD(0x20,0,0,0); // 4 bit interface
  writeLCD(0x28,0,0,1); // 2 line display, 5x7 font
  writeLCD(0x28,0,0,1); // repeat
  writeLCD(0x06,0,0,1); // enable display
  writeLCD(0x0C,0,0,1); // turn display on; cursor, blink is off
  writeLCD(0x01,0,0,1); // clear display, move cursor to home
  DELAY_MS(3);
}

//Output a string to the LCD
void outStringLCD(char *psz_s) {
  while (*psz_s) {
    writeLCD(*psz_s, 1, 1,1);
    psz_s++;
  }
}


int main(void) {
  configBasic(HELLO_MSG);      // Set up heartbeat, UART, print hello message and diags

  configControlLCD();      //configure the LCD control lines
  initLCD();               //initialize the LCD

  GOTO_LINE1();          // cursor to 1st line
  outStringLCD("Line 1");
  GOTO_LINE2();          // cursor to 2nd line
  outStringLCD("Line 2");
  GOTO_LINE3();          // cursor to 3rd line
  outStringLCD("Line 3");
  GOTO_LINE4();          // cursor to 4th line
  outStringLCD("Line 4");
  while (1) {
    doHeartbeat();
  }
}