Friday, September 23, 2016

How to change baud rate of ESP8266 to 9600

ESP8266





                                                         The ESP8266 WiFi Module is a self contained SOC with integrated TCP/IP protocol stack that can give any microcontroller access to your WiFi network. The ESP8266 is capable of either hosting an application or offloading all Wi-Fi networking functions from another application processor. Each ESP8266 module comes pre-programmed with an AT command set firmware, meaning, you can simply hook this up to your Arduino device and get about as much WiFi-ability as a WiFi Shield offers (and that’s just out of the box)! The ESP8266 module is an extremely cost effective board with a huge, and ever growing, community.
This module has a powerful enough on-board processing and storage capability that allows it to be integrated with the sensors and other application specific devices through its GPIOs with minimal development up-front and minimal loading during runtime. Its high degree of on-chip integration allows for minimal external circuitry, including the front-end module, is designed to occupy minimal PCB area. The ESP8266 supports APSD for VoIP applications and Bluetooth co-existance interfaces, it contains a self-calibrated RF allowing it to work under all operating conditions, and requires no external RF parts.
There is an almost limitless fountain of information available for the ESP8266, all of which has been provided by amazing community support. In the Documents section below you will find many resources to aid you in using the ESP8266, even instructions on how to transforming this module into an IoT (Internet of Things) solution!


SET DEFAULT BAUD RATE TO 9600

    Must update firmware to version V0.9.2.2,  in this version defalt baud rate is 9600
    you can download firmware here   Download Firmware

    Esp Flasher download here   ESP Flasher Download

CONNECTION FOR FIRMWARE FLASH





  • Now let’s flash some firmware:
  • Ground GPIO0 and start-up or reset device.
  • You should see the blue LED flash once.
  • Run the executable “esp8266_flasher.exe” that was just downloaded.
  • Click the “Bin” button to select the downloaded firmware (ESP_8266_BIN0.22.bin).
  • Enter the COMx number assigned to your serial port (mine was 7).
  • Click “download.
  • When the download finishes, you will see “Failed to leave Flash mode”
  • Ignore this and close the downloader program.
  • Remove the GPIO0 to ground connection.
  • Reset the ESP8266: You will see two blue flashes again (the second flash is dimmer than the first).
  • The firmware is running at 115200 now and should respond to AT commands.
  • The most basic command to verify the firmware:
  • Enter “AT<enter>”
  • ESP8266 response: OK



Monday, September 19, 2016

Water level system with 3 Probes


Interface current sensor wcs2705 to PIC16f877a


Encoder interface to pic18f4550


Interfacing CD4094 to pic 16f628a


Comparator example of PIC16f628a


EEPROM read write of PIC16F877a


PIC16F877a PWM example


Interfacing RS485 To PIC16F877a


Interfacing Push SW To PIC16F877a


Interfacing KeyBoard To PIC16F877a


Interfacing Stepper Motor To PIC16F877a


Interfacing DC Motor To PIC16F877a


Interfacing LDR To PIC16F877a

CIRCUIT DIAGRAM



SOURCE CODE


//----------------------------------------------------------------------------------
#include <xc.h> // this file contains the SFR information of the chip
//----------------------------------------------------------------------------------
#define _XTAL_FREQ     4000000L
//Function prototypes
void port_initialise(void);
void init_variables (void);
void DelayMs(unsigned int msCount);
void ADCLdrread(void);
void adc_init(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 452 system.
//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


//--------------------------------------------------------------------------
//#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;
//-------------------------------------------------------------------------
near union
{ //This is a union to hold the ADC reading for ADC converter. This ADC result
  unsigned char ADCbytes[2];// we are getting as bytes from ADRSL and ADRSH. But it is
  unsigned int  Volt16Bit ; // a 10 bit number. So for calculation purpose
}ADCvolts; //we need to process it as a two byte number- so  union
//---------------------------------------------------------------------------
// Variable dclarations(Global)

//-------------------------------------------------------------------------
// Defines



//----------------------------------------------------------------------------
//This is the intruupt area.
void interrupt InterruptArea(void)
{
}
//#pragma code MainlineCode //*********************************************************************************
void main (void)
{ // main loop starts-------------
unsigned char count;
unsigned char n;
  port_initialise(); // setting up I/O lines as input and output
  init_variables (); // variables are to be initialised
adc_init(); // initialises ther ADC module

//---------------------------------------------------------------------------------------------------
while(1) // Normally, every MCU programe is a never ending loop. here starts it
{ //while 1 loop starts----------
ADCwait++; // ADC need not be reated on every loop
if(ADCwait>10)
{
ADCwait=0; // do ADC only count reaches 11
ADCLdrread();
}
} //while 1 loop ends------------
} // main loop ends--------------
///*********************************************************************************************************
//#pragma code functions

//------------------------------------------------------------------------------------------------------
void ADCLdrread(void)
{
ADCON0bits.GO=1;
while(ADCON0bits.GO);     // if conversion over
ADCvolts.ADCbytes[0]=ADRESL;
ADCvolts.ADCbytes[1]=ADRESH; // load ADC result bytes
ADCcurrentValue=(ADCvolts.Volt16Bit/2); // the 16bit number stord 5
}
//-------------------------------------------------------------------------------------------------------
void adc_init(void) // three analogue channels enables ,module on. but ch2 selected
{
ADCON0 = 0b01000001 ; //selecting channel 1 & making ADC module on .
ADCON1 = 0b10000100 ;//making channels AN0 & AN1 and AN2 as analog inputs .
TRISA |= 0b00101011;// three PORTA pins as inputs
ADCON0 &= 0b11000111;
ADCON0 |= 0b00011000; //select channel 3 and set the GO/DONE bit
ADRESH = 0x00;
ADRESL = 0x00;
}
//------------------------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------
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; // ADC Pins as input pins all other pins as output
PORTA = 0X00; // and cleared
TRISC &= 0b11111110; // LCD enable pin output
TRISE = 0B00000111;  //sw pins as input
PORTE = 0X00;
TRISB = 0b00000000;// switch pins as input- all else output
PORTB = 0X00 ; // 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;
LCDFrameFlag=0;cutoffDispFlag=1;
//_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 XC8 function creates a delay of 1 millisecond
} // looping this msCount times will give that much milliseconds delay
}

}
}


