文章目錄
- 0 簡介
- 1 專案介紹
- 2 系統設計
- 3 恒溫控制實作
- 3.1 功能描述
- 3.2 PID演算法原理
- 3.2.1 P:比例
- 3.2.2 I:積分
- 3.2.3 D:微分
- 3.3 溫控代碼實作
- 4 實作效果
- 5 最后
0 簡介
Hi,大家好,這里是丹成學長,今天向大家介紹一個 單片機專案
基于stm32的智能水杯 - 恒溫控制
大家可用于 課程設計 或 畢業設計
技術解答、畢設幫助、開題指導
print("Q 746876041")
1 專案介紹
今天向大家介紹學長設計的一個畢設專案,基于STM32單片機控制的智能水杯,可利用插口式電源或無線充電底座為加熱器提供能量,并在達到某種飲品所需溫度時進行保溫, 水杯內置充電電池,可選用 USB 介面或無線充電, 在水杯內部設定無線模塊,用戶利用上位機與水杯進行匹配進行加熱操作,加熱完后水杯會通過上位機和液晶顯示屏實時反饋液體溫度,通過指示燈顯示電量情況等,給用戶進行提醒,
2 系統設計
該智能水杯控制系統是由 STM32 單片機作為主要控制芯片,接受和發送信號給溫度傳感器,進行溫度檢測;溫度檢測是由溫度傳感器來執行,實時采集水杯內液體溫度并利用 LCD 屏顯示; 電池檢測由電池電壓指示電路進行操控, 實時檢測電池電量, 并通過 LED 燈進行提示;內置充電電池充電可提供無線和 USB介面充電方式;控溫及發熱系統由 PTC 發熱體構成, 并利用倍訓控制使水杯液體溫度恒溫;利用無線模塊,接收來自手機App所發出的指令, 通過手機App與水杯進行匹配進行加熱操作,并反饋水杯的使用狀態
系統設計

3 恒溫控制實作
智能水杯的核心功能是水溫的恒溫控制,為了實作這一點,學長選用了以下元器件
stm32f103核心板、L298N模塊(當然用MOS管更好)、led一個、NPN三極管一個、蜂鳴器一個、DHT11一個、LCD1602一個、電阻200歐兩個、可調電阻10K一個、加熱絲一個
3.1 功能描述
用DHT11檢測當前環境溫濕度,并將資料顯示在LCD1602上,在用設定溫度與當前溫度相減,通過PID演算法計算出當前輸出脈寬,并將其加在L298N模塊中,使加熱絲發熱,形成一個倍訓,經過一段時間溫度穩定在設定值,

3.2 PID演算法原理

3.2.1 P:比例
成比例地反映控制系統的偏差信號e(t),偏差一旦產生,控制器立即產生控制作用,以減小偏差,當僅有比例控制時系統輸出存在穩態誤差(Steady-state error),
P引數越小比例作用越強,動態回應越快,消除誤差的能力越強,通常將P引數由大向小調,以能達到最快回應又無超調(或無大的超調)為最佳引數,
3.2.2 I:積分
為消除靜差,提高系統的無差度,積分作用的強弱取決于積分時間常數T,T越大,積分作用越弱,反之則越強,
3.2.3 D:微分
反映偏差信號的變化趨勢,并能在偏差信號變得太大之前,在系統中引入一個有效的早期修正信號,從而加快系統的動作速度,減少調節時間,在微分控制中,控制器的輸出與輸入誤差信號的微分(即誤差的變化率)成正比關系,
D越大,微分作用越強,D越小,微分作用越弱,系統除錯時通常把D從小往大調,具體引數由試驗決定,
3.3 溫控代碼實作
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);
#endif
LedAndBeep.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在中斷函式中,蜂鳴器響的時間小于250ms
beep_1;
else
beep_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
#endif
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);
#endif
LCD1602.c
#include "LCD1602.h"
#include "delay.h"
void GPIO_INIT(void)
{ //GPIO初始化
GPIO_InitTypeDef GPIO;
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //禁用jtag
RCC_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不是準雙向IO
GPIO_InitTypeDef GPIO;
GPIO_Write(GPIOA, 0x00ff);
GPIO.GPIO_Pin = GPIO_Pin_7; //選定GPIOA的第七Pin
GPIO.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);
#endif
PID.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周期200
pid.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;
}
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.out
LCD_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();
}
}
4 實作效果


5 最后
技術解答、畢設幫助、開題指導
print("Q 746876041")
獲取更多畢設資料,關注公眾號
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/395242.html
標籤:其他
