基于单片机的温度控制系统课设报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
基于单片机的温度控制系统
摘要:该实验设计基于飞思卡尔MC9S12DG128开发板平台,根据实验任务要求,完成了水温自动控制系统的设计,该系统的温度给定值可由人工通过键盘进行设定,测量温度经过A/D转换由数码管显示,通过PID控制算法对温度进行调节,使温度输出值在给定值上下波动,控制该系统的静态误差为1℃,用LED灯模拟加热强度,并用串口将输出的水温随时间的变化数值发到PC机上。
关键字:飞思卡尔单片机水温控制MC9S12DG128
1、设计题目与设计任务
σ≤;3.温度误要求:1温度连续可调范围是30-150摄氏度;2 超调量20%
<±;4尝试使用能预估大滞后的方法,如史密斯预估,或大林算法;也可差0.5
用PID及改进算法。
内容:1.根据题目的技术要求,画出系统组成的原理框图;2. 给出系统硬件电路图;3.确定温度控制方案;4. 给出控制方法及控制程序;5.整理设计数据资料,课程设计总结,撰写设计计算说明书。
2、前言:
随着电子技术和计算机的迅速发展,计算机测量控制技术拥有操作简单、控制灵活、使用便捷以及性价比较高的优点,从而得到了广泛的应用。
单片机是一种集CPU、RAM、ROM、I/O接口和中断系统等部分于一体的器件,只需要外加电源和晶振就可以实现对数字信息的处理和控制,因此,单片机广泛应用于现代工业控制中。
利用单片机对温度测量控制会大大提高系统的可靠性和准确性。
该设计实验是在实验室完成,实验任务是设计制作一个水温自动控制系统,控制对象为1L净水,容器为搪瓷器皿。
水温由人工通过4*4的键盘设定,并能在环境温度改变时实现对水温的自动控制,采用PWM技术控制电阻丝的加热,加热强度由8个LED小灯模拟,以保持设定的温度基本不变,测量温度经过A/D
转换在4位数码管上显示(保留一位小数),并将温度每秒钟向计算机发送一次。
一、系统设计的功能
该系统的闭环控制系统框图如图1.1所示。
图1.1 水温控制系统结构框图
单片机对温度的测量控制是基于传感器、A/D转换器以及扩展接口和执行机构来进行的。
在闭环过程控制系统中,过程的实时参数由传感器和A/D转换器来进行实时采集,并由单片机自动记录、处理并控制执行机构来进行调节和控制。
图1.2 系统功能框图
1.电源电路:提供HCS12MCU的芯片内部电压,I/O端口和外部供电电压。
2.复位电路:响应各种外部或侦测到的内部系统故障时进行系统复位。
3.晶振电路:产生总线时钟。
4.BDM接口电路:背景调试模式(BDM)是由Freescal半导体公司自定义的片上调试规范[1] 。
5.键盘:用于设定温度。
6.数码管:显示模拟的外界环境温度值。
7.Pt100:热电阻传感器,检测温度。
8.SSR:过零导通继电器,通过继电器的闭合断开对电阻丝进行加热或是不叫热。
二、硬件设计原理及内容
图2.1 实验电路原理图
本系统软件设计包括两个部分:管理程序和控制程序。
管理程序包括LED
显示动态刷新、控制指示灯、处理键盘的扫描和响应、执行中断服务操作等等。
控制程序包括A/D转换、数据采样、数字处理、PID计算等等。
2.1 单片机:单片机是整个控制系统的核心,在此我们用MC9S12DG128可
以提供系统控制所需要的I/0口、中断、定时以及存放中间结果的RAM
电路。
2.2 电源电路:电路图如图2.2所示。
图2.2 电源电路
电源电路部分的C21和C22构成的滤波电路,可以改善系统的电磁兼容性,降低电源波动对系统的影响,增强电路工作的稳定性。
2.3 复位电路:电路图如图2.3所示。
图2.3 复位电路
正常工作时,复位引脚通过4.7k电阻接到电源征集,所以应为高电平。
若按下复位按钮RST1,则复位引脚为低电平,芯片复位。
2.4 晶振电路:电路图如图2.4所示。
图2.4 晶振电路
2.5键盘设置电路:电路图如图2.5所示。
图2.5 键盘电路
用4*4键盘设计温度输入,如上图所示,4条行线分别接P口的PP7~PP4,4条列线分别接P口的PP0~PP3,用扫描法获取键值,例如识别“1”键,编程时将PP0~PP3定义为输入并有上拉电阻,PP4~PP7定义为输出,那么当键按下时对应键值11101110,同理2键对应的键值11011110……编程实现当图中A键按下时,进入温度设定状态,设定完成后,按下B键,确定设定温度[2]。
2.6数码管显示电路:电路图如图2.6所示。
图2.6 数码管显示电路
数码管的a、b、c、d、e、f、g、DP分别接A口的PA0~PA7,上图中的3引脚作为片选线号引脚,外加驱动电路,驱动电路的输入接T口的PT0~PT3,七段式数码管,每一段相当于一个二极管,当PT0为高电平时,三极管导通,DS3为低电平,即被选中,此时判断A口状态,为低电平的那一段亮,高电平的段不亮,例如PORTA=11111110,数码管显示0[3]。
2.7温度检测电路:电路如图2.7所示。
图2.7 温度检测电路
Pt100与前置放大电路组成温度检测电路,Pt100作为温度传感器,测温范围主要在中低温区(-200~630℃),利用导体的电阻值随温度变化的特性
对与温度相关的参量进行检测[4]。
将检测到的温度小信号,经过前置运算放大器进行信号放大。
三、系统软件设计流程
系统软件设计流程图如图3.1所示。
其中初始化包括串口初始化、键盘初始化、数码管初始化、A/D转换初始化、PWM初始化。
继电器控制为当检测温度小于设定温度,使继电器闭合给电阻丝加热;当检测温度大于设定温度,使继电器断开,停止给电阻丝加热。
图3.1 系统软件流程图
四、调试过程及数据
在开始做这个设计的时候,先是把系统的每一部分都分成不同模块,每一个模块先单独作为一个工程建立,每一个模块调试成功之后才将各部分组合在一起,最终调试成为一个系统的。
系统的模块分为:SCI串行口输入输出模块、LED 数码管显示模块、KB键盘输入模块、AD转换输入模块、PWM模块、定时器模块。
4.1 SCI串口调试
编写串口程序,包括SCI的初始化函数、发送数据函数、接收数据函数,通过BDM将编好的程序下载到单片机里,使程序编译通过,在SCI的调试主程序中通过输入字符,并让其在电脑自带的超级终端上显示,如果超级终端成功显示我们在键盘上输入的字符,则说明SCI串行口模块可以调用,如果显示不成功则需要继续对程序进行修改和编译。
问题:键盘输入的字符不在超级终端显示。
解决过程:首先直接在SCI调试主程序中直接调用发送数据函数,但是超级终端上仍然没有显示,然后检查程序发现所设SCI0BDL过大,因为实
验板的晶振16M,但是单片机MC9S12DG128中未启用锁相环,故
单片机的内部总线实际上只有16M/2,所以在串行口波特率要求
9600时,需要在程序的串口初始化中将SCI0BDL=0X80改为
SCI0BDL=0X34。
4.2 数码管调试
编程实现,用四个共阴极8段数码管显示要显示的四位数。
问题1:数码管不显示
解决过程:检查硬件连接,发现所用引脚和课本给出的不一致,通过修改连线,数码管显示正常数字。
问题2:数码管显示亮度不一致,现象如下图所示。
解决过程:发现延时时间比较短,增加延时时间,显示的亮度基本接近但还是有差异,因此该用定时中断的方式,此问题彻底得到解决,正常显示如下图所示。
4.3 键盘输入模块调试
在理解了4*4矩阵键盘的编程原理后,自己结合课本成功编译通过键盘程序。
将按键值通过实验板上的八个LED等显示。
问题:有些按键对小灯没有影响。
解决过程:单步运行程序,发现那些对小灯没有影响的按键不会使PTP口的值改变,于是换个实验板,运行正常。
4.4 A/D转换输入调试
在编译通过AD转换程序后,联系数码管显示模块,用一个电位器的检测采样,用单片机的AD转换通道AN6输入采样信号,将其转化为30~150可变
数字,用以模拟温度30~150度的变化,并在数码管上显示。
问题:改变电位器,但是显示的值不变
解决过程:检查硬件电路,发现0~5V输出接到了AD7口上,而程序中采样信号是由A/D转换通道AN6输入的,改变接线,问题得到解决。
五、实验结果与心得
5.1 最终调试结果:1.通过键盘能够设定温度值;2.用电位器模拟温度变化时
数码管及时显示30到150摄氏度的变化,并且数码管的最后一位为小数部分;
3.用8个LED灯模拟加热强度。
4.此系统为自动控制系统,通过PID对其进
行调节,使输出温度在设定温度范围内波动。
5.2 实验心得:
通过本次的设计性实验,实验过程中设计到了很多学科的东西,例如计算机控制技术、检测技术、C语言、模拟电子技术、最主要的是嵌入式系统。
实验前的准备工作是我学会了Altium Designer的使用,自己能过完成简单电路的绘制。
这次设计实验虽然只是模拟一个简单的水温控制系统,但是设计思路是相同的,由于老师给出了例子,这个我们提供了很大的帮助,使我们更快的着手实验,实验过程中遇到了很多问题,也使我认识到自己在嵌入式系统设计和编程方面的不足,让我意识到即使再简单的任务,我都要全力以赴的去完成,而不是在学习上投机取巧,要踏踏实实,一步一个脚印的去学习。
或许学习的过程很漫长、很艰辛,但是会受益匪浅。
对于一个系统而言它是由多个功能模块组成的,实验中将整个系统分解开来,将每一个涉及的功能模块化,然后又逐步整合逐渐向最终目标靠拢的这种思维,这次实验的思维方式和实验过程,为复杂的系统设计提供了很好的借鉴作用。
六、参考资料
[1]嵌入式系统——使用HCS12微控制器的设计与应用.王宜怀P30 最小系统设计
[2]嵌入式系统——使用HCS12微控制器的设计与应用.王宜怀P134 键盘处理函数
[3]嵌入式系统——使用HCS12微控制器的设计与应用.王宜怀P139 扫描法LED显示
[4]检测技术——使用现代检测技术.金伟P46热电阻传感器
[5]计算机技术——使用计算机控制技术.顾德英P107 数字PID控制算法的计算机实现
附录:
1、源程代码
头文件
#include <mc9s12dg128.h>
//----------------------------------------------/ /
uchar Flag_Send = 0,Flag_ADC = 0; uint NUM = 0;
uint Temperature_Set = 600;
uchar RCVData[16];
//----------------------------------------------/ /
#define ReSendStatusR SCI0SR1
#define ReTestBit 5
#define SendTestBit 7
#define ReSendDataR SCI0DRL
#define EnableSCIReInt SCI0CR2 |= 0x20
#define DisableSCIReInt SCI0CR2 &= 0xdf
//----------------------------------------------/ /
#define LEDduan PORTA
#define LEDduan_DDRA DDRA #define LEDwei PTT
#define LEDwei_DDRT DDRT
//----------------------------------------------/ /
#define KB_P PTP
#define KB_D DDRP
#define KB_PE PERP
#define KB_PS PPSP
#define KB_IE PIEP
#define KB_IF PIFP
//----------------------------------------------/ /
#define SCFBit 7
#define kp 10000
#define ki 1000
#define kd 2000
//----------------------------------------------/ /
uchar KB_Table[33] =
{
0xee,'1',0xde,'2',0xbe,'3',0x7e,'4',
0xed,'5',0xdd,'6',0xbd,'7',0x7d,'8',
0xeb,'9',0xdb,'0',0xbb,'A',0x7b,'B',
0xe7,'C',0xd7,'D',0xb7,'E',0x77,'F',
0x00
};
uchar Duantable[11] =
{
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x00
};
uchar Weitable[4] = {0xf7,0xfb,0xfd,0xfe};
uint AD_wData = 0;
uchar LEDbuf[4] = {'0','0','0','0'};
//----------------------------------------------/ /
void AD_Init(void);
void ADCInit(void);
void Sample(void);
void KB_Init(void);
void Key_Del(void);
void LEDInit(void);
void LEDShow(uchar *data,uchar wei); void LEDShow1(uchar i,uchar c);
void SCIInit(void);
void SCISend1(uchar data);
void SCISendN(uchar n,uchar ch[]) ; void SendToSCI(void);
void ECT_Init(void);
void interrupt Int_TimerOverFlow(void);
uint ADCvalue(uchar channel);
byte PID(dword result);
uchar KB_Scan1(void);
uchar KB_Def(uchar KB_valve); uchar KB_ScanN(uchar KB_count);
主函数
#include <hidef.h>
#include <shiyan.h>
//----------------------------------------------/ /
void LEDInit(void) //LED初始化
{
LEDduan_DDRA = 0xff; //段选PA口输出
LEDwei_DDRT |= 0x0f; //位选PT(低4位)位口输出
}
//----------------------------------------------/ /
void LEDShow(uchar *shuzi,uchar wei)//LED显示
{
uchar temp = 0;
for(wei=0;wei<=3;wei++)
//循环显示4位数码管
{
temp = shuzi[wei] - '0';
LEDShow1(3 - wei,temp); //调用1位显示函数
}
}
//----------------------------------------------/ /
void LEDShow1(uchar wei,uchar data) //1位数码管显示
{
LEDwei = Weitable[wei];
if(wei==2)
//第2位数码管加小数点
{
LEDduan = (~Duantable[data])&0x7f;
}
else
{
LEDduan = ~Duantable[data];
}
} //----------------------------------------------/ /
void KB_Init(void) //矩阵键盘I/O口初始化
{
KB_P = 0x00;
KB_D = 0x0f;
KB_PE = 0xf0;
KB_PS = 0x00;
KB_IE = 0x00;
KB_IF = 0xff;
}
//----------------------------------------------/ /
uchar KB_Scan1(void) //矩阵键盘扫描
{
uchar line ,i ,temp;
line = 0xfe; //第一行扫描
for(i=0;i<4;i++)
//循环扫描矩阵键盘
{
temp = KB_P;
temp |= 0x0f;
KB_P = temp & line;
asm("nop");asm("nop");asm("nop");
temp = KB_P;
temp &= 0xf0;
if(temp!=0xf0)
{
temp = KB_P;
break;
}
else
line = (line<<1) | 0x01; //准备扫描下一行
}
if(i==4)
{
temp = 0xff;
}
return (temp);
}
//----------------------------------------------/ /
uchar KB_Def(uchar KB_valve)
{
uchar KeyPress=0;
uchar i=0,j=0;
for(;;)
{
j = KB_Table[i];
if(j == 0)
{
KeyPress = 0xff;
break;
}
else
{
if(j == KB_valve)
{
KeyPress = KB_Table[i+1];
break;
}
else
{
i += 2;
}
}
}
return KeyPress;
}
//----------------------------------------------/ /
uchar KB_ScanN(uchar KB_count) {
uchar
i,KB_value_last,KB_value_now;
if(KB_count == 0 || KB_count == 1) {
return KB_Scan1();
}
KB_value_now = KB_value_last = KB_Scan1();
for(i = 0; i < KB_count - 1; i++)
{
if(KB_value_now == KB_value_last)
{
return KB_value_now;
}
else
{
KB_value_last = KB_value_now;
}
}
return 0xff;
}
//----------------------------------------------/ /
uchar Get_Key(void)
{
uchar keynum,temp;
temp = KB_ScanN(8);
if(temp != 0xff)
{
keynum = KB_Def(temp);
while(KB_ScanN(8)!=0xff);
}
else
{
keynum = 0xff;
}
return keynum;
}
//----------------------------------------------/ /
void Key_Del(void)
{
uchar keynum;
keynum = Get_Key();
if (keynum == 'A')
{
LEDbuf[3] = Temperature_Set/1000+'0';
LEDbuf[2] = (Temperature_Set%1000)/100+'0';
LEDbuf[1] =
(Temperature_Set%100)/10+'0';
LEDbuf[0] = Temperature_Set%10+'0';
for(;;)
{
keynum = Get_Key();
if(keynum == 'B')
{
Temperature_Set = (LEDbuf[3] - '0')*1000 + (LEDbuf[2] - '0')*100 + (LEDbuf[1] - '0')*10 + (LEDbuf[3] - '0');
break;
}
if((keynum >= '0') && (keynum <= '9'))
{
LEDbuf[3] = LEDbuf[2];
LEDbuf[2] = LEDbuf[1];
LEDbuf[1] = LEDbuf[0];
LEDbuf[0] = keynum;
}
}
}
}
//----------------------------------------------/ /
void AD_Init(void)
{
ATD0TEST1 = 0b00000000; //禁止特殊通道
ATD0CTL2 = 0b11000010; //快速清除模式,完成中断允许
ATD0CTL3 = 0b00001000; //队列长度为1
ATD0CTL4 = 0b01000011; //ATDclock=1M
ATD0CTL5 = 0b10100110;
}
//----------------------------------------------/ /
void ADCInit(void)
{
ATD0CTL2 = 0XC0;
ATD0CTL3 = 0X0B;
ATD0CTL4 = 0X07;
ATD0CTL4 &= 0X7F;
}
//----------------------------------------------/ /
uint ADCvalue(uchar channel)
{
uint temp;
ATD0CTL5 = (0x20 | channel);
for(;;)
{
if((ATD0STAT0&(1 << SCFBit)) != 0)
{
temp = ATD0DR0;
temp = (temp >> 6);
break;
}
}
return temp;
}
//----------------------------------------------/ /
byte PID( dword result)
{
int ek1,Pik1;
int ek,Ppk,Pik,Pdk,Pk;
unsigned char tmp;
ek = (int)result - Temperature_Set; ek1 = ek;
Ppk = kp * ek;
Pik = ki * ek + Pik1;
Pik1 = Pik;
Pdk = kd * (ek - ek1);
Pk = Ppk + Pik + Pdk;
if(Pk>0)
{
if(Pk>25500)
{
tmp = 255;
}
else
{
tmp = (byte)(Pk / 100);
}
return tmp;
}
else
{
tmp = 0;
return tmp;
}
}
//----------------------------------------------/ /
void Sample(void)
{
byte ge,shi,bai,point;
dword result;
if(Flag_ADC == 0xff)
{
result=(dword)(AD_wData)*1000/1023 ;
bai = (byte)(result/1000+'0');
shi = (byte)((result%1000)/100+'0');
ge = (byte)((result%100)/10+'0');
point =(byte)(result%10+'0');
if(bai == '0')
{
LEDbuf[3] = '9'+1;
if(shi == '0')
{
LEDbuf[2] = '9'+1;
}
else
{
LEDbuf[2] = shi;
}
}
else
{
LEDbuf[3] = bai;
LEDbuf[2] = shi;
}
LEDbuf[1] = ge;
LEDbuf[0] = point;
if(result>Temperature_Set+1)
{
PWMDTY3++;
}
if(result<Temperature_Set-1)
{
PWMDTY3--;
}
PORTB = 0xff << PWMDTY3 / 32;
Flag_ADC = 0x00;
}
}
//----------------------------------------------/ /
void ECT_Init(void)
{
TSCR2_PR = 0; //prescale factor is 0, bus clock=8Mhz
TSCR2_TOI = 1; //timer overflow interrupt enable
TSCR1_TEN = 1; //timer enable }
//////////////////////////////////////////////////
void SCIInit(void)
{
SCI0BDH = 0x00;
SCI0BDL = 0x34;
SCI0CR1 = 0x00;
SCI0CR2 = 0x0c;
}
//----------------------------------------------/ /
void SCISend1(uchar o)
{
for(;;)
{
if((ReSendStatusR & (1 << SendTestBit)) != 0)
{
ReSendDataR = o;
break;
}
}
}
//----------------------------------------------/ /
void SCISendN(uchar n,uchar ch[]) {
uchar i;
for(i=0;i<n;i++)
{
SCISend1(ch[i]);
}
}
//----------------------------------------------/ /
void SendToSCI(void)
{
uchar temp[4];
if( Flag_Send==0xff)
{
if(LEDbuf[3]=='9'+1)
{
temp[3]=' ';
}
else
{
temp[3]= LEDbuf[3];
}
if(LEDbuf[2]=='9'+1)
{
temp[2]=' ';
}
else
{
temp[2]= LEDbuf[2];
}
temp[1] = LEDbuf[1];
temp[0] = LEDbuf[0];
SCISend1(temp[3]);
SCISend1(temp[2]);
SCISend1(temp[1]);
SCISend1('.');
SCISend1(temp[0]);
SCISend1(0x0d);
SCISend1(0x0a);
Flag_Send = 0;
}
}
//----------------------------------------------/ /
void Pwm_Init(void)
{
PWME_PWME3 = 0; PWMPRCLK= 0X22; //clockA=总线时钟/4
PWMSCLA = 1; //clockSA=clockA/(2*PWMSCLA) PWMSCLB = 1;
PWMCLK = 0X04; //通道5选用clockSA为时钟源PWMPOL = 0X04; //通道5首先输出高电平
PWMCAE = 0X00; //输出左对齐
PWMCTL = 0X00; //非级联模式
PWMDTY3 = 255;
PWMPER3 = 255; //通道5的周期寄存器
PWME_PWME3 = 1;
}
//----------------------------------------------/ /
void main(void)
{
DDRB = 0XFF;
PORTB = 0X00;
ECT_Init();
LEDInit();
ADCInit();
AD_Init();
SCIInit();
KB_Init();
Pwm_Init();
EnableSCIReInt;
EnableInterrupts;
for(;;)
{
Key_Del();
Sample();
SendToSCI();
}
}
//----------------------------------------------//
void interrupt Int_TimerOverFlow(void)
{
static unsigned int i,j,k;
TFLG2_TOF = 1; //clear timer overflow flag
if((++i)>3)
{
i = 0;
}
LEDShow1(3 - i,LEDbuf[i]-'0');
if(j++>500)
{
j = 0;
Flag_Send = 0xff;
}
if( k++ >10)
{
k = 0;
Flag_ADC = 0xff;
}
}
//----------------------------------------------//
void interrupt ATD_Interrupt(void)
{
AD_wData = ATD0DR0; //Read out the Result Register }。