EXPLANATIONS

This program is to under stand the working of light-dependent resistor (LDR).its aphotoresistor (or light-dependent resistor,LDR, or photocell) is a light-controlled variable resistor. The resistance of a photoresistor decreases with increasing incident light intensity; in other words, it exhibits photoconductivity. The output from ldr is connected to an adcpin.since 16f877a got a 10 bit adc we get a value from 0 to 1023  this value is divided by 2 to convert it to 0-5.11V.This volt is shown in the lcd and seven segment. We can see the change in the voltage by blocking the light to the LDR

Serial port example of PIC16F877a

CIRCUIT DIAGRAM






SOURCE CODE



#include <xc.h> // this file contains the SFR information of the chip
//----------------------------------------------------------------------------------
#define _XTAL_FREQ     4000000L
//Function prototypes
void port_initialise(void);
void init_variables (void);
void DelayMs(unsigned int msCount);
//--------------------------------------------------------------------------

#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

//--------------------------------------------------------------------------
//#pragma udata access myaccess //Access RAM data which needs fast baccess
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 // normal RAM data (not access)
// Variable dclarations(Global)
unsigned char digit1_var=0,digit2_var=0 ;// holds the digit to be dispalyed
unsigned char PORTDbackup;

//-------------------------------------------------------------------------
// Define

//----------------------------------------------------------------------------
//Function prototypes
void transmit_data(unsigned char tx_data);
unsigned char receive_data(void);
void usart_ini (void);
void lcdstringptr( const char *ptr, unsigned char lineNo);
void stringToSerialPort( const char *ptr);
void Timer1_ini(void );
//--------------------------------------------------------------------------
const char * welcomestring ={"This is PIC16F877A serial Port of your System!"};
//--------------------------------------------------------------------------
//This is the intruupt area.
void interrupt InterruptArea(void)
{

} //This return will be a "retfie".
//-------------------------------------------------------------------------------------------------------------
//#pragma code MainlineCode //****************************************************************************
void main (void)
{ // main loop starts-------------
unsigned char count;
unsigned char RxedData; // 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
usart_ini(); // initialise serial port for 9600 baud
stringToSerialPort(welcomestring); // send a string to serial port


//---------------------------------------------------------------------------------------------------
while(1) // Normally, every MCU programe is a never ending loop. here starts it
{ //while 1 loop starts----------

} //while 1 loop ends------------
} // main loop ends--------------
///*********************************************************************************************************
//#pragma code functions
//-----------------------------------------------------------------------
void Timer1_ini(void )
{
//Timer1 initialisation
T1CON=0b00000001; // prescalar 1:1,internal clock, timer1 started.
PIR1bits.TMR1IF=0; // Timer1 interrupt flag cleared
PIE1bits.TMR1IE=1; // Timer1 interrupt enbled
INTCON |= 0b11000000; // enable app interrupts
}
//----------------------------------------------------------------------
void usart_ini()
{
SPBRG = 25; //baud rate 9600@4Mhx
TXSTA = 0B00100100;
RCSTA = 0B10010000; // see datasheet for configuration
TRISC   =0B11000000;// configure pins as input
PIE1 &=0b11001111; // disable transmit and receive interrupts
}
//-------------------------------------------------------------------------
unsigned char receive_data(void)
{
unsigned char rx_data;
while(!PIR1bits.RCIF);// wait for the rxbuffer to get filled
rx_data = RCREG ;
PIR1bits.RCIF = 0 ;//clear the flag and return
return(rx_data);

}
//--------------------------------------------------------------------------
void transmit_data(unsigned char tx_data)
{
TXREG = tx_data ;
while(!TXSTAbits.TRMT);// wait till transmition is over
}
//----------------------------------------------------------------------------
void stringToSerialPort( const char *ptr)
{
unsigned char loc;
while(1)
{
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
transmit_data(loc); // send each data to serialport
*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;
PORTA = 0X00;
TRISC &= 0b11111110;
TRISE = 0B00000111;
PORTE = 0X00;
TRISB = 0b00000000;
PORTB = 0X00 ;
LEDenable=0; // led array enable transistor off
Digit1_En=0; // digits to be kept low
Digit2_En=0;
}
//*******************************************************************************************************
void init_variables (void)
{
digit1_var=0,digit2_var=0 ;
LCDiniFlag=0;
NOP();  // 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 XC8 function creates a delay of 1 millisecond
} // looping this msCount times will give that much milliseconds delay
}


