冰楓論壇

標題: PIC16F877 [打印本頁]

作者: 洪嵐峰    時間: 2023-4-27 06:37
標題: PIC16F877
製作PID控制器所需的零件如下:

PIC16F877微控制器
16x2液晶顯示器
電位計
電容
電阻
電晶體
繼電器
電源供應器

PIC16F877微控制器的引腳連結如下:

VDD連接至電源正極
VSS連接至電源負極
RA0連接至電位計輸出
RA1連接至繼電器控制腳
RA2連接至液晶顯示器RS腳
RA3連接至液晶顯示器EN腳
RC0連接至液晶顯示器D4腳
RC1連接至液晶顯示器D5腳
RC2連接至液晶顯示器D6腳
RC3連接至液晶顯示器D7腳

以下是PIC16F877製作PID控制器的程式碼,使用的開發環境是MPLAB IDE和XC8編譯器:


#include <xc.h>
#include <stdio.h>
#include <stdlib.h>

#define _XTAL_FREQ 20000000

// PID參數
float Kp = 1.0;
float Ki = 0.0;
float Kd = 0.0;
float last_error = 0.0;
float integral = 0.0;

// 目標值
float target = 50.0;

void main(void) {
    // 設定I/O
    TRISA = 0b00000001; // RA0為輸入
    TRISC = 0b00000000; // RC為輸出
   
    // 設定液晶顯示器
    Lcd_Init();
    Lcd_Clear();
    Lcd_Set_Cursor(1,1);
    Lcd_Write_String("Temperature: ");
   
    // 設定ADC
    ADC_Init();
   
    // 設定計時器
    TMR1_Init();
   
    while(1) {
        // 讀取溫度值
        float temp = ADC_Read(0);
        temp = temp * 0.4887; // 轉換為攝氏溫度
        
        // 計算誤差
        float error = target - temp;
        
        // 計算PID輸出
        float Pout = Kp * error;
        integral += error;
        float Iout = Ki * integral;
        float derivative = error - last_error;
        float Dout = Kd * derivative;
        float output = Pout + Iout + Dout;
        last_error = error;
        
        // 限制輸出範圍
        if(output > 255) {
            output = 255;
        } else if(output < 0) {
            output = 0;
}

    // 控制繼電器
    if(output > temp) {
        PORTAbits.RA1 = 1;
    } else {
        PORTAbits.RA1 = 0;
    }
   
    // 顯示溫度和PID輸出
    Lcd_Set_Cursor(1, 14);
    Lcd_Write_Char(temp/10 + '0');
    Lcd_Write_Char(temp%10 + '0');
    Lcd_Write_String("C ");
    Lcd_Set_Cursor(2, 1);
    Lcd_Write_String("PID Output: ");
    Lcd_Set_Cursor(2, 13);
    Lcd_Write_Char(output/100 + '0');
    Lcd_Write_Char((output/10)%10 + '0');
    Lcd_Write_Char(output%10 + '0');
   
    __delay_ms(500);
}
}

// 設定ADC
void ADC_Init() {
ADCON0 = 0b00000001; // 設定RA0為輸入和啟用ADC
ADCON1 = 0b00000000; // 設定VDD和VSS為參考電壓
__delay_us(20);
}

// 讀取ADC
float ADC_Read(unsigned char ch) {
if(ch > 7) {
return 0;
}
ADCON0 = (ch<<2) | 0b00000001;
__delay_us(20);
GO_DONE = 1;
while(GO_DONE);
return ADRESH * 256 + ADRESL;
}

// 設定計時器
void TMR1_Init() {
T1CON = 0b00000111; // 設定為16位元計時器、Fosc/256、啟用計時器
}

// 設定液晶顯示器
void Lcd_Init() {
Lcd_Port = 0x00;
__delay_ms(20);
Lcd_Command(0x03);
__delay_ms(5);
Lcd_Command(0x03);
__delay_ms(11);
Lcd_Command(0x03);
Lcd_Command(0x02);
Lcd_Command(0x28);
Lcd_Command(0x0c);
Lcd_Command(0x06);
Lcd_Command(0x01);
}

// 寫入液晶顯示器指令
void Lcd_Command(unsigned char cmd) {
Lcd_Port = cmd;
RS = 0;
EN = 1;
__delay_ms(4);
EN = 0;
}

// 寫入液晶顯示器字符
void Lcd_Write_Char(unsigned char data) {
Lcd_Port = data;
RS = 1;
EN = 1;
__delay_us(40);
EN = 0;
}

// 寫入液晶顯示器字串
void Lcd_Write_String(char *str) {
int i;
for(i=0;str[i]!='\0';i++) {
Lcd_Write_Char(str[i]);
}
}

// 設定液晶顯示器游標位置
void Lcd_Set_Cursor(unsigned char row, unsigned char col) {
if(row == 1) {
Lcd_Command(0x80 + col - 1} else if(row == 2) {
Lcd_Command(0xc0 + col - 1);
}
}

// 計算PID輸出
int PID_Controller(float set_point, float process_variable) {
static float integral = 0;
static float derivative = 0;
static float last_error = 0;
float error = set_point - process_variable;
integral += error * TIME_INTERVAL;
derivative = (error - last_error) / TIME_INTERVAL;
last_error = error;

return (int)(KP * error + KI * integral + KD * derivative);
}



關於連結腳位,請參考PIC16F877的資料手冊,其中包括了PIC16F877的腳位圖和對應的功能,以及每個腳位的電氣特性和建議的連接方法。





歡迎光臨 冰楓論壇 (https://bingfong.com/) Powered by 冰楓