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. See chapter 8.

I/O pins changed to be compatible with the LCD connections on the Explorer-16 board, also changed the initialization code in initLCD slightly.

 
#include "pic24_all.h"



#define RS_HIGH()        _LATB15 = 1
#define RS_LOW()         _LATB15 = 0
#define CONFIG_RS()      CONFIG_RB15_AS_DIG_OUTPUT()

#define RW_HIGH()        _LATD5 = 1
#define RW_LOW()         _LATD5 = 0
#define CONFIG_RW()      CONFIG_RD5_AS_DIG_OUTPUT()

#define E_HIGH()         _LATD4 = 1
#define E_LOW()          _LATD4 = 0
#define CONFIG_E()       CONFIG_RD4_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()

#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(0x28,0,0,0); // 4 bit interface, 2 line display
  writeLCD(0x28,0,1,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();
  }
}