EXPLANATION

This module actually configures the serial port (RS-232) in PIC to receive and send data at 9600 baud rate. We will demonstrate this by connecting to the serial port of the PC and running a windows application called terminal. The Training kit upon loading this module will listen to the serial port for any data. .
Even though this RS232 module is being demonstrated here as a communication to PC, it can be used to communicate to other devices which are having a standard serial port. In this module we are again doing Seven Segment display in Interrupt. There is a reason for that. Serial communication with 9600 baud rate is not a very fast communication when we are sending strings. So the Display may flicker because of time lags. To avoid this we again moved the display to interrupt so that display will be updated in time.
We need to configure the serial port initially as this is a general purpose peripheral which can handle a wide range of baud rates and a variety of modes.(RS-232 serial port in PC is operating in asynchronous mode, btw!) After we call the USART initialization function the module is ready to send and receive data. Receiving we are doing here on a character basis only, but in the case of transmission we accommodate for strings also. Just like in the case of LCD, we will be placing certain strings in the ROM and fetching them in sequence using pointers and sending them to serial port one by one to be shown in PC side as text.
transmit_data(0x0D);// Line feed and Carriage return to move
//to the next line
transmit_data(0x0A);
These are hex data send to PC after the end of strings. These are actually ASCII values for Carriage return and Line feed, which is the symbols to indicate applications to start new line (Almost like the function of enter key while typing in a word processing program.) We are sending this to host PC to make our strings arranged in lines for visual clarity.
The program logic is simple. After configuration the code will wait for a data to be received in the receive buffer. If received, it will be shown in LCD and in Seven Segment LED (if it is a number) and then echoed back to PC with accompanying sentences. And will wait for the next character to arrive.
The terminal software after starting should be adjusted for 9600 baud and the serial port number you have connected USBMM is also to be selected. Most of PCS will be having only one serial port as general purpose with a number as 1.
Here we are receiving characters only and it is in very low speeds also to be shown in LCD 1 by one. If we need to accept a string we need to avoid all those delays in the code and just push the received characters into a RAM array in PIC which later can be a string.

PIC16F877a ADC Reading

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 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 lcdFrameUpdate(void);
void bin2dec(unsigned int intforBCD);
void ADCtempread(void);
void adc_init(void);
void copyb2dresultcutoff (void);
void copyb2dresultcurrent (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 * tempis   = {" Temparature is "};
const char * degCel   = {"        Deg.Cel "};
const char * cuttoffval  = {"CutOff   .   Cel"};
//--------------------------------------------------------------------------
//#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;
//-------------------------------------------------------------------------
union
{ //This is a union to hold the ADC reading for ADC converter. This ADC result
  unsigned char ADCbytes[2];// we are getting as bytes from ADRSL and ADRSH. But it is 
  unsigned int  Temp16Bit ; // a 10 bit number. So for calculation purpose
}LM35Temp; //we need to process it as a two byte number- so  union
//---------------------------------------------------------------------------
//#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,ADCwait,buzCnt;// wait counts to control the speed of operations
unsigned int ADCcurrentValue,ADCvalueCutoff = 62;
unsigned char b2d_result[5],b2d_resultcutoff[5],b2d_resultcurrent[5]; // arrays for storing bcd results
//-------------------------------------------------------------------------
// Defines

#define  buzzer  PORTBbits.RB5 //  buzzer
#define  LEDenable  PORTCbits.RC3 // led array enable
#define Digit1_En   PORTBbits.RB0
#define Digit2_En   PORTBbits.RB1 // digits enable transistor control
#define  SwUp     PORTEbits.RE0 // up counter
#define  SwDown     PORTEbits.RE1 // Downcounter
#define LCD_EN PORTAbits.RA2
#define LCD_RS PORTDbits.RD5
#define relay  PORTBbits.RB6

#define FlagIncri FLAGAbits.FLAG0 // indicates incriment key pressed
#define FlagDecri FLAGAbits.FLAG1 // decriment
#define FlagDebounce FLAGAbits.FLAG2 // key debounce
#define LCDiniFlag  FLAGAbits.FLAG3
#define LCDFrameFlag  FLAGAbits.FLAG4
#define cutoffDispFlag FLAGAbits.FLAG5
#define cutoffState FLAGAbits.FLAG6
//----------------------------------------------------------------------------
//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)
{
}
//----------------------------------------------------------------------------
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
adc_init(); // initialises ther ADC module
lcd_ini(); // lcd initialise
LCDiniFlag=1; // indicating initialisation is over to use busy flag checking in LCD
//---------------------------------------------------------------------------------------------------
while(1) // Normally, every MCU programe is a never ending loop. here starts it
{ //while 1 loop starts----------
ADCwait++; // ADC need not be reated on every loop
if(ADCwait>10)
{
ADCwait=0;
ADCtempread();
}
lcdFrameUpdate(); // here lcd messages change

} //while 1 loop ends------------
} // main loop ends--------------
///*********************************************************************************************************
//#pragma code functions

