资源描述
电子工程设计报告
题目:闭环温度控制系统设计
——单片机、显示和键盘电路及系统软件
专 业:
小 组:
姓名学号:
指导教师:张辉
完成日期:
摘 要
随着现代工业技术的迅猛发展,工业上的各种技术指标精度的要求也越来越高,在众多指标中,温度的测量与控制是一个永恒的话题,只有了精确地温度测量,才能把加热和散热的工作做得更好。本课题是小型温度测量与控制系统设计,以单片机AT89C51芯片为核心,和LED数码管显示,数模、模数转换和辅以稳压电源及变送器的测量控制系统,另配有键盘输入控制。
本设计详细介绍了LED显示与键盘控制电路的工作原理,硬件电路组成的思路和相应的C语言闭环程序。本设计主要包括以下几个模块:LED显示模块、键盘控制模块.能够对环境温度随时随地检测与显示,并可以在模板上设定相应温度值使系统达到设定温度.
【关键词】:LED显示、键盘控制、闭环程序、模板测试.
目 录
一、 功能指标要求-—-----——---—-—----—-——-————————---——--—-—————————-1
二、 D/A模块调试-——-----——---———--——---—---——----——-—----—-————-———1
三、 A/D模块调试---—--———-—-—----——------—--—-—-----—-——-——-—--—-——2
四、 显示电路模块-—-———-———-———----—-—-————--———-—--————-----—-———--3
1. 显示电路设计—---————-—-—-—-——-—--——--—--————--—--—---——————3
2. 显示电路原理分析—---——--—-—-—-—-—--—--———————-—-———--——-—--4
五、 键盘控制电路模块———--——--—--—-——---—-——-------————-—-——-—---——-4
1. 键盘控制电路设计---—--—-----—-—-——---—-——-—----——----——-———4
2. 键盘控制电路原理分析-——-——--——-—-——-——--——-——------—----—-—4
六、 系统调试及程序设计-——-——--—--—--———-----—--—-—--—---------—--——4
七、 出现的问题分析及解决方法--—-———----—-—-----—————-——--—---——-—-14
八、 创新性-—-----—-——————--—--——---—-—--—----------—----——--——-——-14
九、 体会与建议-——-—--———-——-------—--—--————-———---———-——---————--14
【附录Ⅰ】-—-——-—--———-—---—-———---———-—--—---—----—————————-—--—-——-15
14
一、功能指标要求
1) 显示模块:4 位7 段数码显示,前 3 位含小数点
2) 键盘模块:0~9数字输入键及若干功能设置按键控制 ;第10数字键作为允许控制开关
3) 独立电路板安装结构
二、D/A模块调试
调试方法:数据为从00—FF顺序递增并不断循环的数值。
输出端波形如图二(1):
图二(1)
调试程序:
#include ”C8051F020。h”
#include ”absacc.h”
#include "data_define.c"
#define C3 XBYTE[0x4000]
#define TIMER 0x8000
#include "Init_Device。c”
void delay(void);
void main(void)
{
unsigned char x;
Init_Device();
while(1)
{ ++x; C3=x; delay(); }
}
void delay(void)
{
int i;
for(i=0;i〈TIMER;++i) ;
}
三、A/D模块调试
调试方法:输入信号范围: 0V~+5V
改变设置温度,运行A/D测试程序,检查模/数转换结果;
在调试台上通过+10按键不断改变温度数值,调试台LED会显示相应温度。
调试程序:
#include ”C8051F020。h”
#include "absacc。h"
#include "data_define.c"
#define C2 XBYTE[0x2000]
#define TIMER 0x8000
#include ”Init_Device。c"
void delay(void);
void main(void)
{
unsigned char x;
Init_Device();
while(1) { C2=x; delay(); x=C2; delay(); }
}
void delay(void)
{
unsigned char i;
for(i=0;i〈TIMER;++i);
}
四、显示电路模块
1. 显示电路设计:
这次我们用的均为两位共阳极七段数码管,管脚见图四(1)
图四(1)
显示模块电路图见图四(2):
图四(2)
2. 显示电路原理分析:
我们选用静态显示控制电路,八位数据位作为74LS273数据锁存器的输入信号,273的输出信号作为七段数码管的输入信号,控制七个LED的亮灭.地址数据A1和A2作为74LS138三八译码器的输入信号,138输出信号为锁存器时钟信号,VCC接G1端恒高有效,和C1分别接译码器G2A和G2B端,恒低有效。Y0控制选通锁存器1(即设定值高位),Y1控制选通锁存器2(即设定值低位),Y2控制选通锁存器3(即显示值高位),Y3控制选通锁存器4(即显示值低位)。A1和A2按照程序设定的变化脉冲依次令Y0、Y1、Y2、Y3输出高脉冲,分别选通四个数码管,当8051芯片数据位输入信号时,四位数码管可以依照事先译好的数据表显示出相应的数值。
五、键盘控制电路模块
1. 键盘控制电路设计:见图五(1)
图五(1)
2. 键盘控制电路原理分析:
我们采用阵列键盘读取方案,C4和均输入低电平到74LS32或门,32输出到译码器选通。同时32也输出到74LS244数据锁存器选通.编写程序使138译码器依次输出低电平,扫描是否有键按下,当键盘上有键按下时,Yn为低电平,对应的Sn也为低电平(即对应的Dn也为低电平)。
六、系统调试及程序设计
系统调试:
第一步:我们首先用调试台调试,测试开环,编写程序烧录进8051,使我们自己设计的显示模块显示位跟随调试台上LED显示模块的变化。
第二步:编写PID闭环程序,编写程序烧录进8051,使系统能够从键盘上输入一个设定温度值并显示在系统显示模块设定位上。随后按下调试台“Manual"按键,使系统能够将现在温度值与设定值做差,以确定需要升温还是降温,系统显示模块显示值会逐渐接近设定值并最终稳定在一个与设定值误差非常小的数值上。
第三步:将完整系统(包括电源模块、系统模块、A/D模块和变送器、D/A模块、显示模块和键盘控制模块)移植到实验室真实模板上,先将模板设定为手动调节,设定一个值看系统显示位是否随动;再将模板还原为自动调节,从键盘输入一个设定值,打开调节按钮,观察系统显示位是否逐渐靠近设定位并最终稳定在一个非常相近的数值上,模板上加热(或制冷)提示灯是否点亮.如果上述答案是肯定的,系统成功,能够满足教学要求,实现小型温度控制系统.
调试程序:(自定义头函数见附录Ⅰ)
#include "C8051F020.h”
#include 〈absacc.h>
#include "data_define.c”
#include "Init_Device。c”
#include "address。h" //设备地址列表
#include ”keytable。h" //键盘的键值表
#include "digtaltable.h” //数码管的编码表
static unsigned int DataT=0; //16进制的A/D数据
static int DataTO=0; //10进制的温度数据
static unsigned int DataTI=0; //10进制的目标温度(临时)
static unsigned int TargetT=0; //10进制的目标温度
static char tp=0; //目标温度和当前温度的差值
static unsigned int sign=0; //表示tp的正负号
static unsigned char Switch=0; //开关键是否按下的标志
static unsigned int Input=0; //输入
void delay(void);
void delay1(void);
void Display(const unsigned int,const unsigned int);
unsigned int Convert(const unsigned int);
unsigned int ReadT(void);
unsigned int ReadKey(void);
unsigned int ReadKeyS(void);
unsigned int Decode(unsigned int);
unsigned int ReadInput(void);
void Task(void);
void main(void)
{
Init_Device();
while(1)
{
ReadInput(); //读取键盘
Task(); //对温度进行调整(主任务)
}
}
unsigned int Convert(const unsigned int a)
{
return (int) 95*a/0xff; //返回值=输入数据*95/255 (取整数) 返回值范围(0~95)
}
unsigned int ReadT(void)
{
C2=0; //向A/D转换器发出指令
delay(); //等待采样完成
return C2; //返回采样数据数据
}
void Display(const unsigned int x,const unsigned int z)
{
C13=DT3[z/10]; //第三个数码管,显示第二个数字的个位
C14=DT4[z%10]; //第四个数码管,显示第二个数字的十位
C11=DT1[x/10]; //第一个数码管,显示第一个数字的个位
C12=DT2[x%10]; //第二个数码管,显示第一个数字的十位
}
void delay(void) //延时子程序
{
long int i;
for(i=0;i〈0x1f0;++i) ;
}
void delay1(void)
{
long int i;
for(i=0;i<0x81f0;++i) ;
}
unsigned int ReadKeyS(void)
{
unsigned char temp=0;
unsigned char temp1=0;
unsigned char i=0;
temp=C43; //读取键盘第三行
temp=0x1f&C43; //屏蔽高3位
if(temp==0x1f) //如果没有按下,退出
{
return 0xff;
}
temp1=temp; //如有按下,则延时后再读一次,看看是不是抖动
delay();
temp=0x1f&C43;
if(temp!=temp1) //两次读取值不同,说明是抖动,退出
{
return 0xff;
}
if(temp==0x1e) //是第三行第一列的键,则返回K11(在keytable定义)
{
return K11;
}
if(temp==0x1d) //是第三行第二列的键,对Switch做求反操作,则返回K12(在keytable定义)
{
Switch=~Switch;
return K12;
}
}
unsigned int ReadKey(void)
{
unsigned int temp=0;
unsigned int i=0;
while(1)
{
temp=0x1f&C41; //扫描第一行,看有无按下
if(temp!=0x1f) //第一行如有有按下,则跳出while循环
{
i=0x0;
break;
}
temp=0x1f&C42; //扫描第二行,看有无按下
if(temp!=0x1f) //第二行如有有按下,则跳出while循环
{
i=0x40;
break;
}
temp=0x1f&C43; //扫描第三行,看有无按下
if(temp!=0x1f) //第三行如有有按下,则跳出while循环
{
i=0x80;
break;
}
temp=0x1f&C44; //扫描第四行,看有无按下
if(temp!=0x1f) //第四行如有有按下,则跳出while循环
{
i=0xC0;
break;
}
return 0xff; //都没有按下,返回0xff
}
return i|temp; //将高三位和低五位合并
}
unsigned int Decode(unsigned int a) //对键值解码
{
switch(a)
{
case K1:
return 1;
case K2:
return 2;
case K3:
return 3;
case K4:
return 4;
case K5:
return 5;
case K6:
return 6;
case K7:
return 7;
case K8:
return 8;
case K9:
return 9;
case K10:
return 0;
case K11:
return 10;
default:
return 0xff;
}
}
unsigned int ReadInput(void)
{
unsigned int key=0;
unsigned int i=0;
unsigned int temp=0;
key=ReadKeyS(); //读取开关键是否按下
i=Decode(ReadKeyS()); //对读取的键值解码
if(i!=10) //如果不等于开关键(10为开关键解码后的值),则不读数据,退出
{
return 0xff;
}
Display(0,DataTO); //数码管的输入区清零
//delay1();
while(1)
{
key=ReadKey(); //读取第一个键值(十位)
i=Decode(key); //解码
if(i〈10) //如果是前二行的键值,则更新DataTI(临时的输入数据)
{
DataTI=i; //更新DataTI
Display(DataTI,DataTO); //更新数码管,显示输入的数字
break;
}
Task(); //和main()的那个Task()相同,目的是防止输入数据的时候单片机失去控温能力
}
while(1)
{
key=ReadKey(); //读取键值
if(key==0xff) //等键盘松开
break;
Task();
}
while(1)
{
key=ReadKey(); //读取第二个键值(个位)
i=Decode(key); //解码
if(i<10) //如果是前二行的键值,则更新DataTI(临时的输入数据)
{
DataTI=DataTI*10+i; //更新DateTI数据
Display(DataTI,DataTO); //更新数码管,显示输入的数字
TargetT=DataTI; //更新目标温度(正式)
break;
}
Task();
}
return DataTI; //返回输入的数据
}
void Task(void)
{
DataT=ReadT(); //读取A/D的数据
DataTO=Convert(DataT); //把A/D数据转化为10进制的温度数据
if(DataTI>95) //如果温度大于95度,就修正为96度(95度时温度变送器为5V)
{
DataTI=95;
}
tp=TargetT-DataTO; //目标温度和目标温度的差值
if(tp〈0) //如果为负
{
tp=~tp+1; //求差值绝对值
sign=1; //符号位标记为1(1为负数,0为正数)
}
else
{
sign=0; //符号位标记为0(1为负数,0为正数)
}
//分三段控制,0~30度,31~70度,71~95度
if(DataTO〈31); //第一段
{
while(1)
{
if(tp<1) //如果差值为0,则令驱动器空载(0V)
{
C3=0x80;
break;
}
if(tp〈4&&sign==0) //如果差值小于4且为正数,令驱动器加热(但不是满载)
{
C3=170;
break;
}
if(tp<4&&sign==1) //如果差值小于4且为负数,令驱动器冷却(但不是满载)
{
C3=30;
break;
}
if(sign==1) //如果差值大于4且为负数,令驱动器冷却(满载)
{
C3=0;
break;
}
if(sign==0) //如果差值大于4且为正数,令驱动器加热(满载)
{
C3=255;
break;
}
break;
}
}
if(DataTO〈71&&DataTO>30); //第二段
{
while(1)
{
if(tp<1) //如果差值为0,则令驱动器空载(0V)
{
C3=0x80;
break;
}
if(tp〈4&&sign==0) //如果差值小于4且为正数,令驱动器加热(但不是满载)
{
C3=220;
break;
}
if(tp<4&&sign==1) //如果差值小于4且为负数,令驱动器冷却(但不是满载)
{
C3=75;
break;
}
if(sign==1) //如果差值大于4且为负数,令驱动器冷却(满载)
{
C3=0;
break;
}
if(sign==0) //如果差值大于4且为正数,令驱动器加热(满载)
{
C3=255;
break;
}
break;
}
}
if(DataTO<96&&DataTO〉70); //第三段
{
while(1)
{
if(tp〈1) //如果差值为0,则令驱动器空载(0V)
{
C3=0x80;
break;
}
if(tp<4&&sign==0) //如果差值小于4且为正数,令驱动器加热(但不是满载)
{
C3=255;
break;
}
if(tp〈4&&sign==1) //如果差值小于4且为负数,令驱动器冷却(但不是满载)
{
C3=100;
break;
}
if(sign==1) //如果差值大于4且为负数,令驱动器冷却(满载)
{
C3=0;
break;
}
if(sign==0) /如果差值大于4且为正数,令驱动器加热(满载)
{
C3=255;
break;
}
break;
}
}
Display(DataTI,DataTO); //更新数码管显示
return;
}
七、出现的问题分析及解决方法
问题一:我们在用老师的调试程序调试LED数码管时,发现无论怎么弄LED都显示乱码。
解决方法:后来问老师,我得知老师的程序是按照Q1~Q7依次对应数码管的a~g,
同时将程序再按照四个数码管不同的对应线路译出四个独立的表.
问题二:一开始调试闭环程序时我们用的是P调节,显示值确实很接近设定值,但是不能稳定在某一数值,而是不停地变换,比如85、84来回跳.
解决方法:我们改进了程序,将P调节改进成PID调节,虽然在某些温度依然会来回跳,但是比改进算法前要稳定了很多。
问题三:将系统移植到实验模板上时,我们发现最终显示值偏离设定值较大,最大偏差甚至达到6~7度。
解决方法:我将0℃对应变送器输出0V,100℃对应5V。然而当我用万用表测了100℃时变送器输出端的电压值,发现超过5V很多。于是我们重新整定,调节变送器模块的两个电位器,使系统在模板上0℃时输出0V,100℃时输出5V。再次进行闭环调试,问题解决.
八、创新性
创新点一:按键除抖,我们将程序设定为在扫描完一次按下的键后,间隔一定时间再次扫描按下的键,如果两次扫描到的值相等,则确定其按下,否则将其作为误触。
创新点二:我们将键盘K11键作为设定允许键,每次要输入设定值前要先按下K11(即设定允许键)方可进行输入.这样可以防止键盘被误触.
创新点三:真实条件测试,我们在调试台测试完整个系统后,将我们所有的模块移植到实验室模板上,调试。使其成为能够调节真实温度的具有实际作用的系统。
创新点四:程序模块化编写,我们将一些有可能需要经常改动的参数写为头文件形式,如报告后边的附录Ⅰ。这样可以大大减少程序调试时的工作量,比如在调试LED时,如果发现对应的码位不对,只需要在头文件里修改相应的值,代替修改主程序的所有相关参数。同时使程序更精简,运行速度更快。
九、体会与建议
经历了这次长达一年的电子工程设计。我们确实学到了很多很多,从一开始的不知从何下手,到最终的能够在真实模板上测试成功。这一年里,我们碰到过各种问题,比如电源保险烧断,芯片烧裂,程序出现问题无法进行,但是我们运用我们的能力最终将其解决,不管是问老师,问同学,上图书馆、网络查资料。这次大实验使我了解了一个真正具有实用意义的专业系统到底由哪些模块组成,他们之间又是怎么联系起来共同为系统服务的。其次就是锻炼了我们的耐心,一个从下午一点半上到晚上九点的实验,而且要在这期间不停地学知识,动手焊电路,编程。现在课设结束了,我要谢谢老师一年来不光传授我们知识,更锻炼了我们很多优良的特性,让我们提前适应了工作时的那种高效的要求。
除此之外,我也有一些建议。大家在查芯片的时候不要排斥英文的数据手册,因为本来很多芯片就是外国产的,他们更了解这个芯片的特性,而且在查PDF的过程中还能巩固很多专业词汇.其次,大家要敢于动手,不要因为爆了一个电容,被烙铁烫了一下手就对课设胆战心惊,其实现在多经历些总强于以后因此出大错。
【附录Ⅰ】
address.h:
#define C11 XBYTE[0xE000] //第一个数码管
#define C12 XBYTE[0xE100] //第二个数码管
#define C13 XBYTE[0xE200] //第三个数码管
#define C14 XBYTE[0xE300] //第四个数码管
#define C3 XBYTE[0xBF00] //D/A转换器
#define C2 XBYTE[0xDF00] //A/D转换器
#define C40 XBYTE[0x7F00] //键盘
#define C41 XBYTE[0x7000] //键盘第一行
#define C42 XBYTE[0x7100] //键盘第二行
#define C43 XBYTE[0x7200] //键盘第三行
#define C44 XBYTE[0x7300] //键盘第四行
keytable。h:
#define K1 0x1E
#define K2 0x1D
#define K3 0x1B
#define K4 0x17
#define K5 0x0F
#define K6 0x5E
#define K7 0x5D
#define K8 0x5B
#define K9 0x57
#define K10 0x4F
#define K11 0x9E
#define K12 0x9D
digtaltable。h:
#define D10 0x50
#define D11 0xF9
#define D12 0x4A
#define D13 0x49
#define D14 0xE1
#define D15 0x45
#define D16 0x44
#define D17 0xD9
#define D18 0x40
#define D19 0x41
#define D20 0x0C
#define D21 0xAF
#define D22 0xC8
#define D23 0x8A
#define D24 0x2B
#define D25 0x1A
#define D26 0x18
#define D27 0x8F
#define D28 0x08
#define D29 0x0A
#define D30 0x44
#define D31 0xF5
#define D32 0x86
#define D33 0x85
#define D34 0x35
#define D35 0x0D
#define D36 0x0C
#define D37 0xE5
#define D38 0x04
#define D39 0x05
#define D40 0x0C
#define D41 0xEE
#define D42 0x58
#define D43 0x4A
#define D44 0xAA
#define D45 0x0B
#define D46 0x09
#define D47 0x6E
#define D48 0x08
#define D49 0x0A
#define DT1 digtaltable1
#define DT2 digtaltable2
#define DT3 digtaltable3
#define DT4 digtaltable4
code const unsigned int digtaltable1[]; //存放第一个数码管编码的数组
code const unsigned int digtaltable2[]; //存放第二个数码管编码的数组
code const unsigned int digtaltable3[]; //存放第三个数码管编码的数组
code const unsigned int digtaltable4[]; //存放第四个数码管编码的数组
展开阅读全文