一、回顾
时隔好久才开始补充第二部分的内容,首先总结一下上篇的学习内容:
1、简单了解了NTC热敏电阻的基本概念和使用的原理。
2、对ADC的模拟量取值和代码进行了简单的学习。
3、通过计算公式将ADC转换成电压值,再通过并联电阻的大小进行阻值的计算。
4、最后有了阻值,我能就可以进行温度的查表,通过二分法的方式,一步步找到对应的温度值,可以通过特殊方式进行精确到小数点后几位。
二、控温方式
接下来进行加热设备的温度控制,因场景不需要很高的精确温度,所以我们使用的是简单的3D打印机中的热床加热垫,因为他既可以进行加温加热,有自带嵌入式NTC的传感器,可以实时监测加热垫中部的实际温度(因加热的时间,NTC的数值可能会慢半拍,还是存在误差,有需要可自行调整)。
以下是加热垫相关参数:
我思考的温度控制有两种方式:
1、一种是最简单的通过24V高电平对加热垫进行发送50%占空比的脉冲,当达到目标温度值+1的温度数值后拉低电平,停止加热垫的工作,当NTC监测到实际温度小于目标温度值-1时,再将对加热垫进行PWM脉冲的发送,使其开始加热,让加热垫始终保持在目标温度上下1度的浮动。(加一减一是为了能减少加热垫的PWM频繁开关)
2、另一种方法是我们自己将PWM占空比所对应的加热垫加热温度测试出来,形成固定的表格(就是一直发送百分之多少的占空比可以将温度控制在多少度,优点是更精确,但是比较麻烦),职中方式后期再做一下吧。
三、具体步骤
先实现最简单的功能思路(结合上篇笔记中的函数):
1、功能函数
1、我利用了OLED13寸的屏幕进行功能调试所以加入了OLED.c和OLED.h两个文件(后续会放在整个代码中开源),通过OLED屏幕的调试,我们首先将NTC监测到的温度值和当前的目标温度值显示在屏幕上,我直接使用了一个自定义的函数。
//主函数文件的开头定义:
//三个加热垫的目标温度值(全局变量)
uint32_t Temperature_Threshold_1 = 30; //预设的目标温度
uint32_t Temperature_Threshold_2 = 60; //预设的目标温度
uint32_t Temperature_Threshold_3 = 60; //预设的目标温度
//三个加热垫的编号,当做参数使用(可以根据需要显示具体哪一个加热垫)
#define TIM1_PWM 1 //1号加热板
#define TIM2_PWM 2 //2号加热板
#define TIM3_PWM 3 //3号加热板
//--------------------------------------显示函数------------------------------------------
/**
* 函 数:温度调试显示(Debug用,意义不大)
* 参 数:Ch 加热垫的对象
* 参 数:ADC_Channel 多取几次的到平均值
* 参 数:Ch 加热垫的对象
* 返 回 值:无
*/
void OLED_Temperature(Channel_Value *Ch,uint8_t ADC_Channel,uint8_t Line)
{
Ch->ADValue = Get_Adc_Average(5,ADC_Channel); //获取AD0转换的值(5毫秒采集1次,5次的平均值)(Get_Adc_Average为上篇文章中获取ADC模拟量的函数)
Ch->Voltage = Get_Vout(Ch->ADValue); //(Get_Vout为上篇文章中求电压的函数)
Ch->Resistance = Get_Resistance(Ch->ADValue); //(Get_Resistance为上篇文中求阻值的函数)
Ch->Temperature = Get_Temperature(Ch->Resistance,Temp_Resistance_Tab_2); //(Get_Temperature为上篇文章中查表求温度值的函数)
OLED_ShowString(Line, 2, "T : C");
OLED_ShowNum(Line,3,ADC_Channel + 1,1);
OLED_ShowDouble(Line, 5,Ch->Temperature, 1); //显示温度值
OLED_ShowDouble(1,13,Temperature_Threshold_1,0); //显示当前的温度阈值
OLED_ShowDouble(2,13,Temperature_Threshold_2,0); //显示当前的温度阈值
OLED_ShowDouble(3,13,Temperature_Threshold_3,0); //显示当前的温度阈值
OLED_ShowChar(1,16,'C');
OLED_ShowChar(2,16,'C');
OLED_ShowChar(3,16,'C');
}
这样主函数的while循环中我们可以直接调用OLED_Temperature函数显示某个加热板:
OLED_Temperature(&ch1,ADC_Channel_0,1);
OLED_Temperature(&ch2,ADC_Channel_1,2);
OLED_Temperature(&ch3,ADC_Channel_2,3);
2、当前我们只是显示出了温度和程序默认的目标温度值,还没有具体打开加热功能,后面我又写了一个单独打开某个加热垫的加热功能(就是打开PWM不停发脉冲)的函数。
//-------------------------------------加热开关---------------------------------------
/**
* 函 数:控制加热垫的开关
* 参 数:ch 加热垫的结构体对象
* 参 数:TIMX_PWM 要打开的加热垫
* 返 回 值:无
*/
void OPEN_CLOSE(Channel_Value *ch,uint8_t TIMX_PWM)
{
//如果是1号加热板
if(TIMX_PWM == 1)
{
//如果1号加热板的NTC温度到达设定的目标温度+1的温度之后
if(ch->Temperature > Temperature_Threshold_1 + 1)
{
//就关掉PWM的占空比,停止加热
PWM_SetCompare1(TIMX_PWM,0);
}
//如果1号加热垫NTC小于等于目标温度值-1,并且和目标温度相差5度以上,则就开启加温(因为温度相差较多,PWM占空比就大一些)
if(ch->Temperature <= Temperature_Threshold_1 - 1 && ((Temperature_Threshold_1 - ch->Temperature) > 5))
{
PWM_SetCompare1(TIMX_PWM,500 - 1);
}
//如果1号加热垫NTC小于等于目标温度值-1,并且和目标温度相差5度以内,则就开启加温(因为温度相差很小,PWM占空比就小一些,慢慢加热升温)
if(ch->Temperature <= Temperature_Threshold_1 - 1 && (Temperature_Threshold_1 - ch->Temperature) <= 5)
{
PWM_SetCompare1(TIMX_PWM,100 - 1);
}
}
//2号加热垫与上面同理(没加升温快慢的代码,可自行加上,也可不加)
else if(TIMX_PWM == 2)
{
if(ch->Temperature > Temperature_Threshold_2 + 1)
{
PWM_SetCompare1(TIMX_PWM,0);
}
if(ch->Temperature <= Temperature_Threshold_2 - 1)
{
PWM_SetCompare1(TIMX_PWM,500 - 1);
}
}
//3号加热垫与上面同理(没加升温快慢的代码,可自行加上,也可不加)
else if(TIMX_PWM == 3)
{
if(ch->Temperature > Temperature_Threshold_3 + 1)
{
PWM_SetCompare1(TIMX_PWM,0);
}
if(ch->Temperature <= Temperature_Threshold_3 - 1)
{
PWM_SetCompare1(TIMX_PWM,500 - 1);
}
}
}
这样主函数中就可以循环调用开启OPEN_CLOSE函数,开启各个通道的加热功能了(当然这里面包包含了PWM初始化和其他功能函数,会存放在PWM.c和PWM.h两个文件中,后面会开源自取):
OPEN_CLOSE(&ch1,PWM_TIM1); //通道1的加热开关开启
OPEN_CLOSE(&ch2,PWM_TIM2); //通道2的加热开关开启
OPEN_CLOSE(&ch3,PWM_TIM3); //通道3的加热开关开启
3、现在加热功能也开启了,但是哪个加热设备正在加热哪个加热设备到达或超过目标温度没有在加热,为了能更直观的看出来,我又写了一个显示函数来观察加热垫的开关状态:
//----------------------------------打印当前通道的开关状态-----------------------------------
//这个函数很简单,就是通过TIM_GetCapturex函数来判断当前通道的PWM是否有占空比,如果没有就显示“关”字,反之显示“开”字
void OLED_Show_OPENCLOSE(void)
{
OLED_ShowString(4,1,"1:");
OLED_ShowString(4,7,"2:");
OLED_ShowString(4,13,"3:");
if(TIM_GetCapture2(TIM3))
{
OLED_ShowChar_zh(4,2,STARTA16x16,0); //STARTA16x16是生成的汉字数组
}
if(TIM_GetCapture3(TIM3))
{
OLED_ShowChar_zh(4,5,STARTA16x16,0);
}
if(TIM_GetCapture4(TIM3))
{
OLED_ShowChar_zh(4,8,STARTA16x16,0);
}
if(!TIM_GetCapture2(TIM3))
{
OLED_ShowChar_zh(4,2,STARTA16x16,2);
}
if(!TIM_GetCapture3(TIM3))
{
OLED_ShowChar_zh(4,5,STARTA16x16,2);
}
if(!TIM_GetCapture4(TIM3))
{
OLED_ShowChar_zh(4,8,STARTA16x16,2);
}
}
可以直接在主函数中循环显示:(函数中的STARTA16x16汉字数组在整个工程的OLED_Font.h文件中,后面开源可自己查看)。
OLED_Show_OPENCLOSE(); //OLED显示加热垫是开启的还是关闭的
2、OLED功能界面
还有一些功能函数,放在这个模块来写是因为:我们需要通过简单的三个按键来设置每个加热垫所对应的目标温度值,来达到我们想要实现的三个加热设备能分别控制的目的(但后期我有考虑到如果三个加热设备同时给一块空间加热,设置相同的目标温度值会不会使整个空间的温度误差变大,所以整体项目还不够严谨,只能在要求不高的环境下使用,大家有什么好方法也可以分享学习)。
所以我利用OLED屏幕、结合了SET按键、UP按键、DOWN按键,加入了两个界面分别是:
1、主界面显示:显示三个加热设备的NTC当前温度、目标温度以及开关状态(主函数就是主界面)。
2、设置界面(有三层):
(1)1号加热设备的目标温度设置,可进行上下设置目标温度数值。
(2)2号加热设备的目标温度设置,可进行上下设置目标温度数值。
(3)3号加热设备的目标温度设置,可进行上下设置目标温度数值。
在这就添加了一个设置界面函数:
//---------------------------------------设置菜单函数----------------------------------
//因为有三个加热设备,所以设置了三层的设置界面,根据SET按键的键值不同来进行显示具体是哪个设备的设置界面
void OLED_MENU(void)
{
OLED_ShowChar(3,11,'C');
while(KeyNum_1)
{
if(KeyNum_1 == 1)
{
OLED_ShowString(2,6,"NTC_1");
OLED_ShowDouble(3,6,Temperature_Threshold_1,1);
}
else if(KeyNum_1 == 2)
{
OLED_ShowString(2,6,"NTC_2");
OLED_ShowDouble(3,6,Temperature_Threshold_2,1);
}
else if(KeyNum_1 == 3)
{
OLED_ShowString(2,6,"NTC_3");
OLED_ShowDouble(3,6,Temperature_Threshold_3,1);
}
Temperature_Threshold(); //设置各个加热垫的目标温度值
Key_GetNum_1(); //切换要设置的加热垫界面
}
OLED_Clear();
}
大家也能看出来,在这个函数之前,我们需要首先获取到当前SET按键的键值是多少才能显示相应的设置界面(获取SET按键的键值):
//-----------------------------------设置菜单页面标志---------------------------------------
void Key_GetNum_1(void)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4) == 0); //等待按键松手
Delay_ms(20); //延时消抖
if(KeyNum_1 == 0)
{
KeyNum_1 = 1; //NTC1号的设置界面
}
else if(KeyNum_1 == 1)
{
KeyNum_1 = 2; //NTC2号的设置界面
}
else if(KeyNum_1 == 2)
{
KeyNum_1 = 3; //NTC3号的设置界面
}
else if(KeyNum_1 == 3)
{
KeyNum_1 = 0; //回到主界面进行显示
}
}
}
现在我们就可以进入到了三个设备的设置界面,接下来就是要设置这个界面中的目标温度值的大小了,我就用了通过UP按键和DOWN按键的简单加减方法进行设置。以下是这两个按键的功能函数:
//-------------------------------设置目标温度-------------------------------------
void Temperature_Threshold(void)
{
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
//长按快加
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == 0)
{
Delay_ms(200);
//根据SET按键键值的不同,程序会让加减按键设置不同设备的目标温度值
if(KeyNum_1 == 1)
{
Temperature_Threshold_1++;
OLED_ShowDouble(3,6,Temperature_Threshold_1,1);
}
else if(KeyNum_1 == 2)
{
Temperature_Threshold_2++;
OLED_ShowDouble(3,6,Temperature_Threshold_2,1);
}
else if(KeyNum_1 == 3)
{
Temperature_Threshold_3++;
OLED_ShowDouble(3,6,Temperature_Threshold_3,1);
}
}
}
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
//长按快减
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) == 0)
{
Delay_ms(200);
if(KeyNum_1 == 1)
{
Temperature_Threshold_1--;
OLED_ShowDouble(3,6,Temperature_Threshold_1,1);
}
else if(KeyNum_1 == 2)
{
Temperature_Threshold_2--;
OLED_ShowDouble(3,6,Temperature_Threshold_2,1);
}
else if(KeyNum_1 == 3)
{
Temperature_Threshold_3--;
OLED_ShowDouble(3,6,Temperature_Threshold_3,1);
}
}
}
}
当然我们也可以通过其他内容来设置可设置的温度范围,超过范围不允许等代码逻辑(这就不加了)。
接下来就可以在主函数循环中添加我们的界面函数了:
if(KeyNum_1 > 0)
{
OLED_Clear(); //屏幕清屏,不清会导致显示重叠
OLED_MENU(); //设置界面函数
}
最后,我们可以再将温度显示的慢一些,以至于能用肉眼不到数字:
Delay_ms(300);
基本就完成了。上面的功能函数很齐全了,下面把整个主函数的内容连接起来:
//-----------------------------------主函数---------------------------------------------
int main(void)
{
OLED_Init(); //OLED初始化
AD_Init(); //AD初始化
PWM_Init(); //加热垫引脚
Key_Init(); //按键初始化
//开机
OLED_ShowChar_zh(2,4,STARTA16x16,0);
OLED_ShowChar_zh(2,5,STARTA16x16,1);
Delay_ms(1000);
OLED_Clear();
while (1)
{
Key_GetNum_1();
OLED_Temperature(&ch1,ADC_Channel_0,1);
OLED_Temperature(&ch2,ADC_Channel_1,2);
OLED_Temperature(&ch3,ADC_Channel_2,3);
OPEN_CLOSE(&ch1,PWM_TIM1); //通道1的加热开关开启
OPEN_CLOSE(&ch2,PWM_TIM2);
OPEN_CLOSE(&ch3,PWM_TIM3);
OLED_Show_OPENCLOSE(); //OLED显示加热垫是开启的还是关闭的
if(KeyNum_1 > 0)
{
OLED_Clear();
OLED_MENU(); //设置界面
}
Delay_ms(300);
}
}
以上就是整个“温度的监测与控制”的所有程序,中间或许有些逻辑和想法不够严谨,自己也发现了一些问题,但简单实现功能,拿出来练习练习代码和电路还是可以的,有大神能够指出问题或添加更加完善的思维想法和逻辑你就再好不过。后续会找时间在把PCB板的电路图完善一下,让路过的大神只指点一下。
附上工程代码供大家修改使用:
通过网盘分享的文件:测温控温加热垫
链接: https://pan.baidu.com/s/1VGAZZNw4IPr_OVfWIaQf-A?pwd=ya7u 提取码: ya7u