void ADCtempread(void)
{
ADCON0 &= 0b11000111; //select channel 1 and set the GO/DONE bit
ADCON0bits.GO=1;
while(ADCON0bits.GO);     // if conversion over
LM35Temp.ADCbytes[0]=ADRESL;
LM35Temp.ADCbytes[1]=ADRESH; // load ADC result bytes
ADCcurrentValue=LM35Temp.Temp16Bit; // the 16bit number stord to disaply
LM35Temp.Temp16Bit= 48*(LM35Temp.Temp16Bit); //multiply by 48
bin2dec(LM35Temp.Temp16Bit); //now result is a 16 bit number, we need to extract it as digits
copyb2dresultcurrent(); // bcd conversion done and digits copied to a array for dispaly
Digit1=(b2d_resultcurrent[1]-0x30); Digit2=(b2d_resultcurrent[2]-0x30);// digits are in ASCII for LCD
} //30h is substracted to extract the value only .
//-------------------------------------------------------------------------------------------------------
void adc_init(void) // three analogue channels enables ,module on. but ch0 selected
{
ADCON0 = 0b01000001 ; //selecting channel 1 & making ADC module on .
ADCON1 = 0b10000100 ;//making channels AN0 & AN1 and AN2 as analog inputs .
TRISA |= 0b00001011;// three PORTA pins as inputs 
ADRESH = 0x00;
ADRESL = 0x00;
}
//------------------------------------------------------------------------------------------------------
//This function converts a 16 bit number to ascii
void bin2dec(unsigned int intforBCD)
{
 unsigned char count;
   count=5;
   do
count--; // to extract the bcd digits modulas of 10 is arrived
     b2d_result[count] = '0' + (intforBCD % 10); //add '0' that is 30h for ASCII
     intforBCD /= 10; // divide by 10 and repeat the same thing 4 times
} while(count);
}
//------------------------------------------------------------------------------------------------------
void lcdFrameUpdate(void)
{// this function arranges different messages to appear in LCD sequentially
FrameWait++;
if(FrameWait>100) // this value determions the speed at which messages change in lcd
{
if(LCDFrameFlag)
{
lcdstringptr(USBMicro,1);
if(cutoffDispFlag)
{
LM35Temp.Temp16Bit=ADCvalueCutoff; // here cut off value is converted to digits and shown in lcd
LM35Temp.Temp16Bit= 48*(LM35Temp.Temp16Bit);
bin2dec(LM35Temp.Temp16Bit);
copyb2dresultcutoff();
lcdstringptr(cuttoffval,2);
lcd_writeCMD(0xC7);
lcd_writeDAT(b2d_resultcutoff[1]);
lcd_writeDAT(b2d_resultcutoff[2]);
lcd_writeDAT('.');
lcd_writeDAT(b2d_resultcutoff[3]);
lcd_writeDAT(b2d_resultcutoff[4]);
lcd_writeCMD(0xF0);// fix cursor out side dispaly address to avoid cursor
cutoffDispFlag=0;
}
else
{
lcdstringptr(EmbCkit,2);
cutoffDispFlag=1;
}
LCDFrameFlag=0;
}
else
{
lcdstringptr(tempis,1); // here normal tempararure is shown
lcdstringptr(degCel,2);
lcd_writeCMD(0xC1);
lcd_writeDAT(b2d_resultcurrent[1]);
lcd_writeDAT(b2d_resultcurrent[2]);
lcd_writeDAT('.');
lcd_writeDAT(b2d_resultcurrent[3]);
lcd_writeDAT(b2d_resultcurrent[4]);
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;
LCDFrameFlag=0;cutoffDispFlag=1;
//_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
NOP();
}
    LCD_RS=1; //LCD to data 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.....
}
//*************************************************************************************************
//LCD needs some time to update display as it is a slow device. So before sending data to-
//LCD sequentially we must make sure LCD has finished work on previous data- this function checks it
//If LCD busy 7th bit of data read from LCD will be high. See LCD datasheet
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;
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
}
//***************************************************************************************************
void cutofftempsetting(void) // This function polls the keys and incriments or deciments
{ // the cut off value
if(FlagIncri) // if incriment flag is set by switch Up
{
ADCvalueCutoff++;
FlagIncri=0;
if(ADCvalueCutoff>312) // LM35 has a maximon temp of 150 Degree celcious is 1500mVolts
ADCvalueCutoff=0; // (1500/4.8) ~ 312 . So 312 is the maximum limit
} // incriment end
//----------------------------------------------------------------------------------------------------
if(FlagDecri) // if decriment flag is set by down switch
{
ADCvalueCutoff--;
FlagDecri=0;
if(ADCvalueCutoff<1)
ADCvalueCutoff=312;
}
//-----------------------------------------------------------------------------------------------------
if(!FlagDebounce) // if debounce period is over then only poll keys
{
if(!SwUp)
{
FlagIncri=1; // if key presed indicate flag ,set debounce flag and on buzzer
FlagDebounce=1;
buzzer=1;
}
if(!SwDown)
{
FlagDecri=1;
FlagDebounce=1;
buzzer=1;
}
}
//------------------------------------------------------------------------------------------------------
// for a debounce time avoid polling keys to elliminate multiple keypress on a single stroke.
if(FlagDebounce)
{
DebounceCnt++;
if(DebounceCnt==4) // The buzzer needs a  small beep only.
buzzer=0; // So clear it before debounce over.
if(DebounceCnt>8)
{
FlagDebounce=0; // clear debounce flag and counter
DebounceCnt=0;
}
}
}
//******************************************************************************************************
void copyb2dresultcutoff (void)// These functions copies result array to another one for
{ // We can use pointers to direct results to different arrays-
unsigned char count; //-Here avoided to make things simple
for(count=0;count<5;count++)
{
b2d_resultcutoff[count]=b2d_result[count];
}
}
//-----------------------------------------------------------------------------------------------------
void copyb2dresultcurrent (void)
{
unsigned char count;
for(count=0;count<5;count++)
{
b2d_resultcurrent[count]=b2d_result[count];
}
}
//------------------------------------------------------------------------------------------------------

