当前位置:   article > 正文

基于STM32单片机的温控系统(PID算法)_stm32 pid温度控制

stm32 pid温度控制

简言:巩固学习,分享思路,欢迎指正。

一、整体设计思路

温控系统整体的供电采用12v锂电池并联到面包板上,分别给l298n驱动模块和stm32单片机供电。单片机根据反馈回来的信号结合pid算法控制L298n模块输出不同电压值给ptc加热片和tec制冷片,从而实现制冷和制热的效果。

二、具体实现流程

2.1、所需硬件整理

2.1.1 stm32单片机

stm32最小系统板一个,芯片型号:stm32f103c8t6。网上相关资料非常多这里不多赘述。
在这里插入图片描述

2.1.2 12v锂电池

系统的整体供电我选择的是12V锂电池,有一个原因是12V并联到面包板上方便直接给L298n驱动模块供电。(大家购买时记得买一个转接头如下图所示,会方便很多)
在这里插入图片描述

2.1.3 降压模块

stm32f103c8t6工作电压在2.0到3.6V之间,5v电压也不耐受(由于商家将3.3v降压模块发错成5v,加上我没有用万用表检测,损失一枚芯片)。更不用说是12v电源直接供电了,故还需要一个降压模块。降压模块大家可以选择直接输出3.3v的也可以选择可调式输出的。图片放上
在这里插入图片描述

2.1.4 L298n驱动模块

常见的小型车电机驱动模块有两种——TB6612FNG和L298N驱动模块。这里我选择l298n作为驱动模块。
在这里插入图片描述

2.1.5 PTC加热片

这里我选用的是12V,110度的PTC加热片,如图所示。
在这里插入图片描述

2.1.6 TEC制冷片

也叫半导体制冷片,具体工作原理不和大家解释,想知道的小伙伴可以去网上搜索,网上讲的肯定比我详细。我是买了一个散装的套件需要自己动手。
在这里插入图片描述

2.1.7 DS18B20温度感知模块

DS18B20温度传感器模块,大家也可以买一个带防水外壳的,我就直接用了最普通的。
在这里插入图片描述
在这里插入图片描述

2.1.8 OLED显示屏

这里我选用0.96寸四引脚的OLED显示屏。用啥显示屏都是可以的,作用是为了显示实时温度和其他一些数据。
在这里插入图片描述

2.1.9 面包板

面包板我选用的是830孔的,我们现在是模块化制作,到后面可以进阶一下,绘制一个PCB,性能会稳定很多。
在这里插入图片描述

2.2.0 小部件

杜邦线、跳线和按键。按键的购买如图所示即可。
在这里插入图片描述

2.2、模块接线原理图

正负电源不要接反,接反即冒烟。
(1)stm32f103c8t6最小系统
在这里插入图片描述

(2)L298N电机驱动模块 //注意不要接反
在这里插入图片描述

(3)DS18B20温度传感模块

在这里插入图片描述

(4)OLED显示屏
在这里插入图片描述
(5)按键
在这里插入图片描述
(6)蜂鸣器
在这里插入图片描述
其他的一些元器件自己可以任意添加配置,如LED小灯。

2.3、主要代码实现

