Tuesday, August 30, 2016

3-Wire LCD driver for CCS PIC C compiler


3-Wire LCD using shift register (for HD44780 compliant controllers)
Generally the LCD display needs at least 6 data lines to operate. But there are some microcontrollers don't have this number of available pins like the PIC12F family, which means that we have to find a solution to decrease the number of pins used by the LCD display.
The idea is sending data serially to the LCD display via shift register, this shift register is serial-in parallel-out which receives serial data from the microcontroller via two pins data pin and clock pin.
Any serial-in parallel out shift register can be used for example: 74HC595, 74HC164, CD4094 (HEF4094)... Circuit schematics are below.
3-Wire LCD driver CCS C source code:
This is the full C code of the driver.
This driver tested with 1602 (16x2) LCD and 2004 (20x4) with crystal frequencies 8MHz and 48MHz.
// 3-Wire LCD driver for CCS PIC C compiler (for HD44780 compliant controllers)
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

#define LCD_FIRST_ROW          0x80
#define LCD_SECOND_ROW         0xC0
#define LCD_THIRD_ROW          0x94
#define LCD_FOURTH_ROW         0xD4
#define LCD_CLEAR              0x01
#define LCD_RETURN_HOME        0x02
#define LCD_CURSOR_OFF         0x0C
#define LCD_UNDERLINE_ON       0x0E
#define LCD_BLINK_CURSOR_ON    0x0F
#define LCD_MOVE_CURSOR_LEFT   0x10
#define LCD_MOVE_CURSOR_RIGHT  0x14
#define LCD_TURN_ON            0x0C
#define LCD_TURN_OFF           0x08
#define LCD_SHIFT_LEFT         0x18
#define LCD_SHIFT_RIGHT        0x1E

short RS;
void lcd_write_nibble(unsigned int8 n){
  unsigned int8 i;
  output_low(LCD_CLOCK_PIN);
  output_low(LCD_EN_PIN);
  for( i = 8; i > 0; i = i >> 1){
    if(n & i)
      output_high(LCD_DATA_PIN);
    else
      output_low(LCD_DATA_PIN);
    Delay_us(10);
    output_high(LCD_clock_PIN);
    Delay_us(10);
    output_low(LCD_clock_PIN);
  }
  if(RS)
    output_high(LCD_DATA_PIN);
  else
    output_low(LCD_DATA_PIN);
  for(i = 0; i < 2; i++){
    Delay_us(10);
    output_high(LCD_clock_PIN);
    Delay_us(10);
    output_low(LCD_clock_PIN);
  }
  output_high(LCD_EN_PIN);
  delay_us(2);
  output_low(LCD_EN_PIN);
}

void LCD_Cmd(unsigned int8 Command){
  RS = 0;
  lcd_write_nibble(Command >> 4);
  lcd_write_nibble(Command & 0x0F);
  if((Command == 0x0C) || (Command == 0x01) || (Command == 0x0E) || (Command == 0x0F)
  || (Command == 0x10) || (Command == 0x1E) || (Command == 0x18) || (Command == 0x08)
  || (Command == 0x14) || (Command == 0x02))
    Delay_ms(50);
}

void LCD_GOTO(unsigned int8 col, unsigned int8 row){
  switch(row){
    case 1:
      LCD_Cmd(0x80 + col-1);
      break;
    case 2:
      LCD_Cmd(0xC0 + col-1);
      break;
    case 3:
      LCD_Cmd(0x94 + col-1);
      break;
    case 4:
      LCD_Cmd(0xD4 + col-1);
    break;
  }
}

void LCD_Out(unsigned int8 LCD_Char){
  RS = 1;  
  lcd_write_nibble(LCD_Char >> 4);
  delay_us(10);
  lcd_write_nibble(LCD_Char & 0x0F);break;
}

void LCD_Initialize(){
  RS = 0;
  output_low(LCD_DATA_PIN);
  output_low(LCD_CLOCK_PIN);
  output_low(LCD_EN_PIN);
  output_drive(LCD_DATA_PIN);
  output_drive(LCD_CLOCK_PIN);
  output_drive(LCD_EN_PIN);
  delay_ms(40);
  lcd_Cmd(3);
  delay_ms(5);
  lcd_Cmd(3);
  delay_ms(5);
  lcd_Cmd(3);
  delay_ms(5);
  lcd_Cmd(2);
  delay_ms(5);
  lcd_Cmd(0x28);
  delay_ms(50);
  lcd_Cmd(0x0C);
  delay_ms(50);
  lcd_Cmd(0x06);
  delay_ms(50);
  lcd_Cmd(0x0C);
  delay_ms(50);
}

Or you can download the driver .C file from the following link:
3-Wire LCD driver

It is easy to add this file to your project just put it in your project folder or in the CCS PIC C driver folder.
As any file driver the 3-wire LCD driver must be included using the following line:
#include <3WireLCD.c>
3-Wire LCD CCS C driver routines:
When the 3-wire Lcd driver is used, the following variables must be declared as in this example where the data line mapped at RB0,the clock line at RB1 and the enable line at RB2:
#define LCD_DATA_PIN PIN_B0
#define LCD_CLOCK_PIN PIN_B1
#define LCD_EN_PIN PIN_B2

The driver routines are described below:
LCD_Initialize();  // Must be called before any other function.
LCD_GOTO(unsigned int8 col, unsigned int8 row);  // Set write position on LCD (upper left is 1,1 and second row first position is 1,2)
LCD_Out(unsigned int8 LCD_Char);  // Display Char on the LCD.
LCD_Cmd(unsigned int8 Command);  // Send a command to the LCD
The following commands can be used with LCD_Com() (example: LCD_Com(LCD_CLEAR);)
LCD_FIRST_ROW                          Move cursor to the 1st row
LCD_SECOND_ROW                    Move cursor to the 2nd row
LCD_THIRD_ROW                         Move cursor to the 3rd row
LCD_FOURTH_ROW                     Move cursor to the 4th row
LCD_CLEAR                                    Clear display
LCD_RETURN_HOME                    Return cursor to home position, returns a shifted display to                                                                           its original position. Display data RAM is unaffected.
LCD_CURSOR_OFF                        Turn off cursor
LCD_UNDERLINE_ON                   Underline cursor on
LCD_BLINK_CURSOR_ON             Blink cursor on
LCD_MOVE_CURSOR_LEFT           Move cursor left without changing display data RAM
LCD_MOVE_CURSOR_RIGHT        Move cursor right without changing display data RAM
LCD_TURN_ON                              Turn Lcd display on
LCD_TURN_OFF                             Turn Lcd display off
LCD_SHIFT_LEFT                            Shift display left without changing display data RAM
LCD_SHIFT_RIGHT                          Shift display right without changing display data RAM
3-Wire LCD circuit schematic:
The hardware circuit is simple all what we need is a shift register (74HC595, 74HC164, CD4094).
3-Wire LCD using 74HC595 shift register:
The following circuit schematic shows the connection circuit between the LCD, 74HC595 and the microcontroller.
3-Wire LCD display circuit using 74HC595 shift register
3-Wire LCD using 74HC164 shift register:
The following circuit schematic shows 3-wire LCD connection using 74HC164 shift register.
3-Wire LCD display circuit using 74HC164 shift register
3-Wire LCD using 74HC4094 (HEF4094, CD4094) shift register:
The following circuit for LCD display connection with HEF4094 shift register.
3-Wire LCD display circuit using 74HC4094 shift register
Example:
The following URL shows how to interface PIC16F877A microcontroller with 3-wire LCD.
Interfacing PIC16F877A with 3-wire LCD