lcd4bit.c - Character LCD interface example

Demonstrates a Character LCD using 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 Optrex 24227 and Optrex DMC-50448N-AAE-AD. Should work with any LCD that uses the Hitachi HD44780 LCD controller.

 
#include "pic24_all.h"
 

REMAP THE LCD module pins based on the target system

#if (HARDWARE_PLATFORM == EMBEDDED_C1)
# define RS_HIGH()        (_LATC2 = 1)
# define RS_LOW()         (_LATC2 = 0)
# define CONFIG_RS()      CONFIG_RC2_AS_DIG_OUTPUT()

# define RW_HIGH()        (_LATC1 = 1)
# define RW_LOW()         (_LATC1 = 0)
# define CONFIG_RW()      CONFIG_RC1_AS_DIG_OUTPUT()

# define E_HIGH()         (_LATC0 = 1)
# define E_LOW()          (_LATC0 = 0)
# define CONFIG_E()       CONFIG_RC0_AS_DIG_OUTPUT()

# define LCD4O          (_LATC4)
# define LCD5O          (_LATC5)
# define LCD6O          (_LATC6)
# define LCD7O          (_LATC7)
# define LCD7I          (_RC7)

# define CONFIG_LCD4_AS_INPUT() CONFIG_RC4_AS_DIG_INPUT()
# define CONFIG_LCD5_AS_INPUT() CONFIG_RC5_AS_DIG_INPUT()
# define CONFIG_LCD6_AS_INPUT() CONFIG_RC6_AS_DIG_INPUT()
# define CONFIG_LCD7_AS_INPUT() CONFIG_RC7_AS_DIG_INPUT()

# define CONFIG_LCD4_AS_OUTPUT() CONFIG_RC4_AS_DIG_OUTPUT()
# define CONFIG_LCD5_AS_OUTPUT() CONFIG_RC5_AS_DIG_OUTPUT()
# define CONFIG_LCD6_AS_OUTPUT() CONFIG_RC6_AS_DIG_OUTPUT()
# define CONFIG_LCD7_AS_OUTPUT() CONFIG_RC7_AS_DIG_OUTPUT()
#elif (HARDWARE_PLATFORM == EMBEDDED_F14)
# define RS_HIGH()        (_LATC12 = 1)
# define RS_LOW()         (_LATC12 = 0)
# define CONFIG_RS()      CONFIG_RC12_AS_DIG_OUTPUT()

# define RW_HIGH()        (_LATD11 = 1)
# define RW_LOW()         (_LATD11 = 0)
# define CONFIG_RW()      CONFIG_RD11_AS_DIG_OUTPUT()

# define E_HIGH()         (_LATD10 = 1)
# define E_LOW()          (_LATD10 = 0)
# define CONFIG_E()       CONFIG_RD10_AS_DIG_OUTPUT()

# define LCD4O          (_LATE4)
# define LCD5O          (_LATE5)
# define LCD6O          (_LATE6)
# define LCD7O          (_LATE7)
# define LCD7I          (_RE7)

# define CONFIG_LCD4_AS_INPUT() CONFIG_RE4_AS_DIG_INPUT()
# define CONFIG_LCD5_AS_INPUT() CONFIG_RE5_AS_DIG_INPUT()
# define CONFIG_LCD6_AS_INPUT() CONFIG_RE6_AS_DIG_INPUT()
# define CONFIG_LCD7_AS_INPUT() CONFIG_RE7_AS_DIG_INPUT()

# define CONFIG_LCD4_AS_OUTPUT() CONFIG_RE4_AS_DIG_OUTPUT()
# define CONFIG_LCD5_AS_OUTPUT() CONFIG_RE5_AS_DIG_OUTPUT()
# define CONFIG_LCD6_AS_OUTPUT() CONFIG_RE6_AS_DIG_OUTPUT()
# define CONFIG_LCD7_AS_OUTPUT() CONFIG_RE7_AS_DIG_OUTPUT()
#else
# 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()
#endif

#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();
  }
}
 

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

  outStringLCD("******Hello, my name is Bob********");
  writeLCD(0xC0,0,1,1);  // cursor to 2nd line
  outStringLCD("-----these lines are moving!-------");
  while (1) {
    writeLCD(0x18,0,1,1);  // shift left
    DELAY_MS(200);
    doHeartbeat();
  }
}