In the previous blog post Interfacing LCD with PIC16F877A: Displaying Sensor Data it was shown how to interface a 16x2 LCD with PIC16F877A and alike PIC microcontroller. In that post, circuit diagram and program code for communicating with the LCD was provided. Here I just wanted to post an alternative C program code for inter-connecting LCD and the PIC microcontroller.
C program code
#define _XTAL_FREQ 16000000 // Define operating frequency
#include <xc.h>
#include <stdio.h> // For sprintf()
// CONFIGURATION BITS
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
// LCD pin configuration
#define RS RB0
#define EN RB1
#define D4 RB2
#define D5 RB3
#define D6 RB4
#define D7 RB5
#define delay for(j=0;j<1000;j++)
int j;
void lcd_init();
void lcd_cmd(unsigned char cmd);
void lcd_data(unsigned char data);
void lcd_write_4bits(unsigned char data);
void print(unsigned char *s);
void adc_init();
unsigned int read_adc();
void display_temperature(unsigned int temp);
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & CP_OFF & BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);
void main() {
unsigned int adc_value;
unsigned int temperature;
// Configure LCD pins
TRISB = 0x00; // Set PORTB as output for LCD control and data lines
lcd_init(); // Initialize the LCD
adc_init(); // Initialize ADC
lcd_cmd(0x80); // Set cursor at beginning of first line
print("Temperature:"); // Display label
while (1) {
adc_value = read_adc(); // Read ADC value from AN0 (LM35)
temperature = adc_value * 0.488; // Convert ADC value to temperature (LM35 gives 10mV/°C)
display_temperature(temperature); // Display the temperature on the LCD
__delay_ms(1000); // Delay 1 second before next reading
}
}
void lcd_init() {
lcd_cmd(0x33); // Initialization sequence for 4-bit mode
lcd_cmd(0x32); // Initialization sequence for 4-bit mode
lcd_cmd(0x28); // 4-bit mode, 2-line display, 5x7 font
lcd_cmd(0x0C); // Display on, cursor off
lcd_cmd(0x06); // Entry mode, increment cursor
lcd_cmd(0x01); // Clear display
}
void lcd_cmd(unsigned char cmd) {
RS = 0; // Command mode
lcd_write_4bits(cmd >> 4); // Send higher nibble
lcd_write_4bits(cmd & 0x0F); // Send lower nibble
}
void lcd_data(unsigned char data) {
RS = 1; // Data mode
lcd_write_4bits(data >> 4); // Send higher nibble
lcd_write_4bits(data & 0x0F); // Send lower nibble
}
void lcd_write_4bits(unsigned char data) {
D4 = (data >> 0) & 1;
D5 = (data >> 1) & 1;
D6 = (data >> 2) & 1;
D7 = (data >> 3) & 1;
EN = 1; // Enable pulse
delay;
EN = 0; // Disable pulse
}
void print(unsigned char *s) {
while (*s) {
lcd_data(*s++);
}
}
void adc_init() {
ADCON0 = 0x41; // ADCON0: ADON = 1 (ADC enabled), Channel 0 (AN0)
ADCON1 = 0x80; // ADCON1: Right Justified, A/D Port Configuration Bits
}
unsigned int read_adc() {
__delay_ms(2); // Acquisition time
GO_nDONE = 1; // Start conversion
while (GO_nDONE); // Wait for conversion to finish
return (ADRESH << 8) + ADRESL; // Combine 8-bit high and low registers
}
void display_temperature(unsigned int temp) {
lcd_cmd(0x8C); // Set cursor to position to display temperature
lcd_data((temp / 100) + 48); // Hundreds place
lcd_data(((temp / 10) % 10) + 48); // Tens place
lcd_data((temp % 10) + 48); // Units place
lcd_data(0xDF); // Degree symbol
lcd_data('C'); // 'C' for Celsius
}The following is the circuit diagram.
