Monday, September 19, 2016

Interfacing Lcd To PIC16F877a

CIRCUIT DIAGRAM





SOURCE CODE


#include <xc.h> // this file contains the SFR information of the chip
#include <delays.h> // header file for using C18 delay functionss
//----------------------------------------------------------------------------------
#define _XTAL_FREQ     4000000L
//Function prototypes
void port_initialise(void);
void init_variables (void);
void YourHighPriorityISRCode(void);
void YourLowPriorityISRCode(void);
void DelayMs(unsigned int msCount);
void lcd_ini(void);
void lcd_writeDAT(unsigned char ch);
void lcd_writeCMD(unsigned char ch);
void lcd_writeDAT_Run(unsigned char ch);
void lcd_strob(void);
void setposition(unsigned char pos);
void lcdstringptr(const char *ptr, unsigned char lineNo);
void LCDchk_busy (void);
//void Ram2LCD(char *dirString,unsigned char line);
void lcdFrameUpdate(void);
//--------------------------------------------------------------------------
//These are required if you are using this hex file with a device programmer or Debugger.
//When using with bootloader this will be ignored
//These config settings will start 4550 system and USB engine in 48Mhz System and USB Clock
//But this example code after bootup switches clock to 4Mhz internal oscillator.
  #pragma config WDTE = OFF      
    #pragma config FOSC = HS
    #pragma config PWRTE = ON
    #pragma config BOREN = ON
    #pragma config LVP = OFF
    #pragma config CPD = OFF
    #pragma config WRT = OFF
    #pragma config DEBUG = ON
    #pragma config CP = OFF
//--------------------------------------------------------------------------
//ROM based look uptables and constants
// This array holds the seven segment pattern to be illuminated for each digit.

const char * EmbCkit   = {" PIC16F877A "};
const char * USBMicro   = {" LCD interfaceTo "};
const char * CurCount   = {"Current Count on"};
const char * Digitis   = {"  Digits are    "};
//--------------------------------------------------------------------------
//#pragma udata access myaccess
near volatile  struct // This is a structure to create logic flags.
{ // FLAGAbits
  unsigned FLAG0:1; //
  unsigned FLAG1:1; //
  unsigned FLAG2:1; //
  unsigned FLAG3:1; //
  unsigned FLAG4:1; //
  unsigned FLAG5:1; //
  unsigned FLAG6:1;
unsigned FLAG7:1;
} FLAGAbits;
//---------------------------------------------------------------------------
//#pragma udata
// Variable dclarations(Global)
unsigned char Digit1,Digit2; // digit1 and Digit2 holds the values to be discplayed
unsigned char DebounceCnt; // keyboard debounce time variable
unsigned char FrameWait;
//-------------------------------------------------------------------------
// Defines


#define LCD_EN PORTAbits.RA2
#define LCD_RS PORTDbits.RD5
//#define LCD_RD PORTDbits.RD5