2.3.1 main函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Cooling.h"
#include "Key.h"
#include "Buzzer.h"
#include "DS18B20.h"
#include "PID.h"
#include "PID2.h"
#include "Timer.h"
#include "LED.h"
//所需要的变量声明
uint8_t TEMP;						
uint8_t SETTEMP;//目标温度					
uint8_t flag100ms;
uint8_t flag500ms;
uint8_t KeyNum;	//按键
uint8_t	Hot_flag=0;//制热标志位
uint8_t	Cool_flag=0;//制冷标志位
//主函数逻辑很简单
int	main(void)
{
	int8_t	Speed;
	int8_t	Speed2;
	uint16_t	Temp;
	SETTEMP=25;
	//一些初始化
	Motor_Init();
	Cooling_Init();
	Key_Init();
	Buzzer_Init();
	OLED_Init();
	DS18B20_UserConfig();
	PID_Init();
	PID2_Init();
	TIM3_Int_Init(10-1,7200);
	//oled显示
	OLED_ShowString(1, 1, "Speed:");//占空比显示
	OLED_ShowString(2, 1,"Temp:");//实时温度
	OLED_ShowString(3, 1,"Mode:");//工作模式
	OLED_ShowString(4, 1,"SetTp:");//目标温度
	
	while(1)
	{
		KeyNum=Key_GetNum();
		if(KeyNum==2)
		{
			SETTEMP+=5;
			if(SETTEMP>50)//这边可以根据你的加热片功率和幅值改变,这里我随便设置了一个
			SETTEMP=25;
			Hot_flag=1;
		}
		if(KeyNum==1)
		{
			SETTEMP-=5;
			if(SETTEMP<10)//同上
			SETTEMP=25;
			Cool_flag=1;
		}
		Temp=DS18B20_Read_Temp();
		if(flag100ms)			
		{
			flag100ms=0;		
			flag500ms++;
			if(flag500ms==5)
			{
				flag500ms=0;
				TEMP=Temp/10;
				OLED_ShowTemp(2,7,Temp,4);
				//OLED_ShowSignedNum(2,6,TEMP,4);
				OLED_ShowSignedNum(4,7,SETTEMP,3);
				if(SETTEMP<25)
				{
					//Cool_flag=1;
					Speed2=PID2_realize();
					Cooling_SetSpeed(Speed2);
					OLED_ShowString(3, 7, "Cool");
					OLED_ShowNum(1,7,Speed2,4);
					Motor_SetSpeed(0);
				}
				if(SETTEMP>=25&&SETTEMP<=30)
				{
					Cooling_SetSpeed(0);
					Motor_SetSpeed(0);
					OLED_ShowString(3, 7, "Wait");
					OLED_ShowString(1, 7, "Wait");
				}
				if(SETTEMP>30)
				{
					//Hot_flag=1;
					Speed=PID_realize();
				    Motor_SetSpeed(Speed);
					Cooling_SetSpeed(0);
					OLED_ShowString(3, 7, "Warm");
					OLED_ShowNum(1,7,Speed,4);
				}
				if(TEMP>37||TEMP<15)//设置的一些报警装置,这个可以随便加
					 LED1_ON();
				else
					Buzzer_Off();
			}
		}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106

2.3.2 pid算法

涉及制冷和制热两个模块,两个PID算法。pid算法参数调节的讲解网上教程很多这里不做讲解。此代码中是我已经调节好的参数(不同配置参数各有不同)废话不多说直接上代码。

#include "stm32f10x.h"                  // Device header
#include "PID.h"
#include "OLED.h"

extern uint8_t TEMP;						//存储一位小数的实际温度
extern uint8_t SETTEMP;					//存储一位小数的目标温度
extern uint8_t Hot_flag;

struct PID pid;

void PID_Init(void)					//因为有存储,因此用ReadPID()
{
	pid.Set=SETTEMP;
	pid.Actual=TEMP;
	pid.err=0;
	pid.err_last=0;                                                                                                                                                                                                   

	pid.Kp=11;						//实际为除一倍

	pid.Ki=9;						//实际为除一倍
	pid.Kd=8;						//实际为除一倍
	//pid.Kout=2;	
	pid.voltage=0;
	pid.integral=0;					//累计误差值
}

int PID_realize(void)
{
	int t;
	
	if(Hot_flag==1)
	{
		pid.integral=0;
		Hot_flag=0;
	}
	
	pid.Set      = 	SETTEMP;						//设定值
	pid.Actual   =  TEMP;								//实际值
	//if(pid.Set>pid.Actual)
	pid.err      = pid.Set - pid.Actual;	//实际温度与目标温度之间的差值
	//else
	//pid.err      = pid.Actual - pid.Set;	

	pid.integral = pid.integral + pid.err;

	pid.voltage = pid.Kp*pid.err								//设定值与实际的偏差
				+ pid.Ki*pid.integral/1000						//历史累计偏差
				+ pid.Kd*(pid.err-pid.err_last);			//前一次误差与此次为误差
																						
	
	pid.err_last = pid.err;
	
	t=pid.voltage;
	//限幅
	if(t>99)		t=99;
	else if(t<=0)	t=3;
	return t;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
#include "stm32f10x.h"                  // Device header
#include "PID2.h"
#include "OLED.h"

extern uint8_t TEMP;						//存储一位小数的实际温度
extern uint8_t SETTEMP;					//存储一位小数的实际温度
extern uint8_t Cool_flag;
//extern uint8_t Hot_flag;
//extern uint8_t KeyNum;	
struct PID2 pid2;

void PID2_Init(void)					
{
	pid2.Set= SETTEMP-1;
	pid2.Actual= TEMP;
	pid2.err=0;
	pid2.err_last=0;                                                                                                                                                                                                   

	pid2.Kp=60;						//实际为除一倍
	pid2.Ki=40;						//实际为除一倍
	pid2.Kd=35;						//实际为除一倍
	//pid.Kout=2;						//偏移值
	pid2.voltage=0;
	pid2.integral=0;					//累计误差
}

int PID2_realize(void)
{
	int t;
	if(Cool_flag==1)
	{
		pid2.integral=0;
	}
	pid2.Set      = TEMP;						//实际值
	pid2.Actual   = SETTEMP-1;								//设定值
	//if(pid.Set>pid.Actual)
	pid2.err      = pid2.Set - pid2.Actual;		//实际温度与目标温度之间的差值
	//else
	//pid.err      = pid.Actual - pid.Set;	

	pid2.integral = pid2.integral + pid2.err;
	//OLED_ShowSignedNum(4,6,pid2.integral,5);

	pid2.voltage = pid2.Kp*pid2.err								//设定值与实际的偏差
				+ pid2.Ki*pid2.integral/1000						//历史累计偏差
				+ pid2.Kd*(pid2.err-pid2.err_last);			//前一次误差与此次为误差
	
	pid2.err_last = pid2.err;
	t=pid2.voltage;
	//限幅
	if(t>99)		t=99;
	else if(t<=0)	t=3;
	Cool_flag=0;
	
	return t;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

2.3.3 输出比较代码

#include "stm32f10x.h"                  // Device header

void PWM_Init(void){
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //打开时钟
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);		
		
	GPIO_InitTypeDef	GPIO_InitStructure;		
	GPIO_InitStructure.GPIO_Mode	=	GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin 	=	GPIO_Pin_2|GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed 	=	GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);	//选择内部时钟
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;	//初始化时基单元
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100-1;					//周期ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720-1;				//预分频器PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR的值
	TIM_OC3Init(TIM2,&TIM_OCInitStructure);
	TIM_OC4Init(TIM2,&TIM_OCInitStructure);
	
	TIM_Cmd(TIM2,ENABLE);  //启动定时器
	
}

void PWM_SetCompare3(uint16_t speed){
	
		TIM_SetCompare3(TIM2,speed);
		
}

void PWM_SetCompare4(uint16_t speed){
	
		TIM_SetCompare4(TIM2,speed);
		
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

2.3.4 驱动加热片代码

#include "stm32f10x.h"                  // Device header
#include "PWM.h"
void Motor_Init(void){
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);		
		
	GPIO_InitTypeDef	GPIO_InitStructure;		
	GPIO_InitStructure.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin 	=	GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed 	=	GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
 
	PWM_Init();
}    

void Motor_SetSpeed(int8_t Speed){
	if(Speed>=0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		PWM_SetCompare3(Speed);
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.3.5 驱动制冷片代码

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Cooling_Init(void){
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);		
		
	GPIO_InitTypeDef	GPIO_InitStructure;		
	GPIO_InitStructure.GPIO_Mode	=	GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin 	=	GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed 	=	GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
 
	PWM_Init();
}

void Cooling_SetSpeed(int8_t Speed){
	
	if(Speed>=0)
	{
		GPIO_SetBits(GPIOA,GPIO_Pin_6);
		GPIO_ResetBits(GPIOA,GPIO_Pin_7);
		PWM_SetCompare4(Speed);
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这里给出本项目的核心代码,未给出的代码部分根据需求可自行编写。

三、系统工作视频

基于STM32单片机的温控系统(PID算法)

总结

温控系统的调参和温度控制效果可通过上位机实现。整体设计实现相对简单。但其中蕴含的知识和原理十分值得学习。若有错误,欢迎大家指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/372956
推荐阅读
相关标签
  

闽ICP备14008679号