成绩:重庆邮电大学自动化学院综合实验报告题目:51系列单片机闭环温度控制实验学生姓名:***班级:0811103学号:**********同组人员:黄酉指导教师:***完成时间:2014年12月一、实验要求及分工情况51系列单片机闭环温度控制实验——基于Protuse仿真实验平台实现要求:1. 设计硬件电路:温度检测:采用热电偶或热电阻温度给定:采用电位器进行模拟电压给定,0——5VAD转采用12位转换显示采用8位LED,或者LCD1602显示键盘4X4,PID等参数通过键盘设置。
2. 软件控制算法:数字PID,参数在线修改。
显示窗口:显示温度的设置值SV、温度的实际值PV。
实际温度值,温度峰值、峰值时间等通过串口上传到上位机(选做)具体分工:黄酉负责系统设计思想导向与编程;霍栋博负责辅助编程与编写报告与Proteus仿真。
二、实验内容(实验原理介绍)1、电路的总体工作原理温度控制系统采用AT89S51八位机作为微处理单元进行控制。
采用4X4键盘把设定温度的最高值和最低值存入单片机的数据存储器,还可以通过键盘完成温度检测功能的转换。
温度传感器把采集的信号与单片机里的数据相比较来控制温度控制器。
机AT89S51为测控系统的核心来完成数据采集、处理、显示、报警等功能。
选用数字温度传感器DS18B20,省却了采样/保持电路、运放、数/模转换电路以及进行长距离传输时的串/并转换电路,简化了电路,缩短了系统的工作时间,降低了系统的硬件成本。
该系统的总体设计思路如下:温度传感器DS18B20把所测得的温度发送到AT89S51单片机上,经过51单片机处理,将把温度在显示电路上显示,本系统显示器为点阵字符LCD,1602液晶模块。
检测范围5摄氏度到60摄氏度。
本系统除了显示温度以外还可以设置一个温度值,对所测温度进行监控,当温度高于或低于设定温度时,开始报警并启动相应程序(温度高于设定温度时,风扇开;当温度低于设定温度时,加热器开)。
2、PID算法的数字实现本次试验通过8031通过OVEN 是模拟加热的装置,加一定的电压便开始不停的升温,直到电压要消失则开始降温。
仿真时,U形加热器为红色时表示正在加热,发红时将直流电压放过来接,就会制冷,变绿。
T端输出的是电压,温度越高,电压就越高。
8031对温度的控制是通过可控硅调控实现的。
可控硅通过时间可以通过可控硅控制板上控制脉冲控制。
该触发脉冲想8031用软件在P1.3引脚上产生,受过零同步脉冲后经光偶管和驱动器输送到可控硅的控制级上。
偏差控制原理是要求对所需温度求出偏差值,然后对偏差值处理而获得控制信号去调节加热装置的温度。
PID控制方程式:式中e是指测量值与给定值之间的偏差TD 微分时间T 积分时间KP 调节器的放大系数将上式离散化得到数字PID位置式算法,式中在位置算法的基础之上得到数字PID增量式算法:3、系统的硬件设计3.1 单片机最小系统的设计目前的单片机开发系统只能够仿真单片机,却没有给用户提供一个通用的最小系统。
由设计的要求,只要做很小集成度的最小系统应用在一些小的控制单元。
其应用特点是:(1)全部I/O口线均可供用户使用。
(2)内部存储器容量有限(只有4KB地址空间)。
(3)应用系统开发具有特殊性图 3.1 最小系统图单片机最小系统如图3.1所示,其中有4个双向的8位并行I/O端口,分别记作P0、P1、P2、P3,都可以用于数据的输出和输入,P3口具有第二功能为系统提供一些控制信号。
3.2键盘电路的设计用AT89C51的并行口P1接4×4矩阵键盘,以P1.0-P1.3作输入线,以P1.4-P1.7作输出线;液晶显示器上显示每个按键的“0-F”序号。
对应的按键的序号排列如图3.5图3.4中微处理单元是AT89C51单片机,X1和X2接12M的两脚晶振,接两个30PF的起振电容,J1是上拉电阻.单片机的P1口8位引脚与行列式键盘输出脚相连,控制和检测行列式键盘的输入.行线通过上拉电阻接到+5V上,无按键按下时,行线处于高电平状态,有键按下时,行线的电平状态将由与此行线相连接的列线的电平决定.键盘输入的信息主要进程是:1 CPU判断是否有键按下.2 确定是按下的是哪个键.3 把此键所代表的信息翻译成计算机可以识别的代码或者其他的特征符号.图 3.4 键盘硬件电路图3.3显示电路的设计液晶显示器是一种将液晶显示器件,连接器件,集成电路,PCB线路板,背光源,结构器件装配在一起的组件。
根据显示内容和方式的不同可以分为,数显LCD,点阵字符LCD,点阵图形LCD在此设计中我们采用点阵字符LCD,这里采用常用的2行16个字的1602液晶模块。
1602采用标准的14脚接口,其中:第1脚:VSS为地电源第2脚:VDD接5V正电源第3脚:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:RW为读写信号线,高电平时进行读操作,低电平时进行写操作。
当RS和RW共同为低电平时可以写入指令或者显示地址,当RS为低电平RW为高电平时可以读忙信号,当RS为高电平RW为低电平时可以写入数据。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15~16脚:空脚。
与单片机的连接如图3.7所示。
图 3.5 液晶显示电路图:3、电路原理图仿真结果:附录#include"reg51.h"#define uchar unsigned char#define uint unsigned int#define ulong unsigned long/*****************引脚定义*********************************/ sbit output=P2^5;/********************************/ sbit CLOCK=P3^1;sbit D_IN=P2^7;sbit D_OUT=P2^6;sbit _CS=P3^0;/********************************/ sbit lcdrs=P2^0;sbit lcdrw=P2^1;sbit lcden=P2^2;/********************************/ sbit in1=P2^3;sbit in2=P2^4;/********************************/ sbit led=P3^2;sbit speaker=P3^3;/*****************引脚定义*********************************//*****************变量定义(申明)*********************************/int freq=500,pwm=0;uchar setflag=0,pageflag=0,ok=0;float pwm_temp=0;float KP=100,KI=10,KD=0;uint pul_count=0;float ek=0,ek1=0,ek2=0;uchar code str[16]=" welcome! "; uchar code str1[16]=" temperature PID"; uchar code str2[16]="set-T pra-T " ; uchar code str3[16]="P: I: D:" ; uchar code str4[16]="set-P: " ; uchar code str5[16]="set-I: " ;uchar code str6[16]="set-D: " ;void Init(void); uint adcread(uchar port);void delay(uint n);void writecom(uchar com);void writedata(uchar date);void initlcd();float keyscan(void);uchar keyscans(void);void lcdnumdisplay(uchar pos,double f); void lcdnumdisplays(uchar pos,double f); uint read_pul();void mypid(float Kp,float Ki,float Kd,uint count,uint point);/*****************变量定义(申明)*********************************/void main(){uchar i;float scantemp;float adnum0=0;float adnum1=0;bit init0,init1,init2,init3,init4;Init();initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str[i]);//huanyinwritecom(0x80+0x40);for(i=0;i<16;i++)writedata(str1[i]);//shumingdelay(200);while(1){pwm_temp=500*(float)(adnum0)/4095;//pwm=pwm_temp;mypid(KP,KI,KD,adnum1,pwm_temp);scantemp=keyscan();if(adnum1>=pwm_temp-1.5&&adnum1<=pw m_temp+1.5){led=0;speaker=1;}else{led=1;speaker=0;}if(setflag==0){adnum0=adcread(0);adnum1=adcread(1)/10;if(init0==0){initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str2[i]);//shuminginit0=1;init1=0;init2=0;init3=0;init4=0;}lcdnumdisplays(0x80+0x40,(float)pwm_temp);//pwm_templcdnumdisplays(0x80+0x4a,(float)adnum1);}if(setflag!=0&&pageflag==0){if(init1==0){initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str3[i]);//PIDinit0=0;init1=1;init2=0;init3=0;init4=0;}lcdnumdisplays(0x80+0x40,KP);lcdnumdisplays(0x80+0x46,KI);lcdnumdisplays(0x80+0x4D,KD);}if(setflag!=0&&pageflag==1){if(init2==0){initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str4[i]);//Pinit0=0;init1=0;init2=1;init3=0;init4=0;}lcdnumdisplays(0x80+0x40,scantemp);if(ok==1)KP=scantemp;}if(setflag!=0&&pageflag==2){if(init3==0){initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str5[i]);//iinit0=0;init1=0;init2=0;init3=1;init4=0;}lcdnumdisplays(0x80+0x40,scantemp);if(ok==1)KI=scantemp;}if(setflag!=0&&pageflag==3){if(init4==0){initlcd();writecom(0x80);for(i=0;i<16;i++)writedata(str6[i]);//dinit0=0;init1=0;init2=0;init3=0;init4=1;}lcdnumdisplays(0x80+0x40,scantemp);if(ok==1)KD=scantemp;}}}void mypid(float Kp,float Ki,float Kd,uint count,uint point){static float Uk;ek=point-count;// if(ek>=5&&ek<=-5) //积分分离{Uk=Kp*(ek-ek1)+Ki*ek+Kd*(ek-2*ek1+ek2 );} //增量PID// else// Uk=Kp*ek;pwm=pwm+Uk;if(pwm>freq)pwm=freq;if(pwm<=0){pwm=0;in1=0;in2=1;}if(pwm>0) {in1=1;in2=0;}ek2=ek1;ek1=ek;}uint read_pul(){uint t1,th1,th2;uint val;while(1){th1=TH1;t1=TL1;th2=TH1;if(th1==th2)break;}val=th1*256+t1;return val;} void delay(uint n){uint i,j;for(i=n;i>0;i--)for(j=1;j>0;j--);}void Init(void)//初始化函数{TMOD=0x51;TH0=(65536-10)/256;TL0=(65536-10)%256;EA=1;ET0=1;TR0=1;//T1计数TH1=0;TL1=0;TR1=1;}void Timer_0(void) interrupt 1//中断函数{static ulong t_count=0;static uint num_count=0;TR0=0;TH0=(65536-10)/256;TL0=(65536-10)%256;TR0=1;num_count++;// t_count++;// if(t_count==2320)// {// t_count=0;// TR1=0;// pul_count=read_pul();// TH1=0;// TL1=0;// TR1=1;// }if(num_count>freq)num_count=0; //1khz;if(num_count<pwm)output=1;else output=0;}void writecom(uchar com) {lcdrs=0;P0=com;delay(1);lcden=1;delay(1);lcden=0;}void writedata(uchar date) {lcdrs=1;P0=date;delay(1);lcden=1;delay(1);lcden=0;}void initlcd(){lcdrw=0;writecom(0x38);delay(1);writecom(0x0c);delay(1);writecom(0x06);delay(1);writecom(0x01);delay(5); }uint adcread(uchar port){uint ad=0,i;CLOCK=0;_CS=0;port<<=4;for(i=0;i<12;i++){if(D_OUT) ad|=0x01;D_IN=(bit)(port&0x80); CLOCK=1;delay(1);CLOCK=0;delay(1);port<<=1;ad<<=1;}_CS=1;ad>>=1;return(ad);}void lcdnumdisplays(uchar pos,float f)//(0.001-99999) 精度低但方便数据更新{uchar i;writecom(pos);if(f>65535&&f<0.001)for(i=0;i<5;i++)writedata(0x23);//超出范围显示#elseif(f==0){writedata(0x30);for(i=0;i<4;i++)writ edata(0x20);}else{if((uint)f/10000!=0){writedata((uint)f/10000+0x30);writedata((uint)f%10000/1000+0x30);writedata((uint)f%1000/100+0x30);writedata((uint)f%100/10+0x30);writedata((uint)f%10+0x30);}else{if((uint)f/1000!=0){writedata(0+0x30);writedata((uint)f/1000+0x30);writedata((uint)f%1000/100+0x30);writedata((uint)f%100/10+0x30);writedata((uint)f%10+0x30);}else{if((uint)f/100!=0){writedata((uint)f/100+0x30);writedata((uint)f%100/10+0x30);writedata((uint)f%10+0x30);writedata(0x2e);writedata((uint)(f*10)%10+0x30);}else{if((uint)f/10!=0){writedata((uint)f/10+0x30);writedata((uint)f%10+0x30);writedata(0x2e);writedata((uint)(f*10)%10+0x30);writedata((uint)(f*100)%10+0x30);}else{writedata((uint)f%10+0x30);writedata(0x2e);writedata((uint)(f*10)%10+0x30);writedata((uint)(f*100)%10+0x30);writedata((uint)(f*1000)%10+0x30);}}}}}}void lcdnumdisplay(uchar pos,float f0) //(0.00001-99999.99999){uchar temp;ulong f;writecom(pos);f=(ulong)f0;temp=f/10000; //整数部分if(temp!=0){writedata(temp+0x30);writedata(f%10000/1000+0x30);writedata(f%1000/100+0x30);writedata(f%100/10+0x30);writedata(f%10+0x30);}else{temp=f%10000/1000;if(temp!=0){writedata(temp+0x30);writedata(f%1000/100+0x30);writedata(f%100/10+0x30);writedata(f%10+0x30);}else{temp=f%1000/100;if(temp!=0){writedata(temp+0x30);writedata(f%100/10+0x30);writedata(f%10+0x30);}else{temp=f%100/10;if(temp!=0){writedata(temp+0x30);writedata(f%10+0x30);}else{temp=f%10;if(temp!=0){writedata(temp+0x30);}elsewritedata(0+0x30);}}}}if((ulong)(f0*10)%10!=0||(ulong)(f0*100)%1 0!=0||(ulong)(f0*1000)%10!=0||(ulong)(f0*10 000)%10!=0) //小数部分{writedata(0x2e);temp=(ulong)(f0*10000)%10;if(temp!=0){writedata((ulong)(f0*10)%10+0x30); writedata((ulong)(f0*100)%10+0x30);writedata((ulong)(f0*1000)%10+0x30);writedata(temp+0x30);}else{temp=(ulong)(f0*1000)%10;if(temp!=0){writedata((ulong)(f0*10)%10+0x30);writedata((ulong)(f0*100)%10+0x30);writedata(temp+0x30);}else{temp=(ulong)(f0*100)%10;if(temp!=0){writedata((ulong)(f0*10)%10+0x30);writedata(temp+0x30);}else{temp=(ulong)(f0*10)%10;if(temp!=0)writedata(temp+0x30);}}}}}uchar keyscans(void)//矩阵键盘扫描{uchar i, j, temp, Buffer[4] = {0xef, 0xdf, 0xbf, 0x7f};for(j = 0; j < 4; j++) //循环四次{P1 = Buffer[j]; //在P1高四位分别输出一个低电平temp = 0x01; //计划先判断P1.0位for(i = 0; i < 4; i++){if(!(P1 & temp)) //从P1低四位,截取1位return (i + j * 4); //返回取得的按键值temp <<= 1; //判断的位,左移一位}}return 16; //判断结束,没有键按下,返回}float keyscan(void)//矩阵键盘扫描{static float keytemp=0;static uchar dianflag=0;static uchar diancount=0;static uchar k=0;uchar temp;P1=0xfe;temp=P1;temp=temp&0xf0;if(temp!=0xf0){delay(1);if(temp!=0xf0){switch(temp){case 0xe0: {k=1;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xd0: {k=2;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xb0: {k=3;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0x70: if(setflag==1){pageflag++;dianflag=0;diancount=0;ok=0;ke ytemp=0;if(pageflag==4)pageflag=0;}break;}}}P1=0xfd;temp=P1;temp=temp&0xf0;if(temp!=0xf0){delay(1);if(temp!=0xf0){switch(temp){case 0xe0: {k=4;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ; if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xd0: {k=5;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xb0: {k=6;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0x70: if(setflag==1){pageflag--;dianflag=0;diancount=0;ok=0;key temp=0;if(pageflag==0)pageflag=4;}break;}}}P1=0xfb;temp=P1;temp=temp&0xf0;if(temp!=0xf0){delay(1);if(temp!=0xf0){switch(temp){case 0xe0: {k=7;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001; if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xd0: {k=8;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01; if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0xb0: {k=9;if(dianflag==0)keytemp=keytemp*10+k ;if(dianflag==1){diancount++;if(diancount==1)keytemp=keytemp+k*0.1;if(diancount==2)keytemp=keytemp+k*0.01;if(diancount==3)keytemp=keytemp+k*0.001;if(diancount==4)keytemp=keytemp+k*0.0001 ;if(diancount==5)keytemp=keytemp+k*0.0000 1;}}break;case 0x70: {setflag=!setflag;pageflag=0;ok=0;}break;}}}P1=0xf7;temp=P1;temp=temp&0xf0;if(temp!=0xf0){delay(1);if(temp!=0xf0){switch(temp){case 0xe0:dianflag=1;break;case 0xd0:{k=0;if(dianflag==0)keytemp=keytemp*10+k ;}break;case 0xb0: {dianflag=0;diancount=0;ok=1;keytemp= 0;pageflag++;}break;case 0x70: {dianflag=0;diancount=0;keytemp=0;ok= 0;}break;}}}return keytemp;}。