EXPLANATION

This program basically reads a analogue temperature sensor and displays the temperature in LCD. The sensor used is easily available and low cost temperature sensor LM35. It is a internally calibrated integrated sensor which will give a voltage of 10 mille Volt/Degree Celsius. That means if the current temperature ‘felt’ by LM35 is 0 Degree Celsius, its output pin will give 0 Volts. From then for every degree Celsius rise, the voltage will go up by 10 mille volt.
For example if the Ambient temperature is 30 Degree Celsius, LM35 output pin will give 30*10milli Volt = 300 mille Volt which is .3 Volt. So to get the temperature data from this sensor all we have to do is measure the voltage using an analogue to digital converter which is built in PIC and then divide the result in mill volts by 10 which will give the real temperature in Degree Celsius.
Analog to Digital Converter is a peripheral (like Timers) available in PIC forconverting Analogue data (Most of the real world data are analog, btw!) to digital form so that Microcontroller can process, display or operate upon them. 16F877A is having 10 bit ADC which means, as the analogue voltage varies from its low limit to high limit (Decided by the reference of ADC), in our case 0V to +5V, The digital result will vary from 0 to 1023.
Just like timers ADC’s are designed with a wide variety of users in mind, so we need to configure them before using in our application. ADC peripheral is being initialized in the adc_ini function. Details are available in the data sheet. We need to specify the pins where Sensor is connected and all. Once we command the module to start conversion, we can check a particular bit to see weather conversion is over. Once it is over, we have a result which can be processed by MCU which are available in ADC result registers.
As already discussed, 10 bit ADC is present in this chip. So Maximum value is 1023 and minimum is 0. We set reference for ADC as 5 Volts. So 1 ADC result equals 4.8mV, i.e. (5000/1024) Mille Volts. (ADC result*4.8) will give voltage in mill volts. Also LM35 has a output of 10mV/Degree Celsius. LM35 output m Volts/10 will give Degree Celsius. So ((ADC result)*4.8)/10 = (ADC result)*.48 = Temperature in Degree Celsius. To avoid fraction calculation we multiply (ADC result) by 48 instead of .48 and induce a decimal point on the 100th digit position to effect a divide by 100. To put it straightly, we need to read the ADC in 10 bit mode, multiply by 48 and introduce two decimal places while displaying the value. We will get a resolution of almost .5 degree Celsius by this calculation which is acceptable when considering what is assured by LM35 specifications
The ADC result is a 10 bit number and on multiplying it with 48 we will be getting a number which is 16 bits or lower width. LM35 is not supposed to withstand such high temperatures and it is not expected in our case anyway. Still in principle we need to process a 16 bit result, expecting the worst case scenarios.
Let us consider a possible situation - current temperature is 30° C. Obviously LM35 output pin will be having 300 mill Volts (10mV/° Celsius). So when the ADC reads this we will get a result of 62 or 63 (62.5 is the exact but only whole numbers are possible).Now we will multiply this by 48. 63*48 = 3024. Now if we introduce two decimal places while display the result can be 30.24° Celsius. But what we have in PIC’s internal Registers is a 16 bit number 3024, which needs to be split up into 3, 0, 2, and 4. Then only it can be shown in our display. Now just as we do in elementary mathematics, we need to locate how many thousands in it – 3. Then in the remaining part how many hundreds- nil. Then how many 10s are left -2. And how many is the ultimate 1s? –it is 4!. Yeah!! What we have just done is BCD conversion! We need to do it in software to derive the digits.
Now we have derived means to show temperature in LCD. Our remaining task is to ON/OFF the relay in board when the temperature rises above a certain limit. We have specified a cut off temperature which is showing in LCD in alternate frames, which can be adjusted up or down by our UP/DOWN switches. Our code should check whether the ADC result or rather temperature has exceeded that limit. We have created a flag to indicate whether the relay is in cut off mode or not and the value comparison is done on separate routes. The more simple method is just provide an ‘if’ statement. If ADC greater, relay ON, else relay OFF. But that will cause the relay to chatter when the ADC result will be jogging around our cut off value. So always better to provide a hysteresis ‘window’ in such switching decision loops. We have provided a margin value also here which can be adjusted if your sensor gives more jittery values by any reason,
if(ADCcurrentValue<(ADCvalueCutoff-1)) //check weather current
//value gone below. This -1can be -2 if needed.
In Data Memmory area we can see some new declarations.
union
{
unsigned char ADCbytes[2];// As from registers
unsignedint Temp16Bit ; // for calculation purpose
}LM35Temp;
This a union declaration in C. Even though we say ADC result is a 10 bit number, our controller’s Data memory is only 8 bit wide (That is why it is called 8 bit controller). So the 10 bit number will be available through two 8 bit registers. i.e ADC result will be present in ADRSL and ADRSH registers. So they are physically in two registers but for calculation we need to handle them as single 16 bit number by attaching together- there comes unions. Our LM35Temp Union is a variable which we can access as two bytes and a single 16 bit number. For loading ADC we approach this variable as bytes and for calculation we will tackle them a single 16 bit number.
unsigned char b2d_result[5], b2d_resultcutoff[5], b2d_resultcurrent[5];// arrays for storing bcd results
These are arrays implemented in PIC’s data memory to hold the result digits of calculations so that we can load them to display at the proper time to update display in a round robin fashion. As the LCD needs each numeral to be in ASCII format to display properly , we are storing these digits in ASCII format in these arrays.
We are using a variable ADCwaitTo slow down the ADC conversion frequency. Temperature is not a very fast changing parameter, at least in our test conditions. So we don’t have to convert at very high sampling rates. But to slow down the sampling rate without affecting the other code segments we are using this variable as a ‘reducer’.


