自己设计并制作了一个自己主动温度控制系统
一、课题任务
设计并制作一个水温自己主动控制系统,控制对象为1升净水,容器为搪瓷器皿。水温能够在一定范围内由人工设定,并能在环境温度减少时实现自己主动控制。以保持设定的温度基本不变。
1.基本要求
(1)温度设定范围为40~90℃,最小设定分度为1℃。
(2)具有温度显示功能,分辨率为0.1℃,显示的绝对误差小于1℃。
(3)当温度达到某一设定值并稳定后,水温的波动控制在±2℃以内。要求温度调控达到稳定状态时。必须给出声或光提示信号。
(4)环境温度减少时(比如用电风扇降温)水温的波动控制在±2℃以内。
2.发挥部分
(1)当温度达到某一设定值并稳定后,水温的波动控制在±1℃以内。
要求温度调控达到稳定状态时,必须给出声或光提示信号。
(2)採用适当的控制方法。当设定温度突变(由35℃提高到45℃)时,在超调量不超过2℃的前提下,尽量减小系统的调节时间,并要求温度控制的静态误差≤0.5℃。
(3)在设定温度发生突变(由35℃提高到45℃)时,自己主动打印水温随时间变化的曲线。
(4)其它。
二、 方案比較与选择
依据要求。系统至少有下面功能模块构成:
1. 温度測量模块:
温度測量是本系统的核心问题之中的一个。基于单片机的温度測量。温度传感器毫无疑问是最佳的选择。常见的有数字式温度传感器和模拟温度传感器。
前者以DS18B20为代表。其採用单总线的接口方式与微处理器连接。仅须要一条总线就可以实现微处理器的双向通讯。
单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度測量,使用方便等长处。使用户可轻松地组建传感器网络。測量温度范围宽,測量精度高 DS18B20 的測量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C 范围内,精度为 ± 0.5°C 。
持多点组网功能 多个 DS18B20 能够并联在惟一的单线上,实现多点測温。
供电方式灵活 ,DS18B20 能够通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,能够不接外部电源,从而使系统结构更趋简单,可靠性更高。
測量參数可配置 DS18B20 的測量分辨率可通过程序设定 9~12 位。
模拟温度传感器。以Pt100为代表。
DS18B20 具有体积更小、适用电压更宽、更经济、可选更小的封装方式,更宽的电压适用范围。适合于构建自己的经济的測温系统。因此也就被设计者们所青睐。
2. 温度显示模块:
因为温度精度以及实时性的要求,须要多位动态显示。常见的显示方式有两种,一种是数码管。还有一种是液晶屏幕。动态显示是一种最常见的数码管多位显示方法,应用很广泛。
全部数码管段选都连接在一起的时候,动态显示是多个数码管,交替显示,利用人的视觉暂停作用使人看到多个数码管同一时候显示的效果。就像我们看的电影是有一帧一帧的画面显示的。当速度够快的时候我们看到它就是动态的。当我们显示数码管的速度够快的时候。也就能够看到它们是同一时候显示了。
1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号的点阵型液晶模块。
它是由若干个5x7或者5x11的点阵字符位组成,每一个点阵字符位都能够用显示一个字符。每位之间有一个点距的间隔。每行之间也有间隔,起到了字符间距和行间距的作用。
3. 温度设定模块:
轻触开关是一种电子开关。使用时,轻轻按开关button就可使开关接通。当松开手时,开关断开。使用3个开关,分别实现。功能转换。设定+,设定-。就可以完毕全部设定操作。
4. 温度控制模块:
控制核心选择STC89C52。STC89C52是STC公司生产的一种低功耗、高性能CMOS8位微控制器。具有 8K 在系统可编程Flash存储器。STC89C52使用经典的MCS-51内核。指令代码全然兼容传统8051。还做了非常多的改进使得芯片具有传统51单片机不具备的功能。在单芯片上,拥有机灵的8 位CPU 和在系统可编程Flash。使得STC89C52为众多嵌入式控制应用系统提供高灵活、超有效的解决方式。 具有下面标准功能: 8k字节Flash。512字节RAM, 32 位I/O 口线,看门狗定时器,内置4KB EEPROM,MAX810复位电路。3个16 位定时器/计数器,4个外部中断,一个7向量4级中断结构(兼容传统51的5向量2级中断结构),全双工串行口。另外 STC89X52 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空暇模式下。CPU 停止工作。同意RAM、定时器/计数器、串口、中断继续工作。
掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止。直到下一个中断或硬件复位为止。
最高运作频率35MHz,6T/12T可选。
可以满足本系统的需求。
外电路的控制。通过继电器来完毕。选用HK4100F型号,额定电压5V的电磁式继电器。其有例如以下特点。
降温模块,因为本系统打算将加热电路与继电器的常开触点,即普通情况下加热装置继电器以及继电器均断开。因此理论上当温度过高时,继电器断开,全然能够採用自然降温使得温度达到正常。为了使效果更好,可添加风扇或者空调装置进行降温。
5. 声/光报警模块:
选用绿色和红色LED分别作为外温度在设置范围内和之外的信号标志,对于后者能够加入蜂鸣器或者电铃作为报警装置,因为可能会扰民,因此加入开关。可手动控制其是否警报。
三、 电路设计
本系统先在PROTUES上进行电路初步设计以及仿真实验。依据以上分析,设计电路例如以下。
主要有下面几个部分
1. 单片机最小系统
2. 温度显示
3. 加热和继电器电路
4. 温度获取和设置
DS16B20 功能按键
5. 报警和降温
四、 程序设计
五、 測试方案
通过KEIL编译通过,然后在PROTUES进行仿真,測试系统执行效果。为了提高系统稳定性和可靠性。选择在开发板上进行实际測试以及PC上的程序调试。最后硬件平台搭建,通过选择合适规格的零件,焊接从而完毕整个系统的预定功能。
六、
系统调试
通过优化单片机与LCD1602的接口选择,攻克了之前。因为没有加上拉电阻导致无法显示的问题。
调整了函数结构,使得温度实时更新。提高了系统的灵敏度和实时性。仿真效果,如上图所看到的。
七、 数据測试与处理
为了进一步与实际接近,在普中EM3_V3.0型号开发板上进行了实验。成功实现下面功能:
(1)温度设定范围为0~125℃,最小设定分度为1℃。比要求的40~90℃更宽。
(2)具有温度显示功能。分辨率为0.01℃。显示的绝对误差小于1℃。比要求的0.1℃提升一个数量级。绝对误差小于要求的1℃。
(3)当温度达到某一设定值并稳定后,水温的波动控制在±X℃(X为0-9之间的整数,可调)以内。
比要求的±2℃更灵活且更具有有用性。
(4)要求温度调控达到稳定状态时,给出光提示信号。
(5)环境温度不在设定范围内时,,反应及时且高速。温度过高,触发器断开。停止加热。自然降温。而且能够调用风扇进行降温。
温度过低,触发器闭合加热,直到其达到指定温度。
没有实现下面功能:
(6)当设定温度突变(由35℃提高到45℃)时,在超调量不超过2℃的前提下,尽量减小系统的调节时间,并要求温度控制的静态误差≤0.5℃。
(7)在设定温度发生突变(由35℃提高到45℃)时,自己主动打印水温随时间变化的曲线。
八、 总结
本水温自己主动控制系统,较好地实现了人工设定温度,实时显示温度,自己主动控制温度的功能,而且结合实际应用。对一些地方进行了改进,比如提高了温度显示的准确度,另外实现了对“最高温度”、“最低温度”。“误差范围”的调节。使測定的范围和波动范围更加灵活,更具有实际运用价值。
但因为时间和能力有限。还有发挥部分一部分功能没有实现。软件仿真上,在PROTUES进行的仿真,效果稳定。硬件上,实现了基于普中EM3_V3开发板的执行和调试,效果稳定,此外还单独搭建了实验装置平台,非常不幸在多次努力尝试之后,依旧存在一些显示问题。没能比开发板上更好的执行。
九、 參考文献
[1]HK4100F继电器资料
[2]普中EM3_V3开发板资料
[3]51单片机C语言应用开发技术大全(第二版).人民邮电出版社.
十、 附录:
A、设计电路图(原理图、PCB图)
B、程序(要求有文字、凝视)
因为相关程序较长。受版面限制,故上传至
C、相关作品图片
#include<reg51.h> #include<intrins.h> #define uint unsigned int #define uchar unsigned char /*LCD1602显示模块*/ /*用于PROTUES仿真 #define LCD1602_DATAPINS P2 sbit LCD1602_RS=P3^0; sbit LCD1602_RW=P3^1; sbit LCD1602_E=P3^2; */ #define LCD1602_DATAPINS P0 sbit LCD1602_RS=P2^6; sbit LCD1602_RW=P2^5; sbit LCD1602_E=P2^7; /*按键调节模块*/ sbit k1=P1^1;//功能键 sbit k2=P1^2;//加 sbit k3=P1^3;//减 void keyscan(); void key1_switch(); void key2_add(); void key3_minus(); uchar k_num; void LCD1602_SetTemp(uchar add,uchar dat); uchar high=90,low=10,set_t=30,set_d=2;//这里的数据类型可能有问题!!/*其它模块接口*/ sbit DSPORT=P3^7; //温度传感器单总线端 sbit SPEAKER=P1^5;//蜂鸣器 sbit RELAY=P1^4;//继电器 sbit COOLER=P1^7;//降温装置 /*LCD1602相关函数*/ void Delay1us(uint a); void LCD1602_WriteCom(uchar com);//LCD1602写入8位命令子函数 void LCD1602_WriteData(uchar dat); void LCD1602_Init();//LCD1602初始化子程序 void LCD1602_ShowTemp(int temp); /*DS18B20温度传感器相关函数*/ void Delay15us(uchar aa); uchar Ds18b20Init(); void Ds18b20WriteByte(uchar dat); uchar Ds18b20ReadByte(); void Ds18b20SwitchTemp(); int Ds18b20ReadTemp(); float temp_max=125.00,temp_min=-55.00; uchar ii=0,jj=0,kk=0; uchar words_h[2]={"H="}; uchar words_l[2]={"L="}; uchar words_s[4]={"Set="}; void Sound(); void Temp_control(int temp); uchar count=0;//用来解决主程序运行太快,按键光标来不及显示的问题 void Temp_control(int temp) { unsigned int kk=400; uchar set_h,set_l; set_h=set_t+set_d; set_l=set_t-set_d; if (temp<=set_h&&temp>=set_l) //设定温度范围之内 { RELAY = 0; //继电器断开 COOLER=0;//风扇关闭 } else { TR0=0; while (kk--) { Sound(); } RELAY = 1; //继电器打开,外电路(常开触电)闭合加热 if (temp>=set_h)//温度过高 { RELAY = 0; //继电器断开 COOLER=1;//打开 } TR0=1; } } /*其它模块相关函数*/ void Sound()//蜂鸣器 { SPEAKER=1; Delay15us(40); SPEAKER=0; Delay15us(40); } void Delay1us(uint a) { uint b,c; for(c=a;c>0;c--) for(b=110;b>0;b--); } void LCD1602_WriteCom(uchar com) //写入命令 { LCD1602_E=0; LCD1602_RS=0;//0就写指令 LCD1602_RW=0; //0就是写 LCD1602_DATAPINS=com; Delay1us(10); LCD1602_E=1; Delay1us(10); LCD1602_E=0; } void LCD1602_WriteData(uchar dat) //写入数据 { LCD1602_E=0; LCD1602_RS=1; LCD1602_RW=0; LCD1602_DATAPINS=dat; Delay1us(10); LCD1602_E=1; Delay1us(10); LCD1602_E=0; } void LCD1602_Init() //LCD初始化子程序 { // uint num; LCD1602_WriteCom(0x38); //开显示 LCD1602_WriteCom(0x0f); //开显示不显示光标 LCD1602_WriteCom(0x06); //写一个指针加1 LCD1602_WriteCom(0x01); //清屏 LCD1602_WriteCom(0x80); //设置数据指针起点 LCD1602_WriteCom(0x80+0x0B); for (ii=0;ii<2;ii++) { LCD1602_WriteData(words_h[ii]); } LCD1602_SetTemp(13,high); LCD1602_WriteCom(0x80+0x40); for (kk=0;kk<4;kk++) { LCD1602_WriteData(words_s[kk]); } LCD1602_SetTemp(4+0x40,set_t); LCD1602_WriteCom(0x80+0x47); LCD1602_WriteData(0x23); //显示正负号 LCD1602_WriteCom(0x80+0x48); LCD1602_WriteData(0x30+set_d); LCD1602_WriteCom(0x80+0x4B); for (jj=0;jj<2;jj++) { LCD1602_WriteData(words_l[jj]); } LCD1602_SetTemp(13+0x40,low); TMOD=0X01; TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1;//总中断 ET0=1; TR0=1; } void Delay15us(uchar aa) { do{ _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); aa--; }while(aa); } /*DS18B20温度传感器相关函数*/ uchar Ds18b20Init() { uchar flag; DSPORT=0; //总线拉低 Delay15us(40);//延时480~960us DSPORT=1; Delay15us(2); flag=DSPORT; Delay15us(6);//Ds18b20发出应答信号。延时60~240us Delay15us(25); return flag; } uchar Ds18b20ReadByte() { uchar byte,bi; uint i; for (i=0;i<8;i++) { DSPORT=0; _nop_();_nop_();_nop_();_nop_(); //保持低电平至少1us,但不能低于15us DSPORT=1; bi= DSPORT;//读取一位数据,循环8次为一个字节 byte = (byte >> 1) | (bi << 7);//将byte左移一位,然后与上右移7位后的bi,8次合成一个字节 Delay15us(4); } return byte; } void Ds18b20WriteByte(uchar dat) { uint i; for (i=0;i<8;i++) { DSPORT=0;_nop_(); DSPORT=dat&0x01;//一位一位得读,从最低位開始 Delay15us(5); DSPORT=1;_nop_(); dat>>=1; } } void Ds18b20SwitchTemp() { Ds18b20Init(); Delay15us(7); Ds18b20WriteByte(0xcc);//跳过ROM指令 Ds18b20WriteByte(0x44);//启动温度转换指令 } int Ds18b20ReadTemp() { int temp=0; uchar temp_h,temp_l; Ds18b20SwitchTemp(); Ds18b20Init(); Delay1us(1); Ds18b20WriteByte(0xcc); //跳过ROM操作命令 Ds18b20WriteByte(0xbe); //发送读取温度命令 temp_l = Ds18b20ReadByte(); //读取温度值共16位。先读低字节 temp_h = Ds18b20ReadByte(); temp= temp_h; temp<<=8; temp|= temp_l; return temp; } void LCD1602_ShowTemp(int temp) { float tt; uchar show[5]={0,0,0,0,0}; if (temp<0)//温度为负 { LCD1602_WriteCom(0x80); //写地址 80表示初始地址 LCD1602_WriteData('-'); //显示负 temp=temp-1;temp=~temp;tt=temp; temp=tt*0.0625*100; } else//温度为正 { LCD1602_WriteCom(0x80); //写地址 80表示初始地址 LCD1602_WriteData('+'); //显示正 tt=temp; temp=tt*0.0625*100; } show[0]=temp/10000; show[1]=temp%10000/1000; show[2]=temp%1000/100; show[3]=temp%100/10; show[4]=temp%10; LCD1602_WriteCom(0x82); LCD1602_WriteData('0'+show[0]); //百位 LCD1602_WriteCom(0x83); LCD1602_WriteData('0'+show[1]); //十位 LCD1602_WriteCom(0x84); LCD1602_WriteData('0'+show[2]); //个位 LCD1602_WriteCom(0x85); LCD1602_WriteData('.'); LCD1602_WriteCom(0x86); LCD1602_WriteData('0'+show[3]); //十分位 LCD1602_WriteCom(0x87); LCD1602_WriteData('0'+show[4]); //百分位 LCD1602_WriteCom(0x88); LCD1602_WriteData(0xdf); LCD1602_WriteCom(0x89); LCD1602_WriteData('C'); LCD1602_WriteCom(0x0C); temp=temp/100; Temp_control(temp); } void key1_switch()//功能切换 { uchar set_h,set_l; set_h=set_t+set_d; set_l=set_t-set_d; //k1键指令 if(k1==0) { Delay1us(10);//延时消抖 if(k1==0) { k_num++; while(!k1); //按键没有松开 if(k_num==1) { TR0=0; //关掉计时器,使得光标得以显示 LCD1602_WriteCom(0x80+0x46); LCD1602_WriteCom(0x0f); Delay15us(6); } if(k_num==2) { LCD1602_WriteCom(0x80+0x48); LCD1602_WriteCom(0x0f); Delay15us(6); } if(k_num==3) { LCD1602_WriteCom(0x80+0x0F); LCD1602_WriteCom(0x0f); Delay15us(6); } if(k_num==4) { LCD1602_WriteCom(0x80+0x4F); LCD1602_WriteCom(0x0f); Delay15us(6); } if(k_num==5) { k_num=0; LCD1602_WriteCom(0x0C);//光标不显示 if (high<low) //用来default { high=90; low=40;//恢复默认值 } if(set_h>high||set_l<low) { high=90; low=10;//恢复默认值 set_t=30; set_d=2;//恢复默认值 } TR0=1;//计时器又一次打开。使得屏幕得以刷新 } } } } void key2_add()//功能加 { if(k_num!=0) { if(k2==0) { Delay1us(5); if(k2==0) { while(!k2); if(k_num==1) { set_t++; if (set_t>=high) set_t=high; LCD1602_SetTemp(4+0x40,set_t); LCD1602_WriteCom(0x80+0x46); } if(k_num==2) { set_d++; if (set_d>9) set_d=0; LCD1602_WriteCom(0x80+0x48); LCD1602_WriteData(0x30+set_d); LCD1602_WriteCom(0x80+0x48); } if(k_num==3) { high++; if (high>=temp_max) high=0; LCD1602_SetTemp(13,high); LCD1602_WriteCom(0x80+0x0F); // LCD1602_WriteCom(0x80+0x40+8); } if(k_num==4) { low++; if (low>=temp_max) low=0; LCD1602_SetTemp(13+0x40,low); LCD1602_WriteCom(0x80+0x4F); // LCD1602_WriteCom(0x80+0x40+13); } } } } } void key3_minus()//功能减 { if(k_num!=0) { if(k3==0) { Delay1us(5); if(k3==0) { while(!k3); if(k_num==1) { if (set_t<=0) set_t=high+1; set_t--; LCD1602_SetTemp(4+0x40,set_t); LCD1602_WriteCom(0x80+0x46); } if(k_num==2) { if (set_d<=0) set_d=10; set_d--; LCD1602_WriteCom(0x80+0x48); LCD1602_WriteData(0x30+set_d); LCD1602_WriteCom(0x80+0x48); } if(k_num==3) { if (high<0) high=temp_max; high--; LCD1602_SetTemp(13,high); LCD1602_WriteCom(0x80+0x0F); // LCD1602_WriteCom(0x80+0x40+8); } if(k_num==4) { if (low<=0) low=high; low--; LCD1602_SetTemp(13+0x40,low); LCD1602_WriteCom(0x80+0x4F); // LCD1602_WriteCom(0x80+0x40+16); } } } } } void keyscan()//按键检測 { key1_switch(); key2_add(); key3_minus(); } void LCD1602_SetTemp(uchar add,uchar dat)//数码管制定位置显示 { uchar head,body,end; head=dat/100; body=dat%100/10; end=dat%10; LCD1602_WriteCom(0x80+add); LCD1602_WriteData(0x30+head); LCD1602_WriteData(0x30+body); LCD1602_WriteData(0x30+end); } void main() { LCD1602_Init(); while(1) { keyscan(); } } void timer0() interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if (count==10)//让温度每0.5s刷新一次 { count=0; LCD1602_ShowTemp(Ds18b20ReadTemp()); } }