#define FlagIncri FLAGAbits.FLAG0
#define FlagDecri FLAGAbits.FLAG1
#define FlagDebounce FLAGAbits.FLAG2
#define LCDiniFlag  FLAGAbits.FLAG3
#define LCDFrameFlag  FLAGAbits.FLAG4
//----------------------------------------------------------------------------
//The USBMicroMaster Kit uses a bootloader which is residing below 0x1000 of programe memmory
//Here follows the reset and interrupt Re-mapping to suit the Bootloader used.
//aditional directives are added to avoid malfunction even if used with a device programmer .
//-------------------------Here we are re directing the reset vector-------------------------------
//--------------This portion is to re-direct code execution if not used along with bootloader------
void interrupt InterruptArea(void)
{
}
//-------------------------------------------------------------------------------------------------------------
//#pragma code MainlineCode //*********************************************************************************
void main (void)
{ // main loop starts-------------
unsigned char count;
unsigned char n; // Controller starts with 48Mhx clock from 20Mhz Xtal and PLL for USB bootloader
  port_initialise(); // setting up I/O lines as input and output
  init_variables (); // variables are to be initialised
lcd_ini(); // lcd initialise
LCDiniFlag=1;
//---------------------------------------------------------------------------------------------------
while(1) // Normally, every MCU programe is a never ending loop. here starts it
{ //while 1 loop starts----------

lcdFrameUpdate(); // here lcd messages change

} //while 1 loop ends------------
} // main loop ends--------------
///*********************************************************************************************************
//#pragma code functions
//----------------------------------------------------------------------------------------------------------
void lcdFrameUpdate(void) //This function updated lcd in two rames. For some time product name is shown
{ //Then Count is shown. That is done by this function using LCDFrameFlag and FrameWait
FrameWait++;
if(FrameWait>80)
{
if(LCDFrameFlag)// if set show this text
{
lcdstringptr(S2,1);
lcdstringptr(S1,2);
LCDFrameFlag=0; // and clear this bit to show the other frame on next time
}
else
{
lcdstringptr(S3,1);
lcdstringptr(S4,2);
lcd_writeCMD(0xCD); // shows the current count in Digits.
lcd_writeDAT(Digit1|0x30);
lcd_writeDAT(Digit2|0x30);
lcd_writeCMD(0xF0);// fix cursor out side dispaly address to avoid cursor
LCDFrameFlag=1;
}
FrameWait=0;
}
}
//****************************************************************************************
void lcdstringptr( const char *ptr, unsigned char lineNo)
{
unsigned char loc; // this function displays a string stored in the code memmory
unsigned char locCount;

if(lineNo==2)
{
setposition(16); //if second line is to be updated 16 is the second line start
}
else if(lineNo==1)
{
setposition(0); //(0 to 15 is first line)
}
for(locCount=0;locCount<=15;locCount++)// 16 times load local variable with value at pointer
{
loc=*ptr;
if(!loc) //check weather value 0 ( id\f end of string ,it will be 0.
break; // compiler by default null terminates every string
lcd_writeDAT(loc); // send each data to LCD
*ptr++;
}
}
//--------------------------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------
void port_initialise (void)
{
TRISD = 0X00; // LED port is made as output
PORTD = 0X00; // and cleared
ADCON1 = 0b10000100; // A/D module not ued.All pins digital
TRISA =  0b00001011; // LED port is made as output
PORTA = 0X00; // and cleared
TRISC &= 0b11111110; // LCD enable pin output
TRISE = 0B00000111;
PORTE = 0X00; // digit enable transistors in PORTE- hold them low
TRISB = 0b00000000;// switch pins as input- all else output
PORTB = 0X00 ; // LED array and buzzer pins made output and kept low.
LEDenable=0; // led array enable transistor off
Digit1_En=0; // digits to be kept low
Digit2_En=0;
}
//*******************************************************************************************************
void init_variables (void)
{
Digit1=0;Digit2=0;DebounceCnt=0;
FlagIncri=FlagDecri=FlagDebounce=0;
LCDiniFlag=0;
//_asm nop _endasm  // you can add any varibale initialisation here if required
}
//**************************************************************************
void DelayMs(unsigned int msCount)
{
unsigned int locCnt;
for(locCnt=0;locCnt<=msCount;locCnt++)
{
__delay_ms(1); // This C18 function creates a delay of 1000 instructions= 1000uSec= 1 millisecond
} // looping this msCount times will give that much milliseconds delay
}
//******************************************************************************
void lcd_ini(void) //Initialisation Of LCD
{
 
    LCD_EN=0; //Disable LCD
    LCD_RS=0; // write to command register
DelayMs(20); // Carefull! when changing Target increase delay .

PORTD = (PORTD & 0xF0) | 0x03; //Function set
DelayMs(12);
lcd_strob();   // 1 Strobe for 8 bit pushed in!
DelayMs(12);
lcd_strob();   // 2 Strobe for 8 bit  pushed in!
DelayMs(12);
lcd_strob();   // 3 Srobe for 8 bit pushed in!
DelayMs(12);

PORTD &= 0b11111110;       // 4 bit interface command setting!
lcd_strob();   // 4 bit  first strobe
DelayMs(2); // Time to sink command


lcd_writeCMD(0b00101000); // (28h)  set interface data length, number of lines-
DelayMs(2); // Charector Font
lcd_writeCMD(0b00001000); // (08h) dispaly off all cursor all off
DelayMs(2);
lcd_writeCMD(0b00000110); // (06h) position incriment decriment and
DelayMs(2); // display shift
lcd_writeCMD(0b00001111); // (0F)LCD on again after adjustments , cursor on and blink on!
DelayMs(2);
lcd_writeCMD(0b00000001); // (01)Clear  the entire display
DelayMs(2);
}
//********************************************************************************************
// write a byte to the LCD in 4 bit mode
void lcd_writeDAT(unsigned char ch)
{
if(!LCDiniFlag) // check weather in the initialisation stage
{
NOP();
}
else
{
//LCDchk_busy(); //else check busy flag so that process will become fast
}
    LCD_RS=1; //LCD to data mode
//LCD_RD = 0; // lcd in write mode
PORTD = (PORTD & 0xF0) |  (ch >> 4); //Hi nibble first.
lcd_strob();
PORTD= (PORTD & 0xF0) |  (ch & 0x0F); //Low nibble next
lcd_strob();
}
//********************************************************************************************
//FunctionFor Writing Commands To LCD
void lcd_writeCMD(unsigned char ch)
{
if(!LCDiniFlag) // check weather in the initialisation stage
{
DelayMs(1); // if so use delay
NOP();
}
else
{
//LCDchk_busy(); //else check busy flag so that process will become fast
NOP();
}
    LCD_RS=0;           // LCD in Command Mode
PORTD = (PORTD & 0xF0) |  (ch >> 4); // Hi nibble first.
lcd_strob();
PORTD = (PORTD & 0xF0) |  (ch & 0x0F); // Low nibble next
lcd_strob();
}
//*********************************************************************************************

