// Include libraries
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
// Configuration bits
#pragma config FOSC = INTOSCIO // Internal oscillator
#pragma config WDTEN = OFF // Watchdog timer off
#pragma config LVP = OFF // Low voltage programming off
// Define constants
#define _XTAL_FREQ 8000000 // Set oscillator frequency
#define Kp 1.0 // Proportional gain
#define Ki 0.5 // Integral gain
#define Kd 0.2 // Derivative gain
#define SETPOINT 100 // Desired setpoint
// Declare variables
int error = 0;
int previous_error = 0;
int integral = 0;
int derivative = 0;
int output = 0;
// Function prototypes
void init_pwm();
void init_adc();
void init_lcd();
int read_adc();
void write_lcd(char* str);
void compute_pid();
// Initialize PWM module
void init_pwm() {
TRISCbits.TRISC2 = 0; // Set RC2 as output
PR2 = 249; // Set period to 1ms (Fosc/4/((PR2+1)*32))
T2CONbits.T2CKPS = 0b11; // Set prescaler to 1:32
T2CONbits.TMR2ON = 1; // Turn on Timer2
CCP1CONbits.CCP1M = 0b1100; // Set CCP1 as PWM mode
CCP1CONbits.DC1B = 0; // Set LSBs of PWM duty cycle to 0
}
// Initialize ADC module
void init_adc() {
TRISA = 0xFF; // Set PORTA as input
ADCON0bits.ADON = 1; // Turn on ADC module
ADCON1bits.ADFM = 1; // Set result format to right justified
ADCON1bits.ADCS = 0b101; // Set conversion clock to Fosc/16
}
// Initialize LCD module
void init_lcd() {
TRISCbits.TRISC0 = 0; // Set RC0 as output
TRISCbits.TRISC1 = 0; // Set RC1 as output
TRISDbits.TRISD0 = 0; // Set RD0 as output
TRISDbits.TRISD1 = 0; // Set RD1 as output
TRISDbits.TRISD2 = 0; // Set RD2 as output
TRISDbits.TRISD3 = 0; // Set RD3 as output
TRISDbits.TRISD4 = 0; // Set RD4 as output
TRISDbits.TRISD5 = 0; // Set RD5 as output
__delay_ms(100); // Wait for LCD to power up
PORTCbits.RC0 = 0; // Set RS to command mode
PORTCbits.RC1 = 0; // Set RW to write mode
PORTDbits.RD4 = 1; // Set DB4 to 1
PORTDbits.RD5 = 1; // Set DB5 to 1
PORTDbits.RD6 = 0; // Set DB6 to 0
PORTDbits.RD7 = 0; // Set DB7 to 0
PORTDbits.RD2 = 1; // Enable display
__delay_us(1); // Wait for enable pulse width
PORTDbits.RD2 = 0; // Disable display
__delay_us(40); // Wait for command execution time
// Set function mode (8-bit, 2-line, 5x7 font)
PORTCbits.RC0 = 0; // Set RS to command mode
PORTCbits.RC1 = 0; // Set RW to write mode
PORTDbits.RD4 = 0; // Set DB4 to 0
PORTDbits.RD5 = 0; // Set DB5 to 0
PORTDbits.RD6 = 1; // Set DB6 to 1
PORTDbits.RD7 = 0; // Set DB7 to 0
PORTDbits.RD2 = 1; // Enable display
__delay_us(1); // Wait for enable pulse width
PORTDbits.RD2 = 0; // Disable
__delay_us(40); // Wait for command execution time
// Set display on/off control (display on, cursor off, blink off)
PORTCbits.RC0 = 0; // Set RS to command mode
PORTCbits.RC1 = 0; // Set RW to write mode
PORTDbits.RD4 = 0; // Set DB4 to 0
PORTDbits.RD5 = 0; // Set DB5 to 0
PORTDbits.RD6 = 0; // Set DB6 to 0
PORTDbits.RD7 = 0; // Set DB7 to 0
PORTDbits.RD2 = 1; // Enable display
__delay_us(1); // Wait for enable pulse width
PORTDbits.RD2 = 0; // Disable display
__delay_us(40); // Wait for command execution time
// Set entry mode (increment cursor, no display shift)
PORTCbits.RC0 = 0; // Set RS to command mode
PORTCbits.RC1 = 0; // Set RW to write mode
PORTDbits.RD4 = 0; // Set DB4 to 0
PORTDbits.RD5 = 0; // Set DB5 to 0
PORTDbits.RD6 = 0; // Set DB6 to 1
PORTDbits.RD7 = 1; // Set DB7 to 0
PORTDbits.RD2 = 1; // Enable display
__delay_us(1); // Wait for enable pulse width
PORTDbits.RD2 = 0; // Disable display
__delay_us(40); // Wait for command execution time
// Clear display and return cursor to home position
PORTCbits.RC0 = 0; // Set RS to command mode
PORTCbits.RC1 = 0; // Set RW to write mode
PORTDbits.RD4 = 0; // Set DB4 to 0
PORTDbits.RD5 = 0; // Set DB5 to 0
PORTDbits.RD6 = 0; // Set DB6 to 0
PORTDbits.RD7 = 1; // Set DB7 to 0
PORTDbits.RD2 = 1; // Enable display
__delay_us(1); // Wait for enable pulse width
PORTDbits.RD2 = 0; // Disable display
__delay_us(1640); // Wait for command execution time
}
// Convert ADC result to temperature in Celsius
float adc_to_temperature(unsigned int adc_result) {
float voltage = adc_result * 5.0 / 1023.0; // Convert ADC result to voltage
float temperature = (voltage - 0.5) * 100.0; // Convert voltage to temperature
return temperature;
}
float Kp = 20; // Proportional gain
float Ki = 1; // Integral gain
float Kd = 50; // Derivative gain
float setpoint = 25; // Setpoint temperature
float error = 0; // Error term
float integral = 0; // Integral term
float derivative = 0; // Derivative term
float last_error = 0; // Last error term for derivative term calculation
float pid_output = 0; // PID output
while (1) {
// Read temperature sensor and convert result to temperature in Celsius
unsigned int adc_result = read_adc();
float temperature = adc_to_temperature(adc_result);
// Calculate error term
error = setpoint - temperature;
// Calculate integral term (discrete approximation using trapezoidal rule)
integral += (error + last_error) / 2.0 * 0.1; // Sampling time = 100 ms
// Calculate derivative term (discrete approximation using backward difference)
derivative = (error - last_error) / 0.1; // Sampling time = 100 ms
// Calculate PID output
pid_output = Kp * error + Ki * integral + Kd * derivative;
// Limit PID output to PWM duty cycle range (0-100%)
if (pid_output > 100) {
pid_output = 100;
} else if (pid_output < 0) {
pid_output = 0;
}