Code: Select all
// Project uses a PIC16F877A, MCP4921-DAC, EasyPIC AND 2x16 LCD Display
program TemperatureController;
const CHIP_SELECT : byte = 0;
var i, highByte, lowByte : byte;
counter, temp, tmp : byte;
kd, ki, kp : integer;
T, SP : integer;
PV : longint;
errA, errD, errN, errP : integer;
p_Term, i_Term, d_Term : integer;
pid_Out : integer;
txt1 : array[10] of char;
txt2 : array[11] of char;
txt3 : array[6] of char;
txt4 : array[6] of char;
//******************************************************************************
// TMR0 Interrupt
//******************************************************************************
Procedure interrupt;
begin
inc(counter); // Increment value of counter on every interrupt
TMR0 := $00; // Load TMR0 with $00 for a new count
clearbit(INTCON, TMR0IF); // Clear overflow flag bit for TMR0
end;
//******************************************************************************
// Send pid_Out to DAC
//******************************************************************************
Procedure dacOut;
begin
ClearBit(PORTC,CHIP_SELECT); // Prepare for data transfer
temp := hi(pid_Out) and $0F; // Prepare hi-byte for transfer
temp := temp or $30; // It's a 12-bit number, so only
SPI_write(temp); // lower nibble of high byte is used
temp := lo(pid_Out); // Prepare lo-byte for transfer
SPI_write(temp);
SetBit(PORTC,CHIP_SELECT);
end;
//******************************************************************************
// Print temperature and DAC output to LCD
//******************************************************************************
Procedure printTempPID;
begin
intToStr(T, txt3); // Print temperature in fahrenheit to LCD
Lcd_Chr(1,11, txt3[2]);
Lcd_Chr(1,12, txt3[3]);
Lcd_Chr(1,13, txt3[4]);
Lcd_Chr(1,14, txt3[5]);
intToStr(pid_Out, txt4); // Print PID_out to LCD
Lcd_Chr(2,12, txt4[1]);
Lcd_Chr(2,13, txt4[2]);
Lcd_Chr(2,14, txt4[3]);
Lcd_Chr(2,15, txt4[4]);
Lcd_Chr(2,16, txt4[5]);
delay_ms(50);
end;
//******************************************************************************
// Calculate PID output
//******************************************************************************
Procedure PID; // Setpoint calculations
begin // 4095 / 250 = 16.38
PV := (tmp * 1638)/100; // 100 * 250 = 1638
errN := SP - PV; // (250 * 1638) / 100 = 4095
errA := errA + errP; // (180F - 32) / 1.8 = 82.22 C
errD := errN - errP; // 2 * 82.22 C = 164.44 C
errP := errN; // 16.38 * 164.44 = 2693.53
// if errA > X then errA := X; // wind-up limits ???
// if errA < X then errA := X; // wind-up limits ???
p_Term := (kp * errN);
i_Term := (ki * errA);
d_Term := (kd * errD);
pid_Out := p_Term + i_Term + d_Term;
if pid_Out > 4095 then pid_Out := 4095; // High limit
if pid_Out < 0 then pid_Out := 0; // Low limit
end;
//******************************************************************************
// Get temperature and convert from C to F
//******************************************************************************
Procedure temperature;
begin
Ow_Reset(PORTA, 5); // onewire reset signal
Ow_Write(PORTA, 5, $CC); // issue SKIP ROM command to DS1820
Ow_Write(PORTA, 5, $44); // issue CONVERT T command to DS1820
Delay_us(120);
i := Ow_Reset(PORTA, 5);
Ow_Write(PORTA,5, $CC); // issue SKIP ROM command to DS1820
Ow_Write(PORTA,5, $BE); // issue READ SCRATCHPAD command to DS1820
lowByte := Ow_Read(PORTA, 5); // get low byte 0000 0000 XXXX XXXX
highByte := Ow_Read(PORTA, 5); // get high byte XXXX XXXX 0000 0000
T := lowByte; // using range 0 to 127 C
tmp := T; // tmp for PV
T := T shr 1; // convert to whole number
T := ((T * 18) + 320)/10; // convert from C to F
end;
//******************************************************************************
// Initiate
//******************************************************************************
Procedure init;
begin
ADCON1 := 6; // Configure PortA, All Digital I/O
INTCON := $A0; // Enable Enable GIE and TMR0IF
OPTION_REG := $87; // Enable TMR0, 256-bit prescaler = 1:256
TMR0 := $00; // Load TMR0 with 0000 0000
TRISA := $FF; // Designate porta as inputs
PORTA := $FF; // Initialize porta to $FF
TRISB := $00; // Designate portb as output
TRISC := $00; // Designate portc as output
Lcd_Init(PORTB); // Get ready
Lcd_Cmd(LCD_CURSOR_OFF); // Turn off grey display
txt1 := 'Still Temp'; // Still Temp
txt2 := 'PIDOUTPUT = '; // PIDOUTPUT
Lcd_Out(1, 1, txt1); // Print Still Temp
Lcd_Chr(1, 15, 223); // The little circle character
Lcd_Chr(1, 16, 'F'); // Fahrenheit
Lcd_Out(2, 1, txt2); // Print PIDOUTPUT =
ClearBit(TRISC,CHIP_SELECT); // Get DAC ready
SPI_init; // Get DAC ready
counter := 0;
PV := 0;
SP := 2694; // 16.38 * 164.44 = 2693.53
PID_Out := 0;
T := 0;
tmp := 0;
kp := 1;
ki := 0;
kd := 0;
errA := 0;
errD := 0;
errN := 0;
errP := 0;
end;
//******************************************************************************
// Main Program
//******************************************************************************
begin
init;
repeat
begin
if counter = 30 then
begin
temperature;
PID;
printTempPID;
dacOut;
counter := 0;
end;
end;
until 1 = 0;
end.
//******************************************************************************