void lcd_strob(void) //Function For toggling Enable pin of LCD to push data in
{
LCD_EN=1; //Enable LCD
NOP();
LCD_EN=0; //Disable LCD
}
//***********************************************************************************************
void setposition(unsigned char pos) //LCD position
{
unsigned char startloc;
if((pos>=0)&(pos<=15)) // positions 0,1,2,3,...14,15
{ //          16,17,.....30,31
startloc=(0x80+pos); // position should be ored with 80h to set left most bit
  } // for specifying position.see LCD datasheet
if((pos<=31) & (pos>=16))
{
startloc=(0xC0+(pos-16));  // second line
}

lcd_writeCMD(startloc); // fix cursor to required position.....
}
//*************************************************************************************************

void LCDchk_busy (void) // this function checks and hold on if LCD is Busy
{
unsigned char busybyte,temp;
TRISD |= 0b00001111; // busy pin as input

do
{
//LCD_RD = 1;                     // Set the control bits for read
   LCD_RS = 0;
  LCD_EN=1;
temp=PORTD;
LCD_EN=0;
temp=((temp&0b00001111)<<4);
busybyte=temp;
LCD_EN=1;
temp=PORTD; //isolate the needed bits only
LCD_EN=0;
temp=(temp&0b00001111);
busybyte=(busybyte|temp);
//LCD_RD = 0;
//_asm nop _endasm
}while(busybyte&0x80); // returns only if LCD not busy

TRISD &= 0b11000000; // Data and command lines for LCD back to output mode
}
//****************************************************************************
EXPLANATIONS

This module actually demonstrates the interfacing of 16 Characters * 2 Lines LCD Module to a PIC microcontroller using MPALB C-18.
LCD Module is another Device which has its own Microcontroller, Liquid Crystal panel and built in software to manage display by its own. Unlike Seven Segment Display interfacing we have done in last modules, this LCD device doesn’t need MCU’s active full time involvement. Multiplexing seven segment display itself gave us a taste about the timing constraints involved if we want to fool our eyes. Considering that, just think about the huge task we may have to implement if we are going to multiplex the entire pixels in a 16*2 character LCD. So that task is being taken care of by a dedicated controller in the LCD module.
Our Host controller need to provide necessary instructions and data to LCD Module’s interface as per the data sheet specifications of LCD manufactures data sheet.
As the LCD is a general purpose module that is suitable for a variety of needs, it needs some sort of an initialization in our application to configure it for our needs and method of interface. The LCD can be controlled by providing data through 8 lines or 4 lines, its cursor can be made to blink or hide, it can auto increment its next character showing locations, its character formation pixel size value adjustment, - all these needsto be specified at the time of booting up so that LCD can understand and expect what our host controller is about to send and expecting. This process is called LCD initialization and every Program which interfaces a LCD will have to provide such a function.
LCD initialization is actually sending specific data to the controller inside LCD module as per its data sheet specifications. You need to refer LCD data sheet to check the initialization data and its meaning. (Comments are provided along with code). The LCD Driver part is having functions to send data or command to LCD. Here we are using 4 bit interface to ‘talk’ to LCD. This is to reduce the pin count we connect between LCD and PIC16F877A.
Separate commands are available for sending data and instructions to LCD. Letter ‘A’ to be displayed is to be send as data ,but 0x01 is the value to be send as command (instruction) to clear all the display and show complete blank in LCD. As we are using 4 bit interface the data needs to be split into two halves (nibbles) and send to LCD, which will be joined together by LCD to be interpreted.
LCD being a slow device (made in such a way to reduce power consumption!)needs some time to process data once received. So before sending the next data we must check whether LCD had completed the processing of previous data. Function is provided to check busy also.
These LCD functions can be used for any LCD display with Hitachi controller with changes for the port you are using.
See the following declarations.
const char * S1  = {" PIC16F877A "};
const char * S2   = {" LCD interfaceTo "};
const char * S3   = {"Current Count on"};
const char * S4   = {"  Digits are    "};
These are directives to place strings in ROM. These are strings to be stored in the controller, later to be shown in lcd. They are not variables so need not place in RAM. The compiler will store this string in ROM in the most compact way with a pointer. To display this string we have alcdstringptr function which accepts the pointer to this string and will read these characters one by one and will send to LCD display

No comments:

Post a Comment