赞
踩
温控系统整体的供电采用12v锂电池并联到面包板上,分别给l298n驱动模块和stm32单片机供电。单片机根据反馈回来的信号结合pid算法控制L298n模块输出不同电压值给ptc加热片和tec制冷片,从而实现制冷和制热的效果。
stm32最小系统板一个,芯片型号:stm32f103c8t6。网上相关资料非常多这里不多赘述。
系统的整体供电我选择的是12V锂电池,有一个原因是12V并联到面包板上方便直接给L298n驱动模块供电。(大家购买时记得买一个转接头如下图所示,会方便很多)
stm32f103c8t6工作电压在2.0到3.6V之间,5v电压也不耐受(由于商家将3.3v降压模块发错成5v,加上我没有用万用表检测,损失一枚芯片)。更不用说是12v电源直接供电了,故还需要一个降压模块。降压模块大家可以选择直接输出3.3v的也可以选择可调式输出的。图片放上
常见的小型车电机驱动模块有两种——TB6612FNG和L298N驱动模块。这里我选择l298n作为驱动模块。
这里我选用的是12V,110度的PTC加热片,如图所示。
也叫半导体制冷片,具体工作原理不和大家解释,想知道的小伙伴可以去网上搜索,网上讲的肯定比我详细。我是买了一个散装的套件需要自己动手。
DS18B20温度传感器模块,大家也可以买一个带防水外壳的,我就直接用了最普通的。
这里我选用0.96寸四引脚的OLED显示屏。用啥显示屏都是可以的,作用是为了显示实时温度和其他一些数据。
面包板我选用的是830孔的,我们现在是模块化制作,到后面可以进阶一下,绘制一个PCB,性能会稳定很多。
杜邦线、跳线和按键。按键的购买如图所示即可。
正负电源不要接反,接反即冒烟。
(1)stm32f103c8t6最小系统
(2)L298N电机驱动模块 //注意不要接反
(3)DS18B20温度传感模块
(4)OLED显示屏
(5)按键
(6)蜂鸣器
其他的一些元器件自己可以任意添加配置,如LED小灯。
#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(); } } } }
涉及制冷和制热两个模块,两个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; }
#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; }
#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); }
#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); } }
#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); } }
这里给出本项目的核心代码,未给出的代码部分根据需求可自行编写。
基于STM32单片机的温控系统(PID算法)
温控系统的调参和温度控制效果可通过上位机实现。整体设计实现相对简单。但其中蕴含的知识和原理十分值得学习。若有错误,欢迎大家指正。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。