
Project: Temperature Monitoring System with LCD Display Using PIC16F877A
1. Introduction
This project demonstrates how to interface an LM35 temperature sensor with a PIC16F877A microcontroller and display the temperature readings on a 16×2 LCD. The system continuously monitors the temperature and updates the display in real time.
2. Learning Objectives
By completing this project, you will:
- Understand ADC (Analog-to-Digital Conversion) in PIC microcontrollers.
- Learn how to interface an LCD with a PIC16F877A.
- Work with an LM35 temperature sensor to measure temperature.
- Develop and debug embedded C code using MPLAB X and the XC8 compiler.
3. Components and Tools Required
Hardware Components:
- PIC16F877A microcontroller
- LM35 temperature sensor
- 16×2 LCD display
- 10kΩ potentiometer (for LCD contrast adjustment)
- 10kΩ resistor (pull-down resistor for ADC stability)
- Crystal oscillator (20MHz) (for stable clock frequency)
- Two 22pF capacitors (for oscillator stabilization)
- 330Ω resistor (for LCD backlight control)
- Breadboard and jumper wires
Software Tools:
- MPLAB X IDE (for writing and compiling the code)
- XC8 Compiler (for compiling C programs)
- PICkit Programmer (for burning the code to PIC16F877A)
4. Circuit Connections
The key circuit connections are as follows:
- LM35 sensor:
- VCC → 5V
- GND → Ground
- VOUT → RA0 (AN0) of PIC16F877A (ADC input)
- 16×2 LCD (in 4-bit mode):
- RS (Register Select) → RD0
- EN (Enable) → RD1
- D4-D7 (Data Pins) → RD4-RD7
- VEE (Contrast Pin) → Center pin of 10kΩ potentiometer
- VSS (GND) & VDD (Power) → GND & 5V
- Oscillator Circuit:
- 20MHz crystal oscillator connected to OSC1 (Pin 13) and OSC2 (Pin 14)
- 22pF capacitors between crystal terminals and ground
5. Code Implementation (MPLAB XC8)
This program reads the LM35 sensor’s output, converts it to a temperature value, and displays it on the 16×2 LCD.
#include <xc.h>
#include <stdio.h>
#define _XTAL_FREQ 20000000 // Define crystal frequency
#define RS RD0
#define EN RD1
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
// Function prototypes
void ADC_Init();
unsigned int ADC_Read(unsigned char);
void LCD_Init();
void LCD_Command(unsigned char);
void LCD_Char(unsigned char);
void LCD_String(const char *);
void LCD_Clear();
void LCD_Set_Cursor(unsigned char, unsigned char);
void main() {
unsigned int adc_value;
float temperature;
char buffer[16];
TRISA = 0xFF; // Configure PORTA as input (ADC)
TRISD = 0x00; // Configure PORTD as output (LCD)
ADC_Init();
LCD_Init();
while(1) {
adc_value = ADC_Read(0); // Read from AN0
temperature = (adc_value * 4.88) / 10.0; // Convert ADC value to temperature (°C)
LCD_Clear();
LCD_Set_Cursor(1,1);
LCD_String("Temp: ");
sprintf(buffer, "%.2f C", temperature);
LCD_String(buffer);
__delay_ms(1000); // Refresh every second
}
}
// Initialize ADC Module
void ADC_Init() {
ADCON0 = 0x41; // Enable ADC, select channel 0
ADCON1 = 0x80; // Set result format (right justified)
}
// Read ADC Value
unsigned int ADC_Read(unsigned char channel) {
ADCON0 &= 0xC3; // Clear channel bits
ADCON0 |= (channel << 3); // Select channel
__delay_ms(2); // Acquisition delay
GO_nDONE = 1; // Start conversion
while(GO_nDONE); // Wait for conversion to complete
return ((ADRESH << 8) + ADRESL); // Return 10-bit result
}
// Initialize LCD
void LCD_Init() {
LCD_Command(0x02); // Initialize LCD in 4-bit mode
LCD_Command(0x28); // Function set
LCD_Command(0x0C); // Display ON, Cursor OFF
LCD_Command(0x06); // Entry mode
LCD_Command(0x01); // Clear display
}
// Send Command to LCD
void LCD_Command(unsigned char cmd) {
RS = 0;
D4 = (cmd >> 4) & 1;
D5 = (cmd >> 5) & 1;
D6 = (cmd >> 6) & 1;
D7 = (cmd >> 7) & 1;
EN = 1; __delay_ms(1); EN = 0;
D4 = cmd & 1;
D5 = (cmd >> 1) & 1;
D6 = (cmd >> 2) & 1;
D7 = (cmd >> 3) & 1;
EN = 1; __delay_ms(1); EN = 0;
}
// Send Character to LCD
void LCD_Char(unsigned char data) {
RS = 1;
LCD_Command(data);
}
// Send String to LCD
void LCD_String(const char *str) {
while(*str) LCD_Char(*str++);
}
// Clear LCD
void LCD_Clear() {
LCD_Command(0x01);
}
// Set Cursor Position
void LCD_Set_Cursor(unsigned char row, unsigned char column) {
unsigned char pos[] = {0x80, 0xC0}; // Line 1 & 2
LCD_Command(pos[row-1] + (column-1));
}
6. Troubleshooting Guide
LCD Not Displaying Anything:
- Check if the contrast potentiometer is adjusted properly.
- Ensure all wiring connections to the LCD are correct.
Incorrect Temperature Readings:
- Verify the LM35 connections (VCC, GND, VOUT).
- Check if ADC conversion formula is correct.
No ADC Output:
- Ensure ADC module is enabled properly in the code (
ADCON0, ADCON1). - Confirm RA0 is set as an input in
TRISA = 0xFF.
LCD Shows Garbage Characters:
- Check if LCD initialization sequence is followed properly.
- Add delays (
__delay_ms(1)) between LCD commands to stabilize communication.
7. Possible Extensions
- Add a buzzer to alert when temperature exceeds a predefined threshold.
- Display temperature in Fahrenheit alongside Celsius.
- Store temperature data in EEPROM for logging and retrieval.
- Implement a wireless transmission system using Bluetooth or RF modules to send temperature data to a remote system.
This project is a great introduction to ADC interfacing, LCD handling, and embedded C programming for PIC microcontrollers. Let me know if you need any modifications or explanations. 🚀