Like in the previous examples we are handling switch and buzzer with de bounce to adjust the cut–off temperature. That cut off value will be displayed in LCD also

Interfacing LM35 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 lcdFrameUpdate(void);
void bin2dec(unsigned int intforBCD);
void ADCtempread(void);
void adc_init(void);
void copyb2dresultcutoff (void);
void copyb2dresultcurrent (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 * tempis   = {" Temparature is "};
const char * degCel   = {"        Deg.Cel "};
const char * cuttoffval = {"CutOff   .   Cel"};
//--------------------------------------------------------------------------
//#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;
//-------------------------------------------------------------------------
union
{ //This is a union to hold the ADC reading for ADC converter. This ADC result
  unsigned char ADCbytes[2];// we are getting as bytes from ADRSL and ADRSH. But it is
  unsigned int  Temp16Bit ; // a 10 bit number. So for calculation purpose
}LM35Temp; //we need to process it as a two byte number- so  union
//---------------------------------------------------------------------------
//#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,ADCwait,buzCnt;// wait counts to control the speed of operations
unsigned int ADCcurrentValue,ADCvalueCutoff = 62;
unsigned char b2d_result[5],b2d_resultcutoff[5],b2d_resultcurrent[5]; // arrays for storing bcd results
//-------------------------------------------------------------------------
// Defines

#define  buzzer PORTBbits.RB5 //  buzzer
#define  LEDenable  PORTCbits.RC3 // led array enable
#define Digit1_En   PORTBbits.RB0
#define Digit2_En   PORTBbits.RB1 // digits enable transistor control
#define  SwUp    PORTEbits.RE0 // up counter
#define  SwDown    PORTEbits.RE1 // Downcounter
#define LCD_EN PORTAbits.RA2
#define LCD_RS PORTDbits.RD5
#define relay PORTBbits.RB6

#define FlagIncri FLAGAbits.FLAG0 // indicates incriment key pressed
#define FlagDecri FLAGAbits.FLAG1 // decriment
#define FlagDebounce FLAGAbits.FLAG2 // key debounce
#define LCDiniFlag  FLAGAbits.FLAG3
#define LCDFrameFlag  FLAGAbits.FLAG4
#define cutoffDispFlag FLAGAbits.FLAG5
#define cutoffState FLAGAbits.FLAG6
//----------------------------------------------------------------------------
//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)
{
}
//----------------------------------------------------------------------------
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
adc_init(); // initialises ther ADC module
lcd_ini(); // lcd initialise
LCDiniFlag=1; // indicating initialisation is over to use busy flag checking in LCD
//---------------------------------------------------------------------------------------------------
while(1) // Normally, every MCU programe is a never ending loop. here starts it
{ //while 1 loop starts----------
ADCwait++; // ADC need not be reated on every loop
if(ADCwait>10)
{
ADCwait=0;
ADCtempread();
}
lcdFrameUpdate(); // here lcd messages change

} //while 1 loop ends------------
} // main loop ends--------------
///*********************************************************************************************************
//#pragma code functions

