
đź”§ Project Title: Temperature-Controlled Fan using PIC16F877A
đź§ 1. Introduction
This project demonstrates how to automatically control a fan’s speed based on the ambient temperature using a PIC16F877A microcontroller and an LM35 temperature sensor. The fan will increase speed as the temperature rises — a concept commonly used in computers, HVAC systems, and other electronic cooling applications.
🎯 2. Learning Objectives
By completing this project, you will:
- Understand analog-to-digital conversion (ADC) on the PIC16F877A.
- Learn how to read temperature using an LM35 sensor.
- Generate PWM (Pulse Width Modulation) signals using the CCP1 module.
- Interface and control a DC fan using PWM and a motor driver.
- Display real-time temperature on an LCD.
- Gain experience with debugging real-time control systems.
đź§° 3. Tools and Components
Hardware Required:
- PIC16F877A microcontroller
- LM35 temperature sensor
- 16×2 LCD display
- 12V DC fan or 5V brushless motor
- L293D motor driver or NPN transistor (e.g., TIP122)
- 10k potentiometer (for LCD contrast)
- 20 MHz crystal oscillator
- Two 22pF capacitors
- 10kΩ and 1kΩ resistors
- Breadboard and jumper wires
- Power supply (5V for PIC, 12V for fan)
Software Required:
- MPLAB X IDE
- XC8 compiler
- Proteus (optional for simulation and visualization)
📚 4. Background and Key Concepts
LM35: This is a precision temperature sensor with linear output of 10 mV/°C. At 25°C, it outputs 250 mV.
ADC (Analog to Digital Converter): Converts analog voltages (from LM35) into 10-bit digital values for processing by the PIC microcontroller.
PWM (Pulse Width Modulation): A digital technique where the width of the ON pulse is varied to simulate analog control — used to adjust fan speed.
CCP1 Module: Capture/Compare/PWM module in the PIC16F877A. In PWM mode, it is used to generate precise pulse widths.
🛠️ 5. Step-by-Step Implementation
Circuit Setup:
- Connect the LM35 sensor’s Vout to RA0 (AN0) on the PIC.
- Connect the LCD to PORTD (data lines D0–D7), with RS and E connected to RC0 and RC1 respectively.
- Use a 10k potentiometer to adjust LCD contrast by connecting its wiper to the LCD’s VEE pin.
- Connect a 20MHz crystal to OSC1 and OSC2 with 22pF capacitors to ground.
- Connect the CCP1 pin (RC2) to the input of the motor driver (L293D or transistor base with resistor).
- The fan is powered from a 12V supply, and ground is shared with the PIC circuit.
MPLAB XC8 Code (Fully Commented):
#define _XTAL_FREQ 20000000 // Define crystal frequency
#include <xc.h>
#include <stdio.h>
// CONFIG BITS
#pragma config FOSC = HS, WDTE = OFF, PWRTE = OFF, CP = OFF, BOREN = ON, LVP = OFF
// Define control pins
#define RS RC0
#define EN RC1
#define LCD PORTD
void lcd_cmd(char cmd);
void lcd_data(char data);
void lcd_init();
void lcd_string(const char *str);
void adc_init();
int read_adc();
void pwm_init();
void set_pwm(unsigned int duty);
void main(void) {
TRISA = 0x01; // RA0 as input (LM35)
TRISC = 0x00; // RC0 and RC1 as output (LCD), RC2 for PWM
TRISD = 0x00; // LCD data lines as output
lcd_init(); // Initialize LCD
adc_init(); // Initialize ADC
pwm_init(); // Initialize PWM module
char buffer[16];
while (1) {
int adc_val = read_adc(); // Get ADC value from AN0
float temp = adc_val * 0.488; // Convert to Celsius (approx. 0.488 = (5V/1024)*100)
lcd_cmd(0x80); // Move cursor to first line
sprintf(buffer, “Temp: %.1f C “, temp); // Format temperature string
lcd_string(buffer); // Display temp on LCD
if (temp < 25) {
set_pwm(0); // Fan OFF
} else if (temp < 30) {
set_pwm(200); // Low speed
} else if (temp < 35) {
set_pwm(500); // Medium speed
} else {
set_pwm(800); // High speed
}
__delay_ms(1000);
}
}
void lcd_init() {
lcd_cmd(0x38); // 2-line LCD
lcd_cmd(0x0C); // Display ON, cursor OFF
lcd_cmd(0x06); // Increment cursor
lcd_cmd(0x01); // Clear display
__delay_ms(2);
}
void lcd_cmd(char cmd) {
RS = 0;
LCD = cmd;
EN = 1; __delay_ms(2); EN = 0;
}
void lcd_data(char data) {
RS = 1;
LCD = data;
EN = 1; __delay_ms(2); EN = 0;
}
void lcd_string(const char *str) {
while (*str) lcd_data(*str++);
}
void adc_init() {
ADCON1 = 0x80; // Right justified, Vref=Vdd
ADCON0 = 0x41; // Channel 0 (AN0), ADC ON
}
int read_adc() {
__delay_ms(2); // Acquisition time
GO_nDONE = 1; // Start conversion
while (GO_nDONE); // Wait for completion
return ((ADRESH << 8) + ADRESL); // Combine high and low bytes
}
void pwm_init() {
PR2 = 255; // Set PWM period
CCP1CON = 0x0C; // Set PWM mode
T2CON = 0x04; // Turn on Timer2
TRISC2 = 0; // Set CCP1 (RC2) as output
}
void set_pwm(unsigned int duty) {
if (duty > 1023) duty = 1023; // Cap at 10-bit max
CCPR1L = duty >> 2; // Upper 8 bits
CCP1CON = (CCP1CON & 0xCF) | ((duty & 0x03) << 4); // Lower 2 bits into CCP1CON
}
🔍 6. Testing and Debugging Tips
- If the LCD doesn’t display anything, verify contrast voltage and connections to PORTD and control lines. Adjust the potentiometer connected to the LCD’s VEE pin until characters are visible.
- If the temperature value is incorrect, check that LM35 is correctly powered and measure its analog output with a multimeter. Confirm it’s around 250mV at 25°C.
- If the fan does not run or change speed, inspect the PWM output on RC2 using an oscilloscope or multimeter in duty cycle mode. Also check the motor driver (L293D or transistor) wiring.
- If the microcontroller isn’t running, confirm the oscillator is connected correctly and the configuration bits are set properly for HS mode.
🚀 7. Extensions
You can expand this project by:
- Adding a push-button to manually override automatic fan control.
- Interfacing with an SD card to log temperature data.
- Using a Bluetooth module (like HC-05) to send temperature readings to a mobile device.
- Adding a graphical display for more intuitive visuals.
- Implementing a PID control algorithm for more precise fan speed regulation.