目 录
摘 要 I
Abstract II
1绪论 1
1.1 目的及意义 1
1.2 国内外研究现状 1
1.3 研究内容及章节安排 2
2总体方案设计 4
2.1 总体方案的确定 4
2.2 系统组成 5
3技术算法介绍 6
3.1 AT89C51简介 6
3.1.1 单片机的引脚介绍 6
3.1.2 单片机的存储结构 8
3.2 PID算法介绍 10
3.2.1 PID算法的数字化 10
3.2.2 PID算法的运用 11
3.3 小结 12
4系统硬件设计 13
4.1 系统概况 13
4.2 功能模块 13
4.2.1 单片机控制模块 13
4.2.2 数据转换与采集模块A/D0808 14
4.2.3 按键选择模块 15
4.2.4 显示模块 15
4.2.5 报警模块 16
4.2.6 输出模块 16
4.3 总体方案的实现和元器件清单 17
4.3.1系统的整体设计 17
4.3.2元器件清单 18
4.4 小结 18
5系统软件设计 19
5.1 Protues7软件概况 19
5.2 WAVE6000软件简介 20
5.2.1 软件概况 20
5.2.2 程序界面 21
5.3 子程序设定 21
5.4 程序流程 21
5.5 程序仿真调试 29
5.5.1 WAVE6000仿真调试 29
5.5.2 软硬连调 29
5.6 小结 30
总 结 31
参考文献 32
致 谢 34
本系统是采用以AT89C51单片机为核心的电加热炉温度智能控制系统,通过温度传感器采样实时温度,并通过变送器将温度最终转换为电压信号通过A/D转换器0808将其转换为数字信号,送入单片机与给定值进行比较,通过运用PID算法得出控制结果,送显示并进行控制。
2总体方案设计
2.1 总体方案的确定
由于电加热炉温度智能控制系统的控制对象具有惯性大,连续性的特点。因而可以归于具有纯滞后的一阶大惯性环节。一般来说,热过程大多具有较大的滞后,它对任何信号的响应都会推迟一段时间,使输出与输入之间产生相移。对于这样一些存在大的滞后特性的过渡过程控制,一般来说可以采用以下几种控制方案:
1.输出开关量控制
对于惯性较大的过程可以简单地采用输出开关量控制的方法。这种方法通过比较给定值与被控参数的偏差来控制输出的状态:开关或者通断,因此控制过程十分简单,也容易实现。但由于输出控制量只有两种状态,使被控参数在两个方向上变化的速率均为最大,因此容易引起反馈回路产生振荡,对自动控制系统会产生十分不利的影响,甚至会因为输出开关的频繁动作而不能满足系统对控制精度的要求。因此,这种控制方案一般在大惯性系统对控制精度和动态特性要求不高的情况下采用。
2.比例控制(P控制)
比例控制的特点是控制器的输出与偏差成比例,输出量的大小与偏差之间有对应关系。当负荷变化时,抗干扰能力强,过渡时间短,但过程始终存在余差。因此它适用于控制通道滞后较小、负荷变化不大、允许被控量在一定范围内变化的系统。使用时还应注意经过一段时间后需将累积误差消除。
3.比例积分控制(PI控制)
由于比例积分控制的特点是控制器的输出与偏差的积分成比例,积分的作用使得过渡过程结束时无余差,但系统的稳定性降低。虽然加大比例度可以使稳定性提高,但又使过渡时间加长。本文转载自http://www.biyezuopin.vip/onews.asp?id=14096因此,PI控制适用于滞后较小、负荷变化不大、被控量不允许有余差的控制系统,它是工程上使用最多、应用最广的一种控制方法。
4.比例积分加微分控制(PID控制)
比例积分加微分控制的特点是微分的作用使控制器的输出与偏差变化的速度成正比例,它对克服对象的容量滞后有显著的效果。在比例基础上加上微分作用,使稳定性提高,再加上积分作用,可以消除余差。因此,PID控制适用于负荷变化大、容量滞后较大、控制品质要求又很高的控制系统。
#include<reg51.h>
#include<intrins.h>
#include<absacc.h>
#define uchar unsigned char
#define uint unsigned int
#define BUSY 0x80 //1cd忙检測标志
#define DATAPORT P0 //定义P0口为LCD通讯端口
#define PLAYE_ADDR XBYTE[0XBFFF] //语音报警地址
sbit RED_ALARM=P1^0; //红色指示灯
sbit WRITE_ALARM=P1^1; // 白色指示灯
sbit GREEN_ALARM=P1^2; //録色指示灯
sbit BLUE_ALARM = P1^3;
sbit P1_4 = P1^4; //时钟调整
sbit P1_5 = P1^5; //时钟加
sbit P1_6=P1^6; //时钟减
sbit DQ=P1^7; //定义ds18b20通信端口
sbit LCM_RS=P2^0; //数据/命令端
sbit LCM_RW=P2^1; //读/写选择端
sbit LCM_EN=P2^2;
sbit sda=P2^3; //I0口定义
sbit scl=P2^4; //LCD使能信号
sbit ad_busy=P3^2; //adc中断方式接口
sbit RECLED_EOC=P3^5; //ISD1420放音结東查询标志
sbit OUT=P3^7;
uchar ad_data; //ad采样值存储
uchar seconde; //定义并初始化时钟变量
uchar minite;
uchar hour;
uchar mstcnt=0;
uchar temp1,temp2,temp; //温度显示变量
uchar t,set;
uchar K;
bit ON_OFF=0;
bit outflag;
bit write=0;
uchar code str0[]={"--- : : --- "};
uchar code str1[]={"SET: C SA: . C"};
void delay_LCM(uint); //LCD延时子程序
void initLCM(void); //LCD初始化子程序
void lcd_wait(void); //LCD检测忙子程序
void WriteCommandLCM(uchar WCLCM,uchar BusyC);//写指令到1CM子函数
void WriteDataLCM(uchar WDLCM); //写数据到1CM子函数
void DisplayOneChar(uchar X,uchar Y,uchar DData);//显示指定坐标的一个字符子函数
void DisplayListChar(uchar X,uchar Y,uchar code *DData);//显示指定坐标的串字符子函数
void init_timer0(void); //定时器初始化
void displayfun1(void);
void displayfun2(void);
void displayfun3(void);
void displayfun4(void);
void keyscan(void); //键盘扫描子程序
void set_adj(void);
void inc_key(void);
void dec_key(void);
void delay_18B20(unsigned int i);
void Init_DS18B20(void);
uchar ReadOneChar(void);
void WriteOneChar(unsigned char dat);
void ReadTemperature(void);
void ad0809(void);
void playalarm(void);
//**********延时K*1ms,12.000mhz*******/
void delay_LCM(uint k)
{
uint i,j;
for(i=0; i<k; i++)
{
for(j=0;j<60;j++)
{;}
}
}
//*****写指令到LCM子函数********//
void WriteCommandLCM(uchar WCLCM,uchar BusyC)
{
if(BusyC)lcd_wait();
DATAPORT=WCLCM;
LCM_RS=0; //选中指令寄存器
LCM_RW=0; //写模式
LCM_EN=1;
_nop_();
_nop_();
_nop_();
LCM_EN=0;
}
//*****写数据到LCM子函数********//
void WriteDataLcm(uchar WDLCM)
{
lcd_wait(); //检测忙信号
DATAPORT=WDLCM;
LCM_RS=1; //选中数据寄存器
LCM_RW=0; //写模式
LCM_EN = 1;
_nop_();
_nop_();
_nop_();
LCM_EN=0;
}
//*******1cm内部等待函数********/
void lcd_wait(void)
{
DATAPORT=0xff;
LCM_EN=1;
LCM_RS=0;
LCM_RW=1;
_nop_();
while(DATAPORT&BUSY)
{
LCM_EN=0;
_nop_();
_nop_();
LCM_EN=1;
_nop_();
_nop_();
}
LCM_EN=0;
}
//*******LCM初始化子函数*******/
void initLCM()
{
DATAPORT=0;
delay_LCM(15);
WriteCommandLCM(0x38,0);//三次显示模式设置,不检测忙信号
delay_LCM(5);
WriteCommandLCM(0x38,0);
delay_LCM(5);
WriteCommandLCM(0x38,0);
delay_LCM(5);
WriteCommandLCM(0x38,1);//8bit数据传送,2行显示,5*7字型,检测忙信号
WriteCommandLCM(0x08,1);//关闭显示,检测忙信号
WriteCommandLCM(0x01,1);//清屏,检測忙信号
WriteCommandLCM(0x06,1);//显示光标右移设置,检测忙信号
WriteCommandLCM(0x0c,1);//显示屏打开,光标不显示,不闪烁,检测忙信号
}
//****常显示指定坐标的一个字符子函数******//
void DisplayOneChar(uchar X,uchar Y,uchar DData)
{
Y &= 1;
X &= 15;
if(Y)X|=0x40; //若y为1(显示第二行),地址码+0X40
X|=0x80; //指令码为地址码+0X80
WriteCommandLCM(X,0);
WriteDataLCM(DData);
}
//****显示指定坐标的一串字符子函数*****//
void DisplayListChar(uchar X,uchar Y,uchar code *DData)
{
uchar ListLength = 0;
Y &= 0x01;
X &= 0x0f;
while(X < 16)
{
DisplayOneChar(X,Y,DData[ListLength]);
ListLength++;
X++;
}
}
//*****ds18b20延迟子函数(晶振12MHz)****/
void delay_18B20(unsigned int i)
{
while(i--);
}
//*****ds18b20初始化函数*****//
void Init_DS18B20(void)
{
unsigned char x = 0;
DQ = 1; //DQ复位
delay_18B20(8); //稍做延时
DQ = 0; //单片机将DQ拉低
delay_18B20(80); //精确延时大于480us
DQ = 1; //拉高总线
delay_18B20(14);
x = DQ; //稍做延时后如果x=0则初始化成功x=1则初始化失败
delay_18B20(20);
}
//*******ds18b20读一个字节*******/
unsigned char ReadOneChar(void)
{
uchar i = 0;
uchar dat = 0;
for (i = 8; i>0; i--)
{
DQ = 0; //给脉冲信号
dat >>= 1;
DQ = 1; //给脉冲信号
if(DQ)
dat|=0x80;
delay_18B20(4);
}
return(dat);
}
//****ds18b20写一个字节****/
void WriteOneChar(uchar dat)
{
unsigned char i = 0;
for(i = 8;i>0;i--)
{
DQ = 0;
DQ = dat&0x0l;
delay_18B20(5);
DQ = 1;
dat >>= 1;
}
}
//****读取ds18b20当前温度*****/
void ReadTemperature(void)
{
unsigned char a = 0;
unsigned char b = 0;
unsigned char t = 0;
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0x44); //启动温度转换
delay_18B20(100); //this message is wery important
Init_DS18B20();
WriteOneChar(0xCC); //跳过读序号列号的操作
WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器)前两个就是温度
delay_18B20(100);
a = ReadOneChar(); //读取温度值低位
b = ReadOneChar(); //读取温度值高位
temp1 = b<<4;
temp1 += (a&0xf0)>>4;
temp2 = a&0x0f;
temp =((b*256+a)>>4);//当前采集温度值除16得实际温度值
}
//****液品显示子函数1正常显示*****//
void displayfun1(void)
{
WriteCommandLCM(0x0c,1); //显示屏打开,光标不显示,不闪烁,检测忙信号
DisplayListChar(0,0,str0);
DisplayListChar(0,1,str1);
DisplayOneChar(3,0,hour/10+0x30);//液晶上显示小时
DisplayOneChar(4,0,hour%10+0x30);
DisplayOneChar(6,0,minite/10+0x30),//液晶上显示分
DisplayOneChar(7,0,minite%10+0x30);
DisplayOneChar(9,0,seconde/10+0x30);//液晶上显示秒
DisplayOneChar(10,0,seconde%10+0x30);
DisplayOneChar(4,1,K/10+0x30); //液品上显示设定的温度
DisplayOneChar(5,1,K%10+0x30);
DisplayOneChar(11,1,temp1/10+0x30);//液晶上显示测得的温度
DisplayOneChar(12,1,temp1%10+0x30);
DisplayOneChar(14,1,temp2/10+0x30);
if(ON_OFF == 0) //若温控标志为0
{
DisplayOneChar(14,0,0x4f); //液晶上显示不控温的标志
DisplayOneChar(15,0,0x46);
}
else
{
DisplayOneChar(14,0,0x4f); //液晶上显示控温的标志
DisplayOneChar(15,0,0x4e);
if(outflag == 1)
DisplayOneChar(0,0,0x7c);
else
DisplayOneChar(0,0,0xef);
}
}
//*****液晶最示子函数2*****//
void displayfun2(void)
{
WriteCommandLCM(0x0c,1); //显示屏打开,光标不显示,不闪烁,检測忙信号
DisplayListChar(0,0,str0);
DisplayListChar(0,1,str1);
DisplayOneChar(6,0,minite/10+0x30);
DisplayOneChar(7,0,minite%10+0x30);
DisplayOneChar(9,0,seconde/10+0x30);
DisplayOneChar(10,0,seconde%10+0x30);
DisplayOneChar(4,1,K/10+0x30);
DisplayOneChar(5,1,K%10+0x30);
DisplayOneChar(11,1,temp1/10+0x30);
DisplayOneChar(12,1,temp1%10+0x30);
DisplayOneChar(14,1,temp2/10+0x30);
WriteCommandLCM(0x0f,1); //显示打开,光标显示,闪烁,检测忙信号
DisplayOneChar(3,0,hour/10+0x30);
DisplayOneChar(4,0,hour%10+0x30);
}
//*****液晶显示子函数3****//
void displayfun3(void)
{
WriteCommandLCM(0x0c,1); //显示屏打开,光标不显示,不闪烁,检测忙信号
DisplayListChar(0,0,str0);
DisplayListChar(0,1,str1);
DisplayOneChar(3,0,hour/10+0x30);
DisplayOneChar(4,0,hour%10+0x30);
DisplayOneChar(9,0,seconde/10+0x30);
DisplayOneChar (10,0,seconde%10+0x30);
DisplayOneChar(4,1,K/10+0x30);
DisplayOneChar(5,1,K%10+0x30);
DisplayOneChar(11,1,temp1/10+0x30);
DisplayOneChar(12,1,temp1%10+0x30);
DisplayOneChar(14,1,temp2/10+0x30);
WriteCommandLCM(0x0f,1);//显示屏打开,光标显示,闪烁,检测忙信号
DisplayOneChar(6,0,minite/10+0x30);
DisplayOneChar(7,0,minite%10+0x30);
}
//*****液晶显示子函数4******//
void displayfun4(void)
{
WriteCommandLCM(0x0c,1);//显示屏打开,光标不显示,不闪烁,检测忙信号
DisplayListChar(0,0,str0);
DisplayListChar(0,1,str1);
DisplayOneChar(3,0,hour/10+0x30);
DisplayOneChar(4,0,hour%10+0x30);
DisplayOneChar(6,0,minite/10+0x30);
DisplayOneChar(7,0,minite%10+0x30);
DisplayOneChar(9,0,seconde/10+0x30);
DisplayOneChar(10,0,seconde%10+0x30);
DisplayOneChar(11,1,temp1/10+0x30);
DisplayOneChar(12,1,temp1%10+0x30);
DisplayOneChar(14,1,temp2/10+0x30);
WriteCommandLCM(0x0f,1);//显示屏打开,光标显示,闪烁,检测忙信号
DisplayOneChar(4,1,K/10+0x30);
DisplayOneChar(5,1,K%10+0x30);
}
//*****键盘扫描子函数*****//
void keyscan(void)
{
uchar xx; //局部变量
P1 = 0xff;
if( P1 != 0xff)
{
delay_LCM(50);
if(P1 != 0xff)
{
xx = P1;
switch (xx) //根据按键状态,调用不同的子函数
{
case 0xfe:set_adj();
break;
case 0xfd:inc_key();
break;
case 0xfb:dec_key();
break;
case 0xf7:{delay_LCM(150);ON_OFF=!ON_OFF;while(!BLUE_ALARM);}
break;
default: break;
}
}
}
}
//*****设定工作模式子函数*****//
void set_adj(void)
{
delay_LCM(100);
set++;
if(set >= 4)set = 0;
while(!RED_ALARM);
}
//*****按键加法子函数******//
void inc_key(void)
{
delay_LCM(150);
switch(set)
{
case 0: if(P1 == 0xf7)
{delay_LCM(150);ON_OFF = !ON_OFF;}
break;
case 1: hour++; if(hour >= 23)hour = 23;
break;
case 2: minite++; if(minite >= 59)minite = 59;
break;
case 3:K++;if(K >= 99)K = 99;
break;
default: break;
}
while(!WRITE_ALARM);
}
//*****按键减法子函数******//
void dec_key( void)
{
delay_LCM(150);
switch(set)
{
case 0:if(P1 == 0xf7)
{delay_LCM(150);ON_OFF=!ON_OFF;}
break;
case 1:hour--;
if(hour <= 0)hour = 0;
break;
case 2: minite--;
if(minite <= 0)minite = 0;
break;
case 3: K--; if(K <= 1)K = 1;
break;
default: break;
}
while(!GREEN_ALARM);
}
//******24C02读写驱动程序*****//
void delay1(uchar x)
{
uint i;
for(i = 0;i < x;i++);
}
void flash()
{ ; ; }
void x24c02_init()//24c02初始化子程序
{
scl = 1;
flash();
sda = 1;
flash();
}
void start() //启动I2C总线
{
sda = 1;
flash();
scl = 1;
flash();
sda = 0;
flash();
scl = 0;
flash();
}
void stop() //停止I2C总线
{
sda = 0;
flash();
scl = 1;
flash();
sda = 1;
flash();
}
void writex(uchar j)//写一个字节
{
uchar i,temp;
temp = j;
for (i = 0; i < 8; i++)
{
temp = temp<<1;
scl = 0;
flash();
sda = CY;
flash();
scl = 1;
flash();
}
scl = 0;
flash();
sda = 1;
flash();
}
uchar readx()//读一个字节
{
uchar i,j,k = 0;
scl = 0;
flash();
sda = 1;
for(i = 0;i < 8;i++)
{
flash();
scl = 1;
flash();
if(sda == 1)j = 1;
else j = 0;
k = (k<<1)|j;
scl = 0;
}
flash();
return(k);
}
void clock() //I2C总线时钟
{
uchar i = 0;
scl = 1,
flash();
while((sda == 1)&&(i<255))i++;
scl = 0;
flash();
}
//从24c02的地址 address中读取一个字节数据
uchar x24c02_read(uchar address)
{
uchar i;
start();
writex(0xa0);
clock();
writex(address);
clock();
start();
writex(0xa1);
clock();
i = readx();
stop();
delay1(10);
return (i);
}
向24c02的address地址中写入一字节数据info
void x24c02_write(uchar address,uchar info)
{
EA = 0;
start();
writex(0xa0);
clock();
writex(address);
clock();
writex(info);
clock();
stop();
EA = 1;
delay1(50);
}
//*****定时器t0初始化*****//
void init_timer0(void)
{
TMOD = 0x01; //time0为定时器,方式1
TH0 = 0x3c; //于预置计数初值
TL0 = 0xb0;
EA = 1;
ET0 = 1;
TR0 = 1;
}
//*****定时器t0中断子函数*****//
void timer0(void) interrupt 1 using 0 //定时器0方式1,50ms中断一次
{
TH0 = 0x3c;
TL0 = 0xb0;
mstcnt++;
if(mstcnt >= 20 )
{seconde++;write = 1; mstcnt = 0;}
if(seconde >= 60)
{minite++; seconde = 0;}
if(minite >= 60 )
{hour++; minite = 0;}
if(hour >= 24 )
{hour = 0;}
keyscan(); //按键扫描
}
//******the main funtion******/
void main(void)
{
P1 = 0xff; //初始化p1口,全设为1
x24c02_init(); //初始化24002
K = x24c02_read(2); //读出保存的数据于K
seconde = x24c02_read(4);
minite = x24c02_read(6);
hour = x24c02_read(8);
delay_LCM(500); //延时500ms启动
initLCM(); //LCD初始化
init_timer0(); //时钟定时器0初始化
Init_DS18B20(); //DS18B20初始化
DisplayListChar(0,0,str0);
DisplayListChar(0,1,str1);
while (1)
{
keyscan(); //按键扫描
ReadTemperature(); //温度采集
switch(set) //LCD根据工作模式显示
{
case 0: displayfun1();delay_LCM(1000); break;//正常工作显
case 1: displayfun2();delay_LCM(1000); break;//调整时显示
case 2: displayfun3();delay_LCM(1000); break;//调整分显示
case 3: displayfun4();delay_LCM(1000); break;//温度设定
default: break;
}
keyscan(); //相当于延时
if(ON_OFF == 1) //若温控标志位1,控制LAMP动作
{
if(temp1 >= K+1)
{outflag = 1;OUT = 0;}
if(temp1<K)
{
delay_LCM(1000);
if(temp1<K)
{outflag = 0;OUT = 1;}
}
}
else{outflag = 0; OUT = 1;}
if(write == 1) //判断计时器是否计时一秒
{
write = 0; //清零
x24c02_write(8,hour); //在24c02的地址2中写入数据hour
delay_LCM(11);
x24c02_write(6,minite);
delay_LCM(11);
x24c02_write(4,seconde);
delay_LCM(11);
x24c02_write(2,K);
}
}
}