- UID
- 373967
- 帖子
- 9008
- 主題
- 2609
- 精華
- 0
- 積分
- 1003
- 楓幣
- 0
- 威望
- 980
- 存款
- 35891
- 贊助金額
- 0
- 推廣
- 0
- GP
- 1205
- 閱讀權限
- 70
- 在線時間
- 460 小時
- 註冊時間
- 2023-1-12
- 最後登入
- 2024-12-22
|
製作PID需要以下零件:
STM32F103C8T6微控制器
16x2液晶顯示器模組
4x4矩陣按鍵模組
10k歐姆可變電阻
直流電源供應器
杜邦線
引腳編號 功能
PA0 模擬輸入
PA1 模擬輸入
PA2 模擬輸入
PA3 模擬輸入
PA4 模擬輸入
PA5 模擬輸入
PA6 模擬輸入
PA7 模擬輸入
PB0 按鍵
PB1 按鍵
PB2 按鍵
PB3 按鍵
PB4 LCD數據線
PB5 LCD數據線
PB6 LCD數據線
PB7 LCD數據線
PB8 LCD數據線
PB9 LCD數據線
PB10 LCD數據線
PB11 LCD數據線
PB12 LCD數據線
PB13 LCD數據線
PB14 LCD數據線
PB15 LCD數據線
PC13 LCD背光控制
PA8 PWM輸出
PA9 USART1 TX
PA10 USART1 RX
以下是使用C語言實現的PID控制程式碼:
#include "stm32f10x.h"
#include "lcd.h"
#define KP 1.0 // 比例常數
#define KI 0.5 // 積分常數
#define KD 0.1 // 微分常數
int Setpoint = 30; // 目標設置點
int Input = 0; // 系統輸入
int Output = 0; // 控制器輸出
int LastError = 0; // 上一次誤差
int Integral = 0; // 累積誤差
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 初始化LCD
LCD_Init();
// 設置PA8為PWM輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 設置PA9和PA10為USART1的TX和RX引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 設置PB0到PB3為矩陣按鍵輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 設置TIM3為PWM模式
TIM_TimeBaseStructure.TIM_Period = 2000 - 1; // PWM週期為2000個計數
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 計數頻率為72MHz / 72 = 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 設置TIM3的通道1為PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 啟動TIM3的通道1
TIM_Cmd(TIM3, ENABLE);
// 設置USART1的參數
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
// 啟動USART1
USART_Cmd(USART1, ENABLE);
while(1)
{
// 讀取矩陣按鍵輸入
int key = read_key();
// 根據按鍵輸入設置目標設置點
switch(key)
{
case 1: Setpoint = 20; break;
case 2: Setpoint = 30; break;
case 3: Setpoint = 40; break;
case 4: Setpoint = 50; break;
default: break;
}
// 更新PID控制器
PID_Compute();
// 更新PWM輸出
TIM_SetCompare1(TIM3, (int)Output);
// 將輸出值通過USART1發送到電腦端
char buffer[16];
sprintf(buffer, "%.2f\r\n", Output);
USART_SendString(USART1, buffer);
// 延遲100毫秒
delay_ms(100);
}
}
// 讀取矩陣按鍵輸入
int read_key()
{
int key = 0;// 第一列
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
GPIO_ResetBits(GPIOB, GPIO_Pin_1);
GPIO_ResetBits(GPIOB, GPIO_Pin_2);
GPIO_ResetBits(GPIOB, GPIO_Pin_3);
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)) key = 1;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)) key = 2;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2)) key = 3;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_3)) key = 4;
GPIO_SetBits(GPIOB, GPIO_Pin_0);
GPIO_SetBits(GPIOB, GPIO_Pin_1);
GPIO_SetBits(GPIOB, GPIO_Pin_2);
GPIO_SetBits(GPIOB, GPIO_Pin_3);
return key;
}
// 將字串發送到USART1
void USART_SendString(USART_TypeDef* USARTx, char* str)
{
while(*str)
{
USART_SendData(USARTx, *str);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
str++;
}
}
// 延遲指定毫秒數
void delay_ms(u32 n)
{
while(n--)
{
u32 i = 1000;
while(i--);
}
}
// 計算PID控制器的輸出
void PID_Compute()
{
// 計算當前偏差
double error = Setpoint - Input;
// 計算積分項
ITerm += Ki * error;
if(ITerm > outMax) ITerm = outMax;
else if(ITerm < outMin) ITerm = outMin;
// 計算微分項
double dInput = Input - lastInput;
DTerm = -Kd * dInput;
// 計算輸出
Output = Kp * error + ITerm + DTerm;
if(Output > outMax) Output = outMax;
else if(Output < outMin) Output = outMin;
// 保存當前輸入和輸出
lastInput = Input;
}
// 初始化GPIO
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 打開GPIOB時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB0~PB3為推挽輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置PB6為浮空輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
// 初始化USART1
void USART1_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
// 打開USART1時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 配置USART1引腳
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 打開USART1
USART_Cmd(USART1, ENABLE);
}
// 初始化TIM3
void TIM3_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 打開TIM3時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 設置TIM3計數頻率為1kHz
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 設置TIM3通道1為PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 啟動TIM3
TIM_Cmd(TIM3, ENABLE);
}
// 初始化ADC1
void ADC1_Configuration(void)
{
ADC_InitTypeDef ADC_InitStructure;
scss
Copy code
// 打開ADC1時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 配置ADC1通道
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 配置ADC1通道0為采樣時間為55.5個週期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 啟動ADC1校準
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
int main(void)
{
// 初始化GPIO
GPIO_Configuration();
// 初始化USART1
USART1_Configuration();
// 初始化TIM3
TIM3_Configuration();
// 初始化ADC1
ADC1_Configuration();
// 設置初始值
float setpoint = 50.0;
float kp = 1.0;
float ki = 0.1;
float kd = 0.0;
float error = 0.0;
float integral = 0.0;
float derivative = 0.0;
float prev_error = 0.0;
float output = 0.0;
uint16_t adc_value = 0;
while(1)
{
// 讀取ADC值
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
adc_value = ADC_GetConversionValue(ADC1);
// 計算誤差值
error = setpoint - adc_value;
// 計算積分項
integral += error;
// 計算微分項
derivative = error - prev_error;
// 計算輸出值
output = kp * error + ki * integral + kd * derivative;
// 限制輸出值的範圍
if(output > 100.0)
{
output = 100.0;
}
else if(output < 0.0)
{
output = 0.0;
}
// 更新PWM占空比
TIM_SetCompare1(TIM3, (uint16_t)(output * 10.0));
// 記錄上一次誤差值
prev_error = error;
// 延時1ms
Delay_ms(1);
}
}
上面的程式碼中,我們使用了四個變數 kp、ki、kd 和 setpoint,它們分別代表 PID 控制器的比例增益、積分增益、微分增益和目標值。
在主函數中,我們定義了一個無限循環,其中我們不斷讀取 ADC 的值,計算誤差,然後根據 PID 控制算法計算輸出值,最後限制輸出值的範圍(在 0-100% 之間),並將 PWM 占空比更新為計算出的輸出值。
另外,還定義了一個 Delay_ms 函數,用於延時 1ms。
如果您使用的是庫函數,可能不需要定義此函數。 |
|