void ADCtempread(void)
{
ADCON0 &= 0b11000111; //select channel 1 and set the GO/DONE bit
ADCON0bits.GO=1;
while(ADCON0bits.GO);     // if conversion over
LM35Temp.ADCbytes[0]=ADRESL;
LM35Temp.ADCbytes[1]=ADRESH; // load ADC result bytes
ADCcurrentValue=LM35Temp.Temp16Bit; // the 16bit number stord to disaply
LM35Temp.Temp16Bit= 48*(LM35Temp.Temp16Bit); //multiply by 48
bin2dec(LM35Temp.Temp16Bit); //now result is a 16 bit number, we need to extract it as digits
copyb2dresultcurrent(); // bcd conversion done and digits copied to a array for dispaly
Digit1=(b2d_resultcurrent[1]-0x30); Digit2=(b2d_resultcurrent[2]-0x30);// digits are in ASCII for LCD
} //30h is substracted to extract the value only .
//-------------------------------------------------------------------------------------------------------
void adc_init(void) // three analogue channels enables ,module on. but ch0 selected
{
ADCON0 = 0b01000001 ; //selecting channel 1 & making ADC module on .
ADCON1 = 0b10000100 ;//making channels AN0 & AN1 and AN2 as analog inputs .
TRISA |= 0b00001011;// three PORTA pins as inputs
ADRESH = 0x00;
ADRESL = 0x00;
}
//------------------------------------------------------------------------------------------------------
//This function converts a 16 bit number to ascii
void bin2dec(unsigned int intforBCD)
{
unsigned char count;
  count=5;
  do
{ count--; // to extract the bcd digits modulas of 10 is arrived
    b2d_result[count] = '0' + (intforBCD % 10); //add '0' that is 30h for ASCII
    intforBCD /= 10; // divide by 10 and repeat the same thing 4 times
} while(count);
}
//------------------------------------------------------------------------------------------------------
void lcdFrameUpdate(void)
{// this function arranges different messages to appear in LCD sequentially
FrameWait++;
if(FrameWait>100) // this value determions the speed at which messages change in lcd
{
if(LCDFrameFlag)
{
lcdstringptr(USBMicro,1);
if(cutoffDispFlag)
{
LM35Temp.Temp16Bit=ADCvalueCutoff; // here cut off value is converted to digits and shown in lcd
LM35Temp.Temp16Bit= 48*(LM35Temp.Temp16Bit);
bin2dec(LM35Temp.Temp16Bit);
copyb2dresultcutoff();
lcdstringptr(cuttoffval,2);
lcd_writeCMD(0xC7);
lcd_writeDAT(b2d_resultcutoff[1]);
lcd_writeDAT(b2d_resultcutoff[2]);
lcd_writeDAT('.');
lcd_writeDAT(b2d_resultcutoff[3]);
lcd_writeDAT(b2d_resultcutoff[4]);
lcd_writeCMD(0xF0);// fix cursor out side dispaly address to avoid cursor
cutoffDispFlag=0;
}
else
{
lcdstringptr(EmbCkit,2);
cutoffDispFlag=1;
}
LCDFrameFlag=0;
}
else
{
lcdstringptr(tempis,1); // here normal tempararure is shown
lcdstringptr(degCel,2);
lcd_writeCMD(0xC1);
lcd_writeDAT(b2d_resultcurrent[1]);
lcd_writeDAT(b2d_resultcurrent[2]);
lcd_writeDAT('.');
lcd_writeDAT(b2d_resultcurrent[3]);
lcd_writeDAT(b2d_resultcurrent[4]);
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;
LCDFrameFlag=0;cutoffDispFlag=1;
//_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
NOP();
}
    LCD_RS=1; //LCD to data 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.....
}
//*************************************************************************************************
//LCD needs some time to update display as it is a slow device. So before sending data to-
//LCD sequentially we must make sure LCD has finished work on previous data- this function checks it
//If LCD busy 7th bit of data read from LCD will be high. See LCD datasheet
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;
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
}
//***************************************************************************************************
void cutofftempsetting(void) // This function polls the keys and incriments or deciments
{ // the cut off value
if(FlagIncri) // if incriment flag is set by switch Up
{
ADCvalueCutoff++;
FlagIncri=0;
if(ADCvalueCutoff>312) // LM35 has a maximon temp of 150 Degree celcious is 1500mVolts
ADCvalueCutoff=0; // (1500/4.8) ~ 312 . So 312 is the maximum limit
} // incriment end
//----------------------------------------------------------------------------------------------------
if(FlagDecri) // if decriment flag is set by down switch
{
ADCvalueCutoff--;
FlagDecri=0;
if(ADCvalueCutoff<1)
ADCvalueCutoff=312;
}
//-----------------------------------------------------------------------------------------------------
if(!FlagDebounce) // if debounce period is over then only poll keys
{
if(!SwUp)
{
FlagIncri=1; // if key presed indicate flag ,set debounce flag and on buzzer
FlagDebounce=1;
buzzer=1;
}
if(!SwDown)
{
FlagDecri=1;
FlagDebounce=1;
buzzer=1;
}
}
//------------------------------------------------------------------------------------------------------
// for a debounce time avoid polling keys to elliminate multiple keypress on a single stroke.
if(FlagDebounce)
{
DebounceCnt++;
if(DebounceCnt==4) // The buzzer needs a  small beep only.
buzzer=0; // So clear it before debounce over.
if(DebounceCnt>8)
{
FlagDebounce=0; // clear debounce flag and counter
DebounceCnt=0;
}
}
}
//******************************************************************************************************
void copyb2dresultcutoff (void)// These functions copies result array to another one for
{ // We can use pointers to direct results to different arrays-
unsigned char count; //-Here avoided to make things simple
for(count=0;count<5;count++)
{
b2d_resultcutoff[count]=b2d_result[count];
}
}
//-----------------------------------------------------------------------------------------------------
void copyb2dresultcurrent (void)
{
unsigned char count;
for(count=0;count<5;count++)
{
b2d_resultcurrent[count]=b2d_result[count];
}
}
//------------------------------------------------------------------------------------------------------

EXPLANATION


