基于51单片机的水温测控系统实验报告
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
if(duty_tem<0)
duty_tem=0;
if(duty_tem>=100)
duty_tem=100;
duty=floor(duty_tem)/2;//u最大值为50
if(duty_tem<0) pout=1;
else pout=0;
return (pout);
}
///////////////////////
uchar num2[13]={0x4E,0x4F,0x57,0x54,0x3A,0,0,0,0x2E,0,0xDF,0x43};
unsigned int settemp=500,nowtemp;
unsigned char timecount=0;
bit adflag,showflag; //ad标志位
{
while(XBYTE[0xa001]&0x80);//等待可写
XBYTE[0xa002]=(data1);//向数据端口写入数据
}
void writeString(uchar data1[],int len)
{
int k=0;
for(k=0;k<len;k++)//写入字符串
writeData(data1[k]);
摘要
本次实验是软硬件相结合的实验,通过传感器得到的阻值与其它电阻,可以搭建一个电桥,将水温转化为电压,然后通过放大器将电压放大到所需要的值,将所得的电压送入单片机的AD转换电路,将模拟信号转换成数字信号,从而在单片机的液晶屏上显示当前的温度。此烧水壶是可控制的,即设定温度,使水加热到设定温度且保温,此控制算法采用PID控制算法来控制继电器的通断,来保证水温恒定在设定温度处。
}
unsigned int AD() //本次AD读取上一次的值
{
uint temp=0;
uchar i;
AD_CS = 0;
SCLK = 0;
for(i=0;i<10;i++)
{
temp <<=1;
temp |= DIO;
SCLK = 1;
SCLK = 0;
}
AD_CS = 1;
delay(256); //延迟>17us
附:源程序
#include <REG52.H>
#include <absacc.h>
#include <intrins.h>
#include <math.h>
#define uchar unsigned char
#define uint unsigned int
#define KP 0.55 //比例系数
五、
3年大学时光中,觉得最有用的课程就是这些实验了,它让我们学有所用,而不仅仅是各种理论,而是通过实践来巩固深入理解理论。这门实验课让我们不仅在软件上花了大量的功夫,代码力求精益求精;而且.在实际的调试中,还是发现软件只是一方面的内容,外硬件的优劣也是影响系统性能和精度的致命因素.尤其是做控制的时候和调整温度的时候,一个可靠的硬件可以省下不少功夫.温度控制系统的设计让我们学会的不仅仅是对单片机的内部结构的进一步理解与运用,还在上一次实验的基础上进一步提高运放集成电路设计与运用的能力。
}
//LCD初始化
void LCD_inti()
{
BUSY();
XBYTE[0xa000]=0x01;
BUSY();
XBYTE[0xa000]=0x06;
BUSY();
XBYTE[0xa000]=0x1c;
BUSY();
XBYTE[0xa000]=0x38;
BUSY();
XBYTE[0xa000]=0x0c;
#define KI 0.02 //积分系数
#define KD 0.1 //微分系数
sbit pout=P1^7;
sbit SCLK=P1^2;
sbit DIO=P1^3;
sbit AD_CS=P1^4;
uchar num1[13]={0x53,0x45,0x54,0x54,0x3A,0,0,0,0x2E,0,0xDF,0x43};
}
void BUSY()
{
uchar BUSYBF=0;
while(1)
{
BUSYBF=XBYTE[0xa001];
if(BUSYBF <0x80) break;
}
return;
}
//AD初始化
void AD_init()
{
AD_CS=1; //必须为高
SCLK=1; //上升沿有效
DIO=1;//置高
TMOD设置
1.采用T1定时/计数器,工作在方式1下,见下表:
GATE
C/T
M1
M0
GATE
C/T
M1
M0
0
0
0
1
0
0
0
0
所以 TMOD=10H
2.开中断:EA=1;
定时器T1开:ET1=1
3.TH1、TL1设置
定时时间设为50ms,所以
50×10-3=(216-初始值)×(12/11.0592)×10-6
unsigned char duty=0,duty_tem=0; //输出控制量
float el,en=0,es=0;
float p_out,i_out,d_out;
void write();
void BUSY();
void LCD_inti();
void delay(uchar n)
{
int ii,jj;
解得: 初始值=19456=01001100 00000000B
因此:TH1=01001100B=4CH TL1=00000000B=00H
程序中需用到的系统地址
A15
A14
A13
A12
A11
A10
A9
A8
A7
A6
A5
A4
A3
A2
A1
A0
1
0
1
0
0
0
0
0
0
0
0
0
0
0
RS
R/W
A1A0组合分别是00:写指令;01:写数据;10:读指令;11:读数据
es+=en;
p_out=KP*en; //比例项输出
i_out=KI*es; //积分项输出
d_out=KD*(en-el); //微分项输出
if(i_out>100) //积分分离
i_out=100;
if(i_out<-100)
i_out=-100;
duty_tem=p_out+i_out+d_out; //总输出量
}
else
{
pout=0;
}*/
if (timecount%10==0)
{showflag=1; //0.5秒对屏幕进行一次刷新
//nowtemp+=1;
}
TR1=1; //开始计时
EA=1;
}
//pid控制算法
pid()
{
el=en;
en=(signed)settemp-(signed)nowtemp;
5.
三、程序设计
主要思想:首先AD转换器TLC1549是10位的,故转换最大值为1023,为显示及计算方便,只取到1000,对应输入电压值4.88V,对应显示温度值100摄氏度.由于温度传感器基本是线性的,故0摄氏度对应电压值0V,取放大器放大倍数约100倍,将输入的微小电流放大.程序中计时器定时为50ms,每50次也就是2.5秒进行一次AD转换,显示及PID计算,也就是2.5秒的加热周期,故占空比的比例为五十分之几,这个几是PID计算出来的u换算出来的.u的取值有负有正,且大小范围非常大,不好确定,故对u经行比例压缩,u越大加热的占空比就越高,u为0对应不加热,这样就将u转换为占空比.虽然可能精度不够,但基本实现了温度的控制.关于PID三个系数的确定问题,比例项应大,使温度反应快一点.积分微分项系数也稍微取大一点,使震荡次数下降,稳定值波动较小.系统也许需要比较长的时间才能准确的跟踪设定值.
一、设计要求
1.传感器:Pt100铂热电阻
2.测量放大器:自己设计与搭建
3.被控对象:400W电热杯,约0.5公斤自来水
4.执行机构:12V驱动,5A负载能力的继电器
5.控制系统:51单片机
6.控制算法:PID
7.温度范围:环境温度~100度
8.测量误差1度,控制误差2度
二、
1.
热电阻传感器是利用导体或半导体的电阻值随温度变化而变化的原进行测温的。
for(ii=0;ii<n;ii++)
for (jj=0;jj<120;jj++);
return;
}
void keyscan()
{ uchar key=0,tempKey=0;
XBYTE[0x8fff]=0x0f;
delay(100);
tempKey=XBYTE[0x8fff];//读取按键
tempKey&=0x0f;
系统地址:0A000H0A001H0A002H0A003H
源程序附在附录里面。
四、实验结果
在电热水杯加热的初始阶段,继电器始终保持接通状态,电热水杯全速加热。当水温到达50℃以上,继电器在PID算法的控制下开始通断交替,且随着水温的升高,继电器接通的时间逐渐减少,断开的频率逐渐增大。当水温到达设定温度左右(±2℃)时,继电器通断时间比例不再变化,电热水杯处在保温状态,水温恒定在设定温度上下。经过调试和温度的校后显示的温度值是比较准确的,误差大约在零点几摄氏度内.在做温度控制的时候,由于我显示出了占空比值和时间t,故调整系数很方便和直观.控制误差大约一摄氏度左右,也是符合要求的,但系统的超调量还是比较大的,故做了适当软件上的调整,如温度超过设定值时直接将继电器断开,而不是等待PID做出反应,毕竟温度控制是一个大滞后性和大惯性的系统.
main()
{
pout=1;
LCD_inti();
AD_init();
timer_init();
if (adflag)
{
nowtemp=AD();
nowtemp=AD();
adflag=0;
pid();
}
while (1)
{
keyscan();
if (showflag)
{
LCD_inti();
{
TR1=0; //停止计时
EA=0;
TH1=0x4C;
TL1=0x00;//初值19456
timecount++;
if (timecount>50) //2.5秒进行一次PID控制
{
timecount=1;
adflag=1;//ad采样开始控制
}
/*if (timecount<=duty)
{
pout=1;
由此,我们可以选择R8=20K,R4=10K,R2=10K,R7=2K的电位器,为了使放大器的性能更好,我们可以把R10选为200欧姆的电位器。调节R,可以调节电平,调节R7是调节放大倍数。
4.
调节测量放大电路的电位器R2和R7,使差分放大器输出0-5V电压,送入单片机。单片机的A/D转换电路将0-5V电压划分为1024个量化台阶,即0-5V对应0-1023。用10位二进制数表示。采样读出DIO口的连续10个电平值,当量化台阶为1000时,刚好1000对应的是100摄氏度,所以只需把采样读出的电平值左移一位,即可换算出对应的温度值。
热电阻的工作原理:温度升高,金属内部原子晶格的振动加剧,从而使金属内部的自由电子通过金属导体时的阻碍增大,宏观上表现出电阻率变大,电阻值增加,我们称其为正温度系数,即电阻值与温度的变化趋势相同。
2.
3.
说明:电位器R10用来调节偏置电压,而电位器R7则用来调节增益。实验时,用R10来调节零点,用R7来调节满度。该电路将0℃-100℃转换为0-5V电压。
return temp;
}
//定时器初始化
void timer_init()
{
TMOD=0x10; //T1:计时器
EA=1;//全局中断允许
TH1=0x4C;
TL1=0x00; //初值19456
ET1=1;//允许定时器中断
timecount=0;
TR1=1; //开始计时
}
void OnTimer1() interrupt 3
delay(100);
key=XBYTE[0x8fff];//再次读取按键
key&=0x0f;
if(key==tempKey)
{
while(key==tempKey)//等待按键弹起
{
key=XBYTE[0x8fff];
key&=0x0f;
}
switch(tempKey)
{
case 1:if(settemp<=900)settemp+=100;break;
case 2:if(settemp>100)settemp-=100;break;
case 4:if(settemp<=990)settemp+=10; break;
case 8:if(settemp>10)settemp-=10; break;
default: break;
}
}
}
void writeData(uchar data1)
上述电路图采用仪表放大器,将铂热电阻两端的电压U2与电位器R10两端的电压U1差放大,放大器输出电压U0与电压差的关系为:
由铂热电阻阻值与水温的关系可知,铂热电阻的范围是 。则 整理得:
而仪表放大器的输出电压为0~5V,所以放大倍数大约为:5/0.04=125。
当假设R8/R4=2时,R2/R7=30
duty_tem=0;
if(duty_tem>=100)
duty_tem=100;
duty=floor(duty_tem)/2;//u最大值为50
if(duty_tem<0) pout=1;
else pout=0;
return (pout);
}
///////////////////////
uchar num2[13]={0x4E,0x4F,0x57,0x54,0x3A,0,0,0,0x2E,0,0xDF,0x43};
unsigned int settemp=500,nowtemp;
unsigned char timecount=0;
bit adflag,showflag; //ad标志位
{
while(XBYTE[0xa001]&0x80);//等待可写
XBYTE[0xa002]=(data1);//向数据端口写入数据
}
void writeString(uchar data1[],int len)
{
int k=0;
for(k=0;k<len;k++)//写入字符串
writeData(data1[k]);
摘要
本次实验是软硬件相结合的实验,通过传感器得到的阻值与其它电阻,可以搭建一个电桥,将水温转化为电压,然后通过放大器将电压放大到所需要的值,将所得的电压送入单片机的AD转换电路,将模拟信号转换成数字信号,从而在单片机的液晶屏上显示当前的温度。此烧水壶是可控制的,即设定温度,使水加热到设定温度且保温,此控制算法采用PID控制算法来控制继电器的通断,来保证水温恒定在设定温度处。
}
unsigned int AD() //本次AD读取上一次的值
{
uint temp=0;
uchar i;
AD_CS = 0;
SCLK = 0;
for(i=0;i<10;i++)
{
temp <<=1;
temp |= DIO;
SCLK = 1;
SCLK = 0;
}
AD_CS = 1;
delay(256); //延迟>17us
附:源程序
#include <REG52.H>
#include <absacc.h>
#include <intrins.h>
#include <math.h>
#define uchar unsigned char
#define uint unsigned int
#define KP 0.55 //比例系数
五、
3年大学时光中,觉得最有用的课程就是这些实验了,它让我们学有所用,而不仅仅是各种理论,而是通过实践来巩固深入理解理论。这门实验课让我们不仅在软件上花了大量的功夫,代码力求精益求精;而且.在实际的调试中,还是发现软件只是一方面的内容,外硬件的优劣也是影响系统性能和精度的致命因素.尤其是做控制的时候和调整温度的时候,一个可靠的硬件可以省下不少功夫.温度控制系统的设计让我们学会的不仅仅是对单片机的内部结构的进一步理解与运用,还在上一次实验的基础上进一步提高运放集成电路设计与运用的能力。
}
//LCD初始化
void LCD_inti()
{
BUSY();
XBYTE[0xa000]=0x01;
BUSY();
XBYTE[0xa000]=0x06;
BUSY();
XBYTE[0xa000]=0x1c;
BUSY();
XBYTE[0xa000]=0x38;
BUSY();
XBYTE[0xa000]=0x0c;
#define KI 0.02 //积分系数
#define KD 0.1 //微分系数
sbit pout=P1^7;
sbit SCLK=P1^2;
sbit DIO=P1^3;
sbit AD_CS=P1^4;
uchar num1[13]={0x53,0x45,0x54,0x54,0x3A,0,0,0,0x2E,0,0xDF,0x43};
}
void BUSY()
{
uchar BUSYBF=0;
while(1)
{
BUSYBF=XBYTE[0xa001];
if(BUSYBF <0x80) break;
}
return;
}
//AD初始化
void AD_init()
{
AD_CS=1; //必须为高
SCLK=1; //上升沿有效
DIO=1;//置高
TMOD设置
1.采用T1定时/计数器,工作在方式1下,见下表:
GATE
C/T
M1
M0
GATE
C/T
M1
M0
0
0
0
1
0
0
0
0
所以 TMOD=10H
2.开中断:EA=1;
定时器T1开:ET1=1
3.TH1、TL1设置
定时时间设为50ms,所以
50×10-3=(216-初始值)×(12/11.0592)×10-6
unsigned char duty=0,duty_tem=0; //输出控制量
float el,en=0,es=0;
float p_out,i_out,d_out;
void write();
void BUSY();
void LCD_inti();
void delay(uchar n)
{
int ii,jj;
解得: 初始值=19456=01001100 00000000B
因此:TH1=01001100B=4CH TL1=00000000B=00H
程序中需用到的系统地址
A15
A14
A13
A12
A11
A10
A9
A8
A7
A6
A5
A4
A3
A2
A1
A0
1
0
1
0
0
0
0
0
0
0
0
0
0
0
RS
R/W
A1A0组合分别是00:写指令;01:写数据;10:读指令;11:读数据
es+=en;
p_out=KP*en; //比例项输出
i_out=KI*es; //积分项输出
d_out=KD*(en-el); //微分项输出
if(i_out>100) //积分分离
i_out=100;
if(i_out<-100)
i_out=-100;
duty_tem=p_out+i_out+d_out; //总输出量
}
else
{
pout=0;
}*/
if (timecount%10==0)
{showflag=1; //0.5秒对屏幕进行一次刷新
//nowtemp+=1;
}
TR1=1; //开始计时
EA=1;
}
//pid控制算法
pid()
{
el=en;
en=(signed)settemp-(signed)nowtemp;
5.
三、程序设计
主要思想:首先AD转换器TLC1549是10位的,故转换最大值为1023,为显示及计算方便,只取到1000,对应输入电压值4.88V,对应显示温度值100摄氏度.由于温度传感器基本是线性的,故0摄氏度对应电压值0V,取放大器放大倍数约100倍,将输入的微小电流放大.程序中计时器定时为50ms,每50次也就是2.5秒进行一次AD转换,显示及PID计算,也就是2.5秒的加热周期,故占空比的比例为五十分之几,这个几是PID计算出来的u换算出来的.u的取值有负有正,且大小范围非常大,不好确定,故对u经行比例压缩,u越大加热的占空比就越高,u为0对应不加热,这样就将u转换为占空比.虽然可能精度不够,但基本实现了温度的控制.关于PID三个系数的确定问题,比例项应大,使温度反应快一点.积分微分项系数也稍微取大一点,使震荡次数下降,稳定值波动较小.系统也许需要比较长的时间才能准确的跟踪设定值.
一、设计要求
1.传感器:Pt100铂热电阻
2.测量放大器:自己设计与搭建
3.被控对象:400W电热杯,约0.5公斤自来水
4.执行机构:12V驱动,5A负载能力的继电器
5.控制系统:51单片机
6.控制算法:PID
7.温度范围:环境温度~100度
8.测量误差1度,控制误差2度
二、
1.
热电阻传感器是利用导体或半导体的电阻值随温度变化而变化的原进行测温的。
for(ii=0;ii<n;ii++)
for (jj=0;jj<120;jj++);
return;
}
void keyscan()
{ uchar key=0,tempKey=0;
XBYTE[0x8fff]=0x0f;
delay(100);
tempKey=XBYTE[0x8fff];//读取按键
tempKey&=0x0f;
系统地址:0A000H0A001H0A002H0A003H
源程序附在附录里面。
四、实验结果
在电热水杯加热的初始阶段,继电器始终保持接通状态,电热水杯全速加热。当水温到达50℃以上,继电器在PID算法的控制下开始通断交替,且随着水温的升高,继电器接通的时间逐渐减少,断开的频率逐渐增大。当水温到达设定温度左右(±2℃)时,继电器通断时间比例不再变化,电热水杯处在保温状态,水温恒定在设定温度上下。经过调试和温度的校后显示的温度值是比较准确的,误差大约在零点几摄氏度内.在做温度控制的时候,由于我显示出了占空比值和时间t,故调整系数很方便和直观.控制误差大约一摄氏度左右,也是符合要求的,但系统的超调量还是比较大的,故做了适当软件上的调整,如温度超过设定值时直接将继电器断开,而不是等待PID做出反应,毕竟温度控制是一个大滞后性和大惯性的系统.
main()
{
pout=1;
LCD_inti();
AD_init();
timer_init();
if (adflag)
{
nowtemp=AD();
nowtemp=AD();
adflag=0;
pid();
}
while (1)
{
keyscan();
if (showflag)
{
LCD_inti();
{
TR1=0; //停止计时
EA=0;
TH1=0x4C;
TL1=0x00;//初值19456
timecount++;
if (timecount>50) //2.5秒进行一次PID控制
{
timecount=1;
adflag=1;//ad采样开始控制
}
/*if (timecount<=duty)
{
pout=1;
由此,我们可以选择R8=20K,R4=10K,R2=10K,R7=2K的电位器,为了使放大器的性能更好,我们可以把R10选为200欧姆的电位器。调节R,可以调节电平,调节R7是调节放大倍数。
4.
调节测量放大电路的电位器R2和R7,使差分放大器输出0-5V电压,送入单片机。单片机的A/D转换电路将0-5V电压划分为1024个量化台阶,即0-5V对应0-1023。用10位二进制数表示。采样读出DIO口的连续10个电平值,当量化台阶为1000时,刚好1000对应的是100摄氏度,所以只需把采样读出的电平值左移一位,即可换算出对应的温度值。
热电阻的工作原理:温度升高,金属内部原子晶格的振动加剧,从而使金属内部的自由电子通过金属导体时的阻碍增大,宏观上表现出电阻率变大,电阻值增加,我们称其为正温度系数,即电阻值与温度的变化趋势相同。
2.
3.
说明:电位器R10用来调节偏置电压,而电位器R7则用来调节增益。实验时,用R10来调节零点,用R7来调节满度。该电路将0℃-100℃转换为0-5V电压。
return temp;
}
//定时器初始化
void timer_init()
{
TMOD=0x10; //T1:计时器
EA=1;//全局中断允许
TH1=0x4C;
TL1=0x00; //初值19456
ET1=1;//允许定时器中断
timecount=0;
TR1=1; //开始计时
}
void OnTimer1() interrupt 3
delay(100);
key=XBYTE[0x8fff];//再次读取按键
key&=0x0f;
if(key==tempKey)
{
while(key==tempKey)//等待按键弹起
{
key=XBYTE[0x8fff];
key&=0x0f;
}
switch(tempKey)
{
case 1:if(settemp<=900)settemp+=100;break;
case 2:if(settemp>100)settemp-=100;break;
case 4:if(settemp<=990)settemp+=10; break;
case 8:if(settemp>10)settemp-=10; break;
default: break;
}
}
}
void writeData(uchar data1)
上述电路图采用仪表放大器,将铂热电阻两端的电压U2与电位器R10两端的电压U1差放大,放大器输出电压U0与电压差的关系为:
由铂热电阻阻值与水温的关系可知,铂热电阻的范围是 。则 整理得:
而仪表放大器的输出电压为0~5V,所以放大倍数大约为:5/0.04=125。
当假设R8/R4=2时,R2/R7=30