STM32——PID恒温控制
原理
元件
?? stm32f103核心板、L298N模塊(當然用MOS管更好)、led一個、NPN三極管一個、蜂鳴器一個、DHT11一個、LCD1602一個、電阻200歐兩個、可調電阻10K一個、加熱絲一個
功能描述
??用DHT11檢測當前環境溫濕度,并將數據顯示在LCD1602上,在用設定溫度與當前溫度相減,通過PID算法計算出當前輸出脈寬,并將其加在L298N模塊中,使加熱絲發熱,形成一個閉環,經過一段時間溫度穩定在設定值。由于我的初衷是做一個恒溫箱孵蛋,所以加了濕度報警。
原理圖
注:此圖沒有做stm32的最小系統,故只做原理圖使用,不可仿真。
DHT11時序圖
總體時序圖
??DHT11與MCU通訊一次時間在4ms左右,數據分小數部分和整數部分,一次完整的數據傳輸為40bit,高位先出。
??數據格式: 8bit濕度整數數據+8bit濕度小數數據+8bi溫度整數數據+8bit溫度小數數據+8bit校驗和。
??數據傳送正確時校驗和數據等于“ 8bit濕度整數數據+8bit濕度小數數據+8bi溫度整數數據+8bit溫度小數數據” 所得結果的末8位。
??MCU發送一次開始信后,DHT11從低功耗模式轉換到高速模式等待主機開始信號結后,DHT11發送響應信號,送出40bit的數據,并觸發一次信號采集,用戶可選擇讀取部分數據。從模式下,DHT11接收到開始信號觸發一次溫濕度采集,如果沒有接收到主機發送的開始信號,DHT11不會主動進行溫濕度采集。采集數據后轉換到低速模式。
初始化
??總線空閑狀態為高電平,主機把總線拉低等待DHT11響應,主機把總線拉低必須大于18毫秒,保證DHT11能檢測到起始信號。 DHT11接收到主機的開始信號后,等待主機開始信號結束,然后發送80us低電平響應信號.主機發送開始信號結束后,延時等待20-40us后, 讀取DHT11的響應信號,主機發送開始信號后,可以切換到輸入模式,或者輸出高電平均可, 總線由上拉電阻拉高。
信號“0”
??總線為低電平,說明DHT11發送響應信號,DHT11發送響應信號后,再把總線拉高80us,準備發送數據,每一bit數據都以50us低電平時隙開始,高電平持續的時間在26-28us之間表示0。
信號“1”
??總線為低電平,說明DHT11發送響應信號,DHT11發送響應信號后,再把總線拉高80us,準備發送數據,每一bit數據都以50us低電平時隙開始,高電平持續的時間達到70us表示1。
PWM脈寬調制
??我們要控制箱內溫度就要控制電熱絲的發熱量,通電時電熱絲發熱,其向箱內輸入的熱量大于箱子向外散失的熱量,箱內溫度升高;斷電時,電熱絲不在產熱,但其仍有余溫,其依然能向箱內輸入熱量,如果在斷電前電熱絲的溫度已經很高了,則在斷電后的前一段時間內依然會向箱子輸入大量的熱量,箱內溫度還會上升,但是一段時間后,電熱絲自身溫度降低,向箱內輸入的熱量小于箱子向外散失的熱量,箱內溫度就會降低。故而通過調節通電時間和斷電時間就可以控制電熱絲的發熱量和箱子散熱量。以PWM控制開關器件從而控制電熱絲能夠滿足上述要求,調節占空比就可以達到調節通電時間和斷電時間的目的。
??PWM中還有一個重要的參數就是頻率,盡管不能計算出這個值到底是多少,但也不能隨意設置,頻率過高會導致開關器件的開關損耗增大,發熱較為嚴重;頻率過低會導致輸出響應速度變慢,系統調節時間增長,因為在頻率很低的情況下,電熱絲的通電時間或者斷電時間就會變得很長,如果通電時間長則斷電時間就會相應的縮短,那么電熱絲產生的熱量就會很多,甚至溫度已經超過設定值電熱絲仍在發熱,而即使此時電熱絲斷電,在自身的高溫下箱內溫度還會持續上升,甚至電熱絲溫度還未下降多少又開始加熱了;同理,如果斷電時間很長的情況下,箱內溫度已經低于設定值很多了但斷電時間還沒過去,溫度還會持續下降,即使此時電熱絲開始加熱,溫度還未回升可能電熱絲就斷電了。如此往復,溫度值震蕩的會很厲害,調節時間會變得很長。或許你會問,系統及時做出反應,修正占空比不就可以避免溫度過高或者過低了嗎?系統的及時性是有限度的,系統每采樣一次就會做出一次修正,但是采樣周期不能小于PWM周期,過于頻繁的更改占空比不僅會導致控制器輸出波形變形,還會使電熱絲來不及做出反應(箱內溫度變化較慢)。
??在PWM頻率很低時可以形象的按照上述所說來理解,在中高頻時,PWM控制電熱絲發熱原理并不是上述那般,而是通過改變占空比達到改變輸出電壓的原理,調低占空比,那么高電平時間減小,故而電熱絲兩端電壓降低,則發熱量降低。
注:通電時間和斷電時間可以理解為高電平持續時間和低電平持續時間。
補充: MOS管驅動
??后來有朋友問我MOS管驅動電路的問題,于是就在做些補充,介紹幾種MOS驅動電路。當然如果用MOS管驅動芯片那是最好的。之所以需要MOS管驅動電路是因為單片機IO口輸出電壓電流都比較小,驅動能力低,如果單片機輸出PWM信號來控制MOS管,驅動波形有可能會有所形變,最重要的是柵極電壓低不能使MOS管完全導通,其自身內阻較大,發熱嚴重。
??上圖是IRF3205的輸出特性曲線圖,豎軸是ID電流,橫軸是UDS電壓,圖中的幾條曲線分別代表UGS=4.5v、5v、5.5v、6v、7v、8v、10v、15v時ID隨UDS變化而變化的情況,在UDS=10v、UGS=8v時,ID大概在300A左右了,說明此時MOS管內阻已經很小了,繼續增大UGS,ID還能繼續增大,UGS=15v時,ID大概在600A,為了方便使用,UGS=12v就可以了。
??其中IRF4905使P溝道MOS管,IRF3205是N溝道MOS管,s8050是NPN三極管,s9012是PNP三極管。5個電路中三極管都工作在飽和區,所以基極與信號輸入之間串聯的電阻不可取得太大,但也不能沒有,否則IO口輸出高電平導通三極管后,電壓被強行鉗位在Ube上,串聯一個電阻不僅可以限流還可以分壓;在每個電路中,信號輸入后面都有一個二極管,它可以起到保護單片機IO口的作用(在一次實驗中,我還沒有給單片機通電,但先通了12V的電源,因為單片機用電和后面電熱絲用電沒有隔離,接通12V電源瞬間stm32核心板上面的指示燈也亮了,趕緊斷電然后加上了二極管,之后就沒出現這種情況了,所以低壓控制高壓最好用隔離器件,像繼電器、光耦,在頻率不是很高但功率較大的情況下,可以用光耦驅動MOS管,MOS管帶負載);圖中所有MOS管的G極并了一個12V的穩壓二極管,這個二極管可以不要;圖中所有MOS管的G極上都有一個二極管與10歐電阻并聯,這個二極管可起到迅速關斷MOS管的效果,當然這是對N溝道MOS管而言,對P溝道MOS管能不能起到迅速導通還未可知,有時間試驗一下;三極管集電極上串聯的電阻有限流分壓作用,阻值過大會導致MOS管驅動能力不足,過小三極管發熱嚴重,經過實驗,取4~5K合適(這是對12V電源而言的);還有就是P溝道的MOS管電流要從S極流入,D極流出,N溝道MOS管相反。
??我們以圖a為例分析一下電路,這個三極管當作開關管使用,當高電平信號來臨時,三極管Q2導通,其集電極電壓拉低,MOS管G極也為低MOS管導通;當低電平信號來臨時,三極管Q2截至,其集電極電壓被拉高,MOS管G極也為高,MOS管截止。
PID算法
框圖
比例控制
??用戶設定值Sv表示最終將溫度穩定在Sv,從系統運行開始每隔一段時間就采集當前環境內的溫度,得樣本如下:
這些樣本數據也就是程序中的Pv,通過這些樣本數據我們可以知道當前環境溫度與用戶設定值之間的差值Ek,即Ek=Sv-Xk,Ek有三種情況,如表所示:
| Ek > 0 | 當前環境溫度未達標 |
| Ek = 0 | 當前環境溫度滿足要求 |
| Ek < 0 | 當前環境溫度已超標 |
??由于我們是通過改變PWM的占空比來調節電熱絲輸出功率的,PID算法所計算出的數值OUT(最終輸出值,OUT=Pout+Iout+Dout) 就是PWM的脈寬。故而在Ek > 0時我們需要加大Pout從而加大OUT來提高電熱絲輸出功率,Ek < 0時則降低Pout從而降低電熱絲輸出功率,得:
這種算法稱為比例控制算法,out0是一個常數,可設置為1,以避免Ek = 0時Out也等于0,Kp表示比例系數,其大小將直接影響系統的響應速度,不難理解,如果Kp很大,那么一個小的差值Ek也會得到一個較大的數值Pout,那么系統將會出現劇烈震蕩,很難達到穩定,同樣的道理,Kp過小則系統的響應速度太慢,盡管有一個很大的差值Ek也只能得到一個較小的數值Pout,故而系統需要很長一段時間才能達到穩定狀態附近。
積分控制
??通過用戶設定值Sv與我們采集的環境溫度數據做差,我們得到了差值Ek,由于連續的溫度采集,于是有了一系列的差值樣本:
其中:
將這些差值進行累加,得:
同樣,SE也有三種情況,如表:
| SE > 0 | 歷史數據大多數未達標甚至從未達標 |
| SE = 0 | 控制效果較理想 |
| SE < 0 | 歷史數據大多數超標甚至是一直在超標 |
有此可知,SE的正負可以反映出歷史溫度處于哪種階段,對于SE > 0,歷史溫度大多數未達標則將加大Iout從而加大OUT來提高電熱絲輸出功率,同理,SE < 0時,歷史溫度大多數超標則將降低Iout從而*降低電熱絲輸出功率。這種算法稱為積分控制算法。公式如下:
在單片機中,對于積分運算可近似變換,即:
我們知道求積分其實就是求面積,如下圖所示,abcd所圍城圖形的面積就是積分所求的面積,矩形abce的面積 Ek * T 就是近似變換球的的面積,其中 T 表示采樣時間。
由上式可得積分控制算法可寫為:
Kp表示比例系數,T表示采樣時間,out0是一個常數,避免歷史差值積分為0時無輸出值,Ti表示積分時間,其大小會影響 Iout 的大小,從而影響OUT的大小,當Ti的值很大時,環境溫度需要較長的時間才能回到設定值,無論當前環境溫度大于還是小于設定值,當Ti較小時,環境溫度波動會比較大,且震蕩衰減小。
微分控制
??前面我們獲得了差值樣本,那么最近兩次差值之差可表示為:
同樣,Dk也有三種情況,如下表:
| Dk > 0 | 差值有增大趨勢 |
| Dk = 0 | 差值趨勢平穩 |
| Dk< 0 | 差值有減小趨勢 |
??Dk能反應最近兩次采樣的溫度的狀態變化趨勢,Dk的絕對值越大表明溫度變化速率越大,由Dk的公式可知,溫度呈上增長時,Dk為負數,增長速率越大,Dk越小,當溫度呈下降低時,Dk為正數,下降速率越大,Dk越大。由此可知Dk具有抑制溫度變化的功能,并使其趨于穩定。數學模型可表達為:
在單片機中,微分可做近似變換,即:
從而有:
Kp表示比例系數,out0是一個常數,避免Dk為0時取輸出信號,T表示采樣時間,Td表示微分時間,Td越大微分作用越強,即抑制效果越明顯。
整合后的PID算法公式如下:
或簡寫為:
??調節Kp、Ki、Kd三個值從而調節系統輸出,以改善系統響應效果。
比例系數與積分時間的大小對曲線的影響如下:
注:以上均屬個人片面之理解,有誤之處請留言,我很愿意將這篇文章完善的更好
程序
LedAndBeep.h
#ifndef _LEDANDBEEP_H #define _LEDANDBEEP_H#include "sys.h" #include "DHT11.h"#define led_1 GPIO_SetBits(GPIOB,GPIO_Pin_0) #define led_0 GPIO_ResetBits(GPIOB,GPIO_Pin_0)#define beep_1 GPIO_SetBits(GPIOB,GPIO_Pin_1) #define beep_0 GPIO_ResetBits(GPIOB,GPIO_Pin_1)void GPIO_init_Alert(void); void Delay_ms(int k); void Alert(void);#endifLedAndBeep.c
#include "LedAndBeep.h" #include "PID.h"void GPIO_init_Alert() {GPIO_InitTypeDef Alert_GPIO;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);Alert_GPIO.GPIO_Mode = GPIO_Mode_Out_PP;Alert_GPIO.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;Alert_GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &Alert_GPIO);led_0;beep_0; }void Alert() {if((DHT_Data[0]>70)||(DHT_Data[0]==70)||(DHT_Data[0]<45)||(DHT_Data[0]==45))//濕度不在45~70之間就報警{led_1;if(pid.C10ms<(pid.T/2))//pid.C10ms在中斷函數中,蜂鳴器響的時間小于250msbeep_1;elsebeep_0;}else{led_0;beep_0;} }DHT11.h
#ifndef __DHT11_H #define __DHT11_H #include "sys.h" extern char DHT_Data[5];//IO方向設置 #define DHT11_IO_IN() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;} #define DHT11_IO_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;} IO操作函數 #define DHT11_DQ_OUT PBout(11) //數據端口 PB11輸出 #define DHT11_DQ_IN PBin(11) //數據端口 PB11輸入u8 DHT11_Init(void);//初始化DHT11 u8 DHT11_Read_Data(void);//讀取溫濕度 u8 DHT11_Read_Byte(void);//讀出一個字節 u8 DHT11_Read_Bit(void);//讀出一個位 u8 DHT11_Check(void);//檢測是否存在DHT11 void DHT11_Rst(void);//復位DHT11 #endifDHT11.c
#include "DHT11.h" #include "delay.h" #include "PID.h"char DHT_Data[5]={0}; // DHT_Data[0]、DHT_Data[1]存儲濕度數據 //DHT_Data[2]、DHT_Data[3]存儲溫度數據 void DHT11_Rst(void) { DHT11_IO_OUT(); //SET OUTPUTDHT11_DQ_OUT=0; //拉低DQdelay_ms(20); //拉低至少18msDHT11_DQ_OUT=1; //DQ=1 delay_us(30); //主機拉高20~40us }u8 DHT11_Check(void) { u8 retry=0;DHT11_IO_IN();//SET INPUT while (DHT11_DQ_IN&&retry<100)//DHT11會拉低40~80us{retry++;delay_us(1);}; if(retry>=100)return 1;else retry=0;while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后會再次拉高40~80us{retry++;delay_us(1);}if(retry>=100)return 1; return 0; }u8 DHT11_Read_Bit(void) {u8 retry=0;while(DHT11_DQ_IN&&retry<100)//等待變為低電平{retry++;delay_us(1);}retry=0;while(!DHT11_DQ_IN&&retry<100)//等待變高電平{retry++;delay_us(1);}delay_us(40);//等待40usif(DHT11_DQ_IN)return 1;else return 0; }u8 DHT11_Read_Byte(void) { u8 i,dat;dat=0;for (i=0;i<8;i++) {dat<<=1; dat|=DHT11_Read_Bit();} return dat; }u8 DHT11_Read_Data(void) { u8 i;DHT11_Rst();if(DHT11_Check()==0){for(i=0;i<5;i++)//讀取40位數據{DHT_Data[i]=DHT11_Read_Byte();}if((DHT_Data[0]+DHT_Data[1]+DHT_Data[2]+DHT_Data[3])==DHT_Data[4]){pid.Pv=DHT_Data[2]+(DHT_Data[3]/10);return 0; }}elsereturn 1;return 0; }u8 DHT11_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PG端口時鐘GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PG11端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽輸出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化IO口GPIO_SetBits(GPIOB,GPIO_Pin_11); //PG11 輸出高DHT11_Rst(); //復位DHT11return DHT11_Check();//等待DHT11的回應 }LCD1602.h
#ifndef LCD1602_H #define LCD1602_H#include "sys.h"#define RS GPIO_Pin_8 //設置PB8為RS #define RW GPIO_Pin_6 //PB6為RW #define EN GPIO_Pin_7 //PB7為EN使能void ReadBusy(void); void LCD_WRITE_CMD( char CMD ); void LCD_WRITE_StrDATA( char *StrData, char row, char col ); void LCD_WRITE_ByteDATA( char ByteData ); void LCD_INIT(void); void GPIO_INIT(void);#endifLCD1602.c
#include "LCD1602.h" #include "delay.h"void GPIO_INIT(void) { //GPIO初始化GPIO_InitTypeDef GPIO;GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //禁用jtagRCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE );GPIO.GPIO_Pin = EN|RW|RS;GPIO.GPIO_Mode = GPIO_Mode_Out_PP;GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO);GPIO.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;GPIO.GPIO_Mode = GPIO_Mode_Out_PP;GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO);}void LCD_INIT(void) { //初始化GPIO_INIT(); GPIO_Write(GPIOA, 0x0000); GPIO_Write(GPIOB, 0x0000);delay_us(500); LCD_WRITE_CMD(0x38);LCD_WRITE_CMD(0x0d); //開啟光標和閃爍LCD_WRITE_CMD(0x06);LCD_WRITE_CMD(0x01); }void LCD_WRITE_CMD(char CMD) {//寫入命令函數ReadBusy();GPIO_ResetBits(GPIOB, RS);GPIO_ResetBits(GPIOB, RW);GPIO_ResetBits(GPIOB, EN);GPIO_Write(GPIOA, CMD); //GPIO_SetBits(GPIOB, EN);GPIO_ResetBits(GPIOB, EN); }void LCD_WRITE_ByteDATA(char ByteData ) { //寫入單個Byte函數ReadBusy();GPIO_SetBits(GPIOB, RS);GPIO_ResetBits(GPIOB, RW);GPIO_ResetBits(GPIOB, EN);GPIO_Write(GPIOA, ByteData);GPIO_SetBits(GPIOB, EN);GPIO_ResetBits(GPIOB, EN); }void LCD_WRITE_StrDATA(char *StrData,char row, char col) {//寫入字符串char baseAddr = 0x00; //定義256位地址if (row){baseAddr = 0xc0;}else{baseAddr = 0x80; } baseAddr += col;while (*StrData != '\0'){LCD_WRITE_CMD( baseAddr );LCD_WRITE_ByteDATA( *StrData); baseAddr++; StrData++;} }void ReadBusy(void) { //讀忙函數,讀忙之前記得更改引腳的工作方式!!!因為STM32的IO不是準雙向IOGPIO_InitTypeDef GPIO;GPIO_Write(GPIOA, 0x00ff); GPIO.GPIO_Pin = GPIO_Pin_7; //選定GPIOA的第七PinGPIO.GPIO_Mode = GPIO_Mode_IN_FLOATING; //第七Pin的工作方式為浮空輸入模式,用于檢測LCD1602的忙狀態GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO);GPIO_ResetBits(GPIOB, RS);//RS拉低GPIO_SetBits(GPIOB, RW);//RW拉高GPIO_SetBits(GPIOB, EN); //使能開while( GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_7 )); //讀第七Pin狀態,如果一直為1則循環等待GPIO_ResetBits(GPIOB, EN);//使能關GPIO.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //使GPIOA的狀態還原成推挽模式GPIO.GPIO_Mode = GPIO_Mode_Out_PP;GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO); }PID.h
#ifndef PID_H_ #define PID_H_typedef struct Pid {float Sv;//用戶設定值float Pv;float Kp;int T; //PID計算周期--采樣周期float Ti;float Td; float Ek; //本次偏差float Ek_1;//上次偏差float SEk; //歷史偏差之和float Iout;float Pout;float Dout;float OUT0;float OUT;int C1ms;int pwmcycle;//pwm周期int times; }PID;extern PID pid;void PID_Init(void); void PID_Calc(void);#endifPID.c
#include "PID.h"PID pid;void PID_Init() {pid.Sv=38;//用戶設定溫度pid.Kp=30;pid.T=400;//PID計算周期pid.Ti=4000000;//積分時間pid.Td=1000;//微分時間pid.pwmcycle=200;//pwm周期200pid.OUT0=1;pid.C1ms=0; }void PID_Calc() //pid計算 {float DelEk;float ti,ki;float td;float kd;float out;if(pid.C1ms<(pid.T)) //計算周期未到{return ;}pid.Ek=pid.Sv-pid.Pv; //得到當前的偏差值pid.Pout=pid.Kp*pid.Ek; //比例輸出pid.SEk+=pid.Ek; //歷史偏差總和DelEk=pid.Ek-pid.Ek_1; //最近兩次偏差之差ti=pid.T/pid.Ti;ki=ti*pid.Kp;pid.Iout=ki*pid.SEk; //積分輸出td=pid.Td/pid.T;kd=pid.Kp*td;pid.Dout=kd*DelEk; //微分輸出out= pid.Pout+ pid.Iout+ pid.Dout;if(out>pid.pwmcycle){pid.OUT=pid.pwmcycle;}else if(out<=0){pid.OUT=pid.OUT0; }else {pid.OUT=out;}pid.Ek_1=pid.Ek; //更新偏差pid.C1ms=0; }PWMOUT.h
#ifndef PWMOUT_H #define PWMOUT_H#include "sys.h"void Time_init(void); void TimePwm_init(int arr,int psc);#endifPWMOUT.c
#include "PWMOUT.h" #include "PID.h" #include "LedAndBeep.h"void Time_init(void) {NVIC_InitTypeDef NVIC_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 自動裝載的計數值 1msTIM_TimeBaseStructure.TIM_Period = 1000; // 10KHzTIM_TimeBaseStructure.TIM_Prescaler = (72 - 1); // 1MHzTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);TIM_ClearITPendingBit(TIM2, TIM_IT_Update);TIM_Cmd(TIM2,ENABLE); }void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2,TIM_IT_Update)){pid.C1ms++;Alert();TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中斷標志} }void TimePwm_init(int arr,int psc) {GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructure.TIM_Period = arr; TIM_TimeBaseStructure.TIM_Prescaler =psc; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC2Init(TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }main.c
#include "LCD1602.h" #include "DHT11.h" #include "LedAndBeep.h" #include "PID.h" #include "PWMOUT.h" #include "delay.h" #include <string.h> #include <stdio.h>#define PERIOD 400 #define PRESCALER 36000 void Situation() {char hum[5]={0},temp[5]={0},PWM[10]={0},arr[5]={0x20,0x20,0x20,0x20,0x20};sprintf(hum,"%d.%d",DHT_Data[0],DHT_Data[1]);sprintf(temp,"%d.%d",DHT_Data[2],DHT_Data[3]);//顯示濕度LCD_WRITE_StrDATA( hum,0,5 ); LCD_WRITE_StrDATA("%",0,9 ); //顯示溫度LCD_WRITE_StrDATA( temp,0,11); LCD_WRITE_StrDATA("C",0,15 );//顯示pid.outLCD_WRITE_StrDATA("pid.out:",1,0); sprintf(PWM,"%f",pid.OUT);PWM[6]='\0';LCD_WRITE_StrDATA(PWM,1,9); }int main() {unsigned int num=0;GPIO_init_Alert();Time_init();DHT11_Init();PID_Init();LCD_INIT(); LCD_WRITE_CMD( 0x80 ); LCD_WRITE_CMD(0x0C); LCD_WRITE_StrDATA( "situ:",0,0 ); TimePwm_init(PERIOD-1,PRESCALER);while(1){while(DHT11_Read_Data());PID_Calc();num=(((pid.OUT*PERIOD)/pid.pwmcycle)-1);TIM_SetCompare2(TIM3,num);Situation(); } }工程此處下載
鏈接:https://pan.baidu.com/s/1idVRZF63PP-Yq9c3-uDhPQ
提取碼:2j3v
總結
以上是生活随笔為你收集整理的STM32——PID恒温控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019最新C语言知识整理小白进来看看?
- 下一篇: 简易数字时钟软件详细制作过程