洪嵐峰 發表於 2023-4-27 06:28:00

PIC16F877A

製作使用PIC16F877A微控制器的PID控制器
需要以下零件:

PIC16F877A微控制器
LCD顯示器
4x4矩陣按鈕
轉換器(例如:ADC0804)
電阻和電容(用於製作低通濾波器和電源電路)
直流電源供應器
電路板
電路連接線
訊號發生器

以下是PIC16F877A微控制器的腳位連結:

RA0 – 轉換器輸入

RA1 – 轉換器輸入

RA2 – 轉換器輸入

RA3 – 轉換器輸入

RA4 – 轉換器輸入

RA5 – 鍵盤輸入

RA6 – LCD顯示器數據線

RA7 – LCD顯示器數據線

RB0 – 轉換器輸入

RB1 – 轉換器輸入

RB2 – 轉換器輸入

RB3 – 轉換器輸入

RB4 – LCD顯示器選擇器

RB5 – LCD顯示器選擇器

RB6 – LCD顯示器選擇器

RB7 – LCD顯示器選擇器

RC0 – LCD顯示器使能信號

RC1 – LCD顯示器使能信號

RC2 – LCD顯示器使能信號

RC3 – LCD顯示器使能信號

RC4 – 訊號發生器輸入

RC5 – 訊號發生器輸入

RC6 – 訊號發生器輸入

RC7 – 訊號發生器輸入

以下是PID控制器的基本程式碼,可在MPLAB IDE中編譯和上傳到PIC16F877A微控制器中:


#include <pic.h>

#define RS RC0
#define EN RC1
#define D4 RC2
#define D5 RC3
#define D6 RC4
#define D7 RC5
#define Kp 1.0
#define Ki 0.5
#define Kd 0.2

void lcd_init();
void lcd_cmd(unsigned char);
void lcd_data(unsigned char);
void lcd_string(unsigned char *);
void delay_ms(unsigned int);
unsigned char read_adc(unsigned char);

void main() {
  unsigned char setpoint = 0, process_variable = 0, error = 0, previous_error = 0;
  float integral = 0, derivative = 0, output = 0;
  char buffer;
  
A0 = 1; // 將A/D轉換器輸入通道設置為RA0
A1 = 1;
A2 = 1;
A3 = 1;
A4 = 1;
TRISB = 0xF0; // 設置RB0-RB3為輸入,RB4-RB7為輸出
TRISC = 0x00; // 設置RC0-RC7為輸出

lcd_init(); // 初始化LCD顯示器

while(1) {
// 讀取設定點和過程變量
setpoint = read_adc(0);
process_variable = read_adc(1);

// 計算誤差和積分
error = setpoint - process_variable;
integral += error;
derivative = error - previous_error;
previous_error = error;

// 計算PID輸出
output = Kp * error + Ki * integral + Kd * derivative;

// 顯示PID輸出和其他信息
lcd_cmd(0x01); // 清屏
lcd_string("Setpoint: ");
sprintf(buffer, "%d", setpoint);
lcd_string(buffer);
lcd_cmd(0xC0); // 移到第二行
lcd_string("PV: ");
sprintf(buffer, "%d", process_variable);
lcd_string(buffer);
lcd_string("   ");
lcd_cmd(0x94); // 移到第三行
lcd_string("Output: ");
sprintf(buffer, "%.2f", output);
lcd_string(buffer);

delay_ms(1000); // 延遲1秒
}
}

void lcd_init() {
lcd_cmd(0x02); // 返回首位
lcd_cmd(0x28); // 4位數據線,2行顯示,5x8點陣字型
lcd_cmd(0x0C); // 顯示器開啟,光標不顯示
lcd_cmd(0x06); // 光標右移,字符不移動
}

void lcd_cmd(unsigned char cmd) {
RS = 0; // 選擇命令模式
D4 = ((cmd >> 4) & 0x01);
D5 = ((cmd >> 5) & 0x01);
D6 = ((cmd >> 6) & 0x01);
D7 = ((cmd >> 7) & 0x01);
EN = 1;
delay_ms(1);
EN = 0;
D4 = (cmd & 0x01);
D5 = ((cmd >> 1) & 0x01);
D6 = ((cmd >> 2) & 0x01);
D7 = ((cmd >> 3) & 0x01);
EN = 1;
delay_ms(1);
EN = 0;
}

void lcd_data(unsigned char data) {
RS = 1; // 選擇數據模式
D4 = ((data >> 4) & 0x01);
D5 = ((data >> 5) & 0x01);
D6 = ((data >> 6) & 0x01);
D7 = ((data >> 7) & 0x01);
EN = 1;
delay_ms(1);
EN = 0;
D4 = (data & 0x01);
D5 = ((data >> 1) & 0x01);
D6 = ((data >> 2) & 0x01);
D7 = ((data >> 3) & 0x01);
EN = 1;
delay_ms(1);
EN = 0;
}

void lcd_string(char *str) {
while(*str) {
lcd_data(*str++);
}
}

int read_adc(unsigned char channel) {
unsigned int result;
ADCON0 = (channel << 2) | 0x01; // 選擇通道,啟動A/D轉換器
delay_us(10);
GO_DONE = 1; // 啟動轉換
while(GO_DONE); // 等待轉換完成
result = ADRESH;
result = (result << 8) | ADRESL;
return result;
}

void delay_ms(unsigned int ms) {
unsigned int i, j;
for(i = 0; i < ms; i++) {
for(j = 0; j < 1000; j++);
}
}

void delay_us(unsigned int us) {
unsigned int i;
for(i = 0; i < us; i++) {
_delay(5); // 使用5個指令進行延遲
}
}

void _delay(unsigned char n) {
while(n--);
}

最後是PID控制器的主要程式碼:

void pid_control() {
int current_value = read_adc(0); // 讀取目前的測量值
int error = set_point - current_value; // 計算誤差值
integral += error; // 累加誤差值
int derivative = error - last_error; // 計算誤差變化率
last_error = error; // 更新上一次的誤差值
int output = Kp * error + Ki * integral + Kd * derivative; // 計算輸出值
output = constrain(output, 0, 255); // 限制輸出值在0~255之間
PWM = output; // 設定PWM輸出
}

上述程式碼中的read_adc函式會讀取ADC轉換器的值,這個值是模擬信號經過轉換器轉換後得到的數字,代表著模擬信號的大小。

接下來計算誤差值、累加誤差值、計算誤差變化率,並根據PID公式計算輸出值。

最後將輸出值限制在0~255之間,並設定PWM輸出。
頁: [1]
查看完整版本: PIC16F877A