冰楓論壇

 找回密碼
 立即註冊
搜索
查看: 754|回覆: 0
打印 上一主題 下一主題

[討論] STM32F103C8T6

[複製鏈接]

2609

主題

0

好友

1003

積分

金牌會員

Rank: 6Rank: 6

UID
373967
帖子
9008
主題
2609
精華
0
積分
1003
楓幣
0
威望
980
存款
35891
贊助金額
0
推廣
0
GP
1205
閱讀權限
70
在線時間
460 小時
註冊時間
2023-1-12
最後登入
2024-12-22

2023端午節紀念勳章 2023中秋節紀念勳章 2023聖誕節紀念勳章

跳轉到指定樓層
1
發表於 2023-4-28 06:41:27 |只看該作者 |倒序瀏覽
製作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。

如果您使用的是庫函數,可能不需要定義此函數。
收藏收藏0 推0 噓0


把本文推薦給朋友或其他網站上,每次被點擊增加您在本站積分: 1鑰匙
複製連結並發給好友,以賺取推廣點數
簡單兩步驟,註冊、分享網址,即可獲得獎勵! 一起推廣文章換商品、賺$$
高級模式
B Color Image Link Quote Code Smilies |上傳

廣告刊登意見回饋關於我們管群招募本站規範DMCA隱私權政策

Copyright © 2011-2024 冰楓論壇, All rights reserved

免責聲明:本網站是以即時上載留言的方式運作,本站對所有留言的真實性、完整性及立場等,不負任何法律責任。

而一切留言之言論只代表留言者個人意見,並非本網站之立場,用戶不應信賴內容,並應自行判斷內容之真實性。

小黑屋|手機版|冰楓論壇

GMT+8, 2024-12-22 16:55

回頂部