This program basically reads a analogue temperature sensor and displays the temperature in LCD. The sensor used is easily available and low cost temperature sensor LM35. It is a internally calibrated integrated sensor which will give a voltage of 10 mille Volt/Degree Celsius. That means if the current temperature ‘felt’ by LM35 is 0 Degree Celsius, its output pin will give 0 Volts. From then for every degree Celsius rise, the voltage will go up by 10 mille volt.
For example if the Ambient temperature is 30 Degree Celsius, LM35 output pin will give 30*10milli Volt = 300 mille Volt which is .3 Volt. So to get the temperature data from this sensor all we have to do is measure the voltage using an analogue to digital converter which is built in PIC and then divide the result in mill volts by 10 which will give the real temperature in Degree Celsius.
Analog to Digital Converter is a peripheral (like Timers) available in PIC forconverting Analogue data (Most of the real world data are analog, btw!) to digital form so that Microcontroller can process, display or operate upon them. 16F877A is having 10 bit ADC which means, as the analogue voltage varies from its low limit to high limit (Decided by the reference of ADC), in our case 0V to +5V, The digital result will vary from 0 to 1023.
Just like timers ADC’s are designed with a wide variety of users in mind, so we need to configure them before using in our application. ADC peripheral is being initialized in the adc_ini function. Details are available in the data sheet. We need to specify the pins where Sensor is connected and all. Once we command the module to start conversion, we can check a particular bit to see weather conversion is over. Once it is over, we have a result which can be processed by MCU which are available in ADC result registers.
As already discussed, 10 bit ADC is present in this chip. So Maximum value is 1023 and minimum is 0. We set reference for ADC as 5 Volts. So 1 ADC result equals 4.8mV, i.e. (5000/1024) Mille Volts. (ADC result*4.8) will give voltage in mill volts. Also LM35 has a output of 10mV/Degree Celsius. LM35 output m Volts/10 will give Degree Celsius. So ((ADC result)*4.8)/10 = (ADC result)*.48 = Temperature in Degree Celsius. To avoid fraction calculation we multiply (ADC result) by 48 instead of .48 and induce a decimal point on the 100th digit position to effect a divide by 100. To put it straightly, we need to read the ADC in 10 bit mode, multiply by 48 and introduce two decimal places while displaying the value. We will get a resolution of almost .5 degree Celsius by this calculation which is acceptable when considering what is assured by LM35 specifications
The ADC result is a 10 bit number and on multiplying it with 48 we will be getting a number which is 16 bits or lower width. LM35 is not supposed to withstand such high temperatures and it is not expected in our case anyway. Still in principle we need to process a 16 bit result, expecting the worst case scenarios.
Let us consider a possible situation - current temperature is 30° C. Obviously LM35 output pin will be having 300 mill Volts (10mV/° Celsius). So when the ADC reads this we will get a result of 62 or 63 (62.5 is the exact but only whole numbers are possible).Now we will multiply this by 48. 63*48 = 3024. Now if we introduce two decimal places while display the result can be 30.24° Celsius. But what we have in PIC’s internal Registers is a 16 bit number 3024, which needs to be split up into 3, 0, 2, and 4. Then only it can be shown in our display. Now just as we do in elementary mathematics, we need to locate how many thousands in it – 3. Then in the remaining part how many hundreds- nil. Then how many 10s are left -2. And how many is the ultimate 1s? –it is 4!. Yeah!! What we have just done is BCD conversion! We need to do it in software to derive the digits.
Now we have derived means to show temperature in LCD. Our remaining task is to ON/OFF the relay in board when the temperature rises above a certain limit. We have specified a cut off temperature which is showing in LCD in alternate frames, which can be adjusted up or down by our UP/DOWN switches. Our code should check whether the ADC result or rather temperature has exceeded that limit. We have created a flag to indicate whether the relay is in cut off mode or not and the value comparison is done on separate routes. The more simple method is just provide an ‘if’ statement. If ADC greater, relay ON, else relay OFF. But that will cause the relay to chatter when the ADC result will be jogging around our cut off value. So always better to provide a hysteresis ‘window’ in such switching decision loops. We have provided a margin value also here which can be adjusted if your sensor gives more jittery values by any reason,
if(ADCcurrentValue<(ADCvalueCutoff-1)) //check weather current
//value gone below. This -1can be -2 if needed.
In Data Memmory area we can see some new declarations.
union
{
unsigned char ADCbytes[2];// As from registers
unsignedint Temp16Bit ; // for calculation purpose
}LM35Temp;
This a union declaration in C. Even though we say ADC result is a 10 bit number, our controller’s Data memory is only 8 bit wide (That is why it is called 8 bit controller). So the 10 bit number will be available through two 8 bit registers. i.e ADC result will be present in ADRSL and ADRSH registers. So they are physically in two registers but for calculation we need to handle them as single 16 bit number by attaching together- there comes unions. Our LM35Temp Union is a variable which we can access as two bytes and a single 16 bit number. For loading ADC we approach this variable as bytes and for calculation we will tackle them a single 16 bit number.
unsigned char b2d_result[5], b2d_resultcutoff[5], b2d_resultcurrent[5];// arrays for storing bcd results
These are arrays implemented in PIC’s data memory to hold the result digits of calculations so that we can load them to display at the proper time to update display in a round robin fashion. As the LCD needs each numeral to be in ASCII format to display properly , we are storing these digits in ASCII format in these arrays.
We are using a variable ADCwaitTo slow down the ADC conversion frequency. Temperature is not a very fast changing parameter, at least in our test conditions. So we don’t have to convert at very high sampling rates. But to slow down the sampling rate without affecting the other code segments we are using this variable as a ‘reducer’.
Like in the previous examples we are handling switch and buzzer with de bounce to adjust the cut–off temperature. That cut off value will be displayed in LCD also