《自动控制原理》课程设计
目录
1. 系统设计方案3
1.1方案一3
1.2 方案二3
1.3方案三3
1.4方案的比较和确定3
2.系统硬件部分设计3
2.1 系统硬件电路图3
2.1.1 单片机AT89C524
2.1.2单片机与LCD通信连接4
2.1.3 其他电路模块及总电路5
3系统软件部分5
3.1系统的主流程图如下5
4.温度控制系统PID调节6
4.1 PID控制传递函数6
4.2 PID参数调节方法6
4.3 PID参数设定6
5.实验与调试7
6.总论8
附 录8
参 考 文 献24
1.1方案一 选用铂电阻温度传感器。此类温度传感器线性度、稳定性等方面性能都很好,但其成本较高。 1.2 方案二 采用热敏电阻。选用此类元器件有价格便宜的优点,但由于热敏电阻的非线性特性会影响系统的精度。 1.3方案三 采用DS18B20温度传感器。DS18B20是DALLAS公司生产的一线式数字温度传感器,具有3引脚封装形式;温度测量范围为-55℃~+125℃,可编程为9位~12位A/D转换精度,测温分辨率可达0.0625℃,被测温度用符号扩展的16位数字量方式串行输出远端引入 1.4方案的比较和确定 比较以上三种方案,方案三具有具有体积小、质量轻、线形度好、性能稳定等优点其各方面特性都满足此系统的设计要求的优点,因此选用方案三。 硬件设计包含DS18B20模块,1602液晶显示模块,继电器模块,键盘输入模块和声光报警模块,DS18B20可以被编程,所以箭头是双向的,CPU(89C52)首先写入命令给DS18B20,然后DS18B20开始转换数据,转换后通89C52来处理数据。数据处理后的结果就显示到1602液晶上。 2.1系统硬件电路图 2.1.1 单片机STC89C52 2.1.2单片机与LCD通信连接 2.1.3 其他电路模块及总电路 软件设计的部分采用分层模块化设计,主要有键盘扫描、按键处理程序、数码管显示程序、继电器控制程序、温度信号处理程序、超温报警程序。另外以 AT89C52 单片机为控制核心。利用PID 控制算法提高了水温的控制精度使用PID 控制算法实施自动控制系统具有控制参数精度高、反映速度快和稳定性好的特点。 3.1系统的主流程图如下
4.温度控制系统PID调节 4.1 PID控制传递函数 通过热电偶采集的被测温度偏离所希望的给定值时,PID控制可根据测量信号与给定值的偏差进行比例(P)、积分(I)、微分(D)运算,从而输出某个适当的控制信号给执行机构,促使测量值恢复到给定值,达到自动控制的效果。 4.2 PID参数调节方法及参数设定 PID模块的温度控制精度主要受P、I、D这三个参数影响。其中P代表比例,I代表积分,D代表微分。
比例运算(P)
比例控制是建立与设定值(SV)相关的一种运算,并根据偏差在求得运算值(控制输出量)。如果当前值(PV)小,运算值为100%。如果当前值在比例带内,运算值根据偏差比例求得并逐渐减小直到SV和PV匹配(即,直到偏差为0),此时运算值回复到先前值(前馈运算)。若出现静差(残余偏差),可用减小P方法减小残余偏差。如果P太小,反而会出现振荡。
积分运算(I)
将积分与比例运算相结合,随着调节时间延续可减小静差。积分强度用积分时间表示,积分时间相当于积分运算值到比例运算值在阶跃偏差响应下达到的作用所需要的时间。积分时间越小,积分运算的校正时间越强。但如果积分时间值太小,校正作用太强会出现振荡。
微分运算(D)
比例和积分运算都校正控制结果,所以不可避免地会产生响应延时现象。微分运算可弥补这些缺陷。在一个突发的干扰响应中,微分运算提供了一个很大的运算值,以恢复原始状态。微分运算采用一个正比于偏差变化率(微分系数)的运算值校正控制。微分运算的强度由微分时间表示,微分时间相当于微分运算值达到比例运算值在阶跃偏差响应下达到的作用所需的时间。微分时间值越大,微分运算的校正强度越强。
5.实验与调试 用继电器模块来控制200W“热得快”来对1升水进行加热,用键盘设定需要加热温度值,观察1602所显示的稳定水温和环境温度降低时温度控制的静态误差。多次调试并合设定PID参数来完善系统。 实物图: 6.总论 首先,通过本次应用系统设计,在很大程度上提高了我们的独立思考能力和专业知识相信在接下来的日子我们能在已有的基础上做得更好。我们所设计的该系统主要根据目前省能源的发展趋势和国内实际的应用特点和要求,采用了自动化的结构形式,实现对水温的自动检测和控制。该系统的主要特点是: 1)适用性强,用户只需对界面参数进行设置并启动系统正常运行便可满足不同用户水温的要求,实现对水温的实时监控。避免了电力力资源的浪费,节省了能源。 2)将单片机以及温度传感器引入对水温的分析和处理中,单片机控制决策无需建立被控对象的数学模型,系统的适应性强,适合对非线性、时变、滞后系统的控制对水温控制系统进行控制。 3)系统成本低廉,操作非常简单,可扩展性强,只要稍加改变,即可增加其他使用功能。 通过对本设计的思考,更加加深了我们对单片机的认识,熟练了对单片机的控制,更对当前的温度传感器有了更深刻的认识与了解,但是由于此系统依赖温度传感器,因而对温度传感器的稳定性,线性等诸多方面有着严格的要求,但是传感器的性能越好,相对而言其价格也就越高,因而在此设计中,温度传感器觉的还是存在遗憾,由于温度计的使用温度有限所以水温不能达到90℃以上。最后由于时间紧迫,本设计还有诸多地方需要改进,我们会在接下来的时间里继续完善该设计,以期做的更好。 附录: 部分程序: //main.c: - #include<reg51.h>
- #include"lcd.h"
- #include"temp.h"
- #include"pid.h"
-
- sbit c1=P2^6;
- sbit c2=P2^7;
- sbit beep=P1^3;
-
- sbit K3=P3^2;
- sbit K4=P3^3;
-
- void IntConfiguration();
- void Delay(unsigned int n);
- unsigned char KeyValue=0;
-
- extern float set_temper;
-
-
- /*******************************************************************************
- * 函数名 : main
- * 函数功能 : 主函数
- * 输入 : 无
- * 输出 : 无
- *******************************************************************************/
-
- void main()
- {
- extern struct PID spid; // PID Control Structure
-
- LcdInit();
- IntConfiguration();
-
- PIDInit ( &spid );
- spid.Proportion = 10; // Set PID Coefficients
- spid.Integral = 8;
- spid.Derivative =6;
- spid.SetPoint = 100; // Set PID Setpoint
-
- beep=1;
-
- TMOD=0x21;
- TH0=0x2f;
- TL0=0x40;
- //PCON=0X80; //波特率加倍
- //TH1=0XF3; //计数器初始值设置,注意波特率是4800的
- //TL1=0XF3;
- EA=1;
- ET0=1;
- //ES=1;
- TR0=1;
- TR1=1;
-
- c1=0;
- c2=0;
-
- P1=0XFF;
-
- while(1)
- {
- LcdDisplay(Ds18b20ReadTemp());
- LcdDisplay2(set_temper);
- // LcdDisplay3(realtmp);
- /* if((set_temper-realtmp)<1&&(realtmper-set_temp)<1)
- {
- beep=0;
- }
- else
- beep=1; */
- compare_temper();
- // SBUF=Ds18b20ReadTemp();
- // Delay1ms(1);//1s钟刷一次
- }
- }
-
-
- void IntConfiguration()
- {
- //设置INT0
- IT0=1;//跳变沿出发方式(下降沿)
- EX0=1;//打开INT0的中断允许。
- //设置INT1
- IT1=1;
- EX1=1;
-
- EA=1;//打开总中断
- }
- /*******************************************************************************
- * 函数名 : Delay(unsigned int n)
- * 函数功能 : 延时
- * 输入 : n
- * 输出 : 无
- *******************************************************************************/
-
- void Delay(unsigned int n) //延时50us误差 0us
- {
- unsigned char a,b;
- for(;n>0;n--)
- {
- for(b=1;b>0;b--)
- for(a=22;a>0;a--);
- }
- }
- /*******************************************************************************
- * 函数名 : Int0() interrupt 0
- * 函数功能 : 外部中断0的中断函数
- * 输入 : 无
- * 输出 : 无
- *******************************************************************************/
-
- void Int0() interrupt 0 //外部中断0的中断函数
- {
- Delay(1); //延时消抖
- if(K3==0)
- {
- while(K3==0);
- set_temper++;
- }
- if(set_temper>60.00)
- set_temper=60.00;
- }
- /*******************************************************************************
- * 函数名 : Int1() interrupt 2
- * 函数功能 : 外部中断1的中断函数
- * 输入 : 无
- * 输出 : 无
- *******************************************************************************/
-
- void Int1() interrupt 2 //外部中断1的中断函数
- {
- Delay(1); //延时消抖
- if(K4==0)
- {
- while(K4==0);
- set_temper--;
- }
- if(set_temper<40.00)
- set_temper=40.00;
- }
-
-
-
- //pid.c
- #include"pid.h"
- #include"temp.h"
-
- struct PID spid; // PID Control Structure
-
- unsigned int rout; // PID Response (Output)
- unsigned int rin; // PID Feedback (Input)
-
-
-
- int i=0,j=0,flag2=0,tem=0;
-
- sbit output=P1^6;
-
-
- unsigned char flag,flag_1=0;
- unsigned char high_time,low_time,count=0;//占空比调节参数
- float set_temper=40;
-
-
-
- void PIDInit (struct PID *pp)
- {
- high_time=50;
- low_time=50;
- memset ( pp,0,sizeof(struct PID));
-
- }
- /*====================================================================================================
- PID计算部分
- =====================================================================================================*/
- unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )
- {
- unsigned int dError,Error;
- Error = pp->SetPoint - NextPoint; // 偏差
- pp->SumError += Error; // 积分
- dError = pp->LastError - pp->PrevError; // 当前微分
- pp->PrevError = pp->LastError;
- pp->LastError = Error;
- return (pp->Proportion * Error//比例
- + pp->Integral * pp->SumError //积分项
- + pp->Derivative * dError); // 微分项
- }
- /***********************************************************
- 温度比较处理子程序
- ***********************************************************/
- void compare_temper()
- {
- unsigned char i;
- extern float realtmp;
- extern unsigned int s;
-
- if(set_temper>realtmp)
- {
- if(set_temper-realtmp>1)
- {
- high_time=100;
- low_time=0;
- //P1=0XFF;
- //mod1=0;
- }
- else
- {
- //P1=0XFF;
- //mod2=0;
- for(i=0;i<10;i++)
- { Ds18b20ReadTemp();
- rin = s; // Read Input
- rout = PIDCalc ( &spid,rin ); // Perform PID Interation
- }
-
- if (high_time<=100)
- high_time=(unsigned char)(rout/800);
- else
- high_time=100;
- low_time= (100-high_time);
- }
- }
- else if(set_temper<=realtmp)
- {
- if(realtmp-set_temper>0)
- {
- //P1=0XFF;
- //mod3=0;
- high_time=0;
- low_time=100;
- //mod2=0;
- }
- else
- {
- //P1=0XFF;
- //mod4=0;
- for(i=0;i<10;i++)
- { Ds18b20ReadTemp();
- rin = s; // Read Input
- rout = PIDCalc ( &spid,rin ); // Perform PID Interation
- }
- if (high_time<100)
- high_time=(unsigned char)(rout/10000);
- else
- high_time=0;
- low_time= (100-high_time);
- }
- }
- // else
- // {}
- }
- /*****************************************************
- T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
- ******************************************************/
- void serve_T0() interrupt 1 using 1
- {
- j++;
- if(++count<=(high_time))
- {
- output=1;
- i++;
- }
- else if(count<=100)
- {
- output=0;
- }
- else
- count=0;
- if(j==1000)
- {j=0;flag2=1;tem=i;i=0; }
- TH0=0x2f;
- TL0=0xe0;
- }
- /*****************************************************
- 串行口中断服务程序,用于上位机通讯
- ******************************************************/
- void serve_sio() interrupt 4
- {
- /* EA=0;
- // flag2=0;
- while(!TI); //等待发送数据完成
- TI=0;
- EA=1; */
- }
-
-
-
- //temp.c
- #include"temp.h"
-
- int temp=0;
- unsigned int s;
- //float set_temp=27.00;
- /*******************************************************************************
- * 函数名 : Delay1ms
- * 函数功能 : 延时函数
- * 输入 : 无
- * 输出 : 无
- *******************************************************************************/
-
- void Delay1ms(unsigned int y)
- {
- unsigned int x;
- for(y;y>0;y--)
- for(x=110;x>0;x--);
- }
- /*******************************************************************************
- * 函数名 : Ds18b20Init
- * 函数功能 : 初始化
- * 输入 : 无
- * 输出 : 初始化成功返回1,失败返回0
- *******************************************************************************/
-
- unsigned char Ds18b20Init()
- {
- unsigned int i;
- DSPORT=0; //将总线拉低480us~960us
- i=70;
- while(i--);//延时642us
- DSPORT=1; //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
- i=0;
- while(DSPORT) //等待DS18B20拉低总线
- {
- i++;
- if(i>5000)//等待>5MS
- return 0;//初始化失败
- }
- return 1;//初始化成功
- }
-
- /*******************************************************************************
- * 函数名 : Ds18b20WriteByte
- * 函数功能 : 向18B20写入一个字节
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
- void Ds18b20WriteByte(unsigned char dat)
- {
- unsigned int i,j;
- for(j=0;j<8;j++)
- {
- DSPORT=0; //每写入一位数据之前先把总线拉低1us
- i++;
- DSPORT=dat&0x01; //然后写入一个数据,从最低位开始
- i=6;
- while(i--); //延时68us,持续时间最少60us
- DSPORT=1; //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
- dat>>=1;
- }
- }
- /*******************************************************************************
- * 函数名 : Ds18b20ReadByte
- * 函数功能 : 读取一个字节
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
-
- unsigned char Ds18b20ReadByte()
- {
- unsigned char byte,bi;
- unsigned int i,j;
- for(j=8;j>0;j--)
- {
- DSPORT=0;//先将总线拉低1us
- i++;
- DSPORT=1;//然后释放总线
- i++;
- i++;//延时6us等待数据稳定
- bi=DSPORT; //读取数据,从最低位开始读取
- /*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
- byte=(byte>>1)|(bi<<7);
- i=4; //读取完之后等待48us再接着读取下一个数
- while(i--);
- }
- return byte;
- }
- /*******************************************************************************
- * 函数名 : Ds18b20ChangTemp
- * 函数功能 : 让18b20开始转换温度
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
- void Ds18b20ChangTemp()
- {
- Ds18b20Init();
- Delay1ms(1);
- Ds18b20WriteByte(0xcc); //跳过ROM操作命令
- Ds18b20WriteByte(0x44); //温度转换命令
- Delay1ms(100);
- }
- /*******************************************************************************
- * 函数名 : Ds18b20ReadTempCom
- * 函数功能 : 发送读取温度命令
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
- void Ds18b20ReadTempCom()
- {
-
- Ds18b20Init();
- Delay1ms(1);
- Ds18b20WriteByte(0xcc); //跳过ROM操作命令
- Ds18b20WriteByte(0xbe); //发送读取温度命令
- }
- /*******************************************************************************
- * 函数名 : Ds18b20ReadTemp
- * 函数功能 : 读取温度
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
- int Ds18b20ReadTemp()
- {
- unsigned char tmh,tml;
- Ds18b20ChangTemp(); //先写入转换命令
- Ds18b20ReadTempCom(); //然后等待转换完后发送读取温度命令
- tml=Ds18b20ReadByte(); //读取温度值共16位,先读低字节
- tmh=Ds18b20ReadByte(); //再读高字节
- temp=tmh;
- temp<<=8;
- temp|=tml;
- s=(unsigned int)(tml&0x0f);
- s=(s*100)/16;
- return temp;
- }
-
-
-
- //LCD.c
- #include"lcd.h"
- /*******************************************************************************
- * 函数名 : Delay1us
- * 函数功能 : 延时函数,延时1us
- * 输入 : a
- * 输出 : 无
- *******************************************************************************/
- float realtmp=0;
-
- void Delay1us(unsigned int a)
- {
- unsigned int b,c;
- for(c=a;c>0;c--)
- for(b=110;b>0;b--);
- }
- /*******************************************************************************
- * 函数名 : LcdWriteCom
- * 函数功能 : 向LCD写入一个字节的命令
- * 输入 : com
- * 输出 : 无
- *******************************************************************************/
-
- void LcdWriteCom(unsigned char com) //写入命令
- {
- RS=0;
- RW=0;
- GPIO_LCD=com;
- Delay1us(10);
- LCDE=1;
- Delay1us(10);
- LCDE=0;
- }
- /*******************************************************************************
- * 函数名 : LcdWriteData
- * 函数功能 : 向LCD写入一个字节的数据
- * 输入 : dat
- * 输出 : 无
- *******************************************************************************/
-
- void LcdWriteData(unsigned char dat) //写入数据
- {
- RS=1;
- RW=0;
- GPIO_LCD=dat;
- Delay1us(10);
- LCDE=1;
- Delay1us(10);
- LCDE=0;
- }
- /*******************************************************************************
- * 函数名 : LcdInit()
- * 函数功能 : 初始化LCD屏
- * 输入 : 无
- * 输出 : 无
- *******************************************************************************/
-
- void LcdInit() //LCD初始化子程序
- {
- LcdWriteCom(0x38); //开显示
- LcdWriteCom(0x0c); //开显示不显示光标
- LcdWriteCom(0x06); //写一个指针加1
- LcdWriteCom(0x01); //清屏
- LcdWriteCom(0x80); //设置数据指针起点
- }
-
- void LcdDisplay(int temp) //lcd显示
- {
-
- unsigned char datas[] = {0, 0, 0, 0, 0}; //定义数组
- float tp;
- //初始化LCD
- if(temp< 0) //当温度值为负数
- {
- LcdWriteCom(0x80); //写地址 80表示初始地址
- LcdWriteData('-'); //显示负
- //因为读取的温度是实际温度的补码,所以减1,再取反求出原码
- temp=temp-1;
- temp=~temp;
- tp=temp;
- temp=tp*0.0625*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算加上0.5,还是在小数点后面。
-
- }
- else
- {
- LcdWriteCom(0x80); //写地址 80表示初始地址
- LcdWriteData('+'); //显示正
- tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
- //如果温度是正的那么,那么正数的原码就是补码它本身
- temp=tp*0.0625*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算加上0.5,还是在小数点后面。
- realtmp=(float)temp/100;
- }
- datas[0] = temp / 10000;
- datas[1] = temp % 10000 / 1000;
- datas[2] = temp % 1000 / 100;
- datas[3] = temp % 100 / 10;
- datas[4] = temp % 10;
-
- LcdWriteCom(0x82); //写地址 80表示初始地址
- LcdWriteData('0'+datas[0]); //百位
-
-
- LcdWriteCom(0x83); //写地址 80表示初始地址
- LcdWriteData('0'+datas[1]); //十位
-
- LcdWriteCom(0x84); //写地址 80表示初始地址
- LcdWriteData('0'+datas[2]); //个位
-
- LcdWriteCom(0x85); //写地址 80表示初始地址
- LcdWriteData('.'); //显示 ‘.’
-
- LcdWriteCom(0x86); //写地址 80表示初始地址
- LcdWriteData('0'+datas[3]); //显示小数点
-
- LcdWriteCom(0x87); //写地址 80表示初始地址
- LcdWriteData('0'+datas[4]); //显示小数点
-
- LcdWriteCom(0x88); //写地址 80表示初始地址
- LcdWriteData('C');
- }
-
- void LcdDisplay2(float num)
- {
- unsigned char datas[] = {0, 0, 0, 0, 0}; //定义数组
- int temp;
- // LcdInit(); //初始化LCD
-
- LcdWriteCom(0xC0); //写地址 80表示初始地址
- LcdWriteData('+'); //显示正
- temp=num*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算加上0.5,还是在小数点后面。
-
- datas[0] = temp / 10000;
- datas[1] = temp % 10000 / 1000;
- datas[2] = temp % 1000 / 100;
- datas[3] = temp % 100 / 10;
- datas[4] = temp % 10;
-
- LcdWriteCom(0xC1); //写地址 80表示初始地址
- LcdWriteData('0'+datas[0]); //百位
-
-
- LcdWriteCom(0xC2); //写地址 80表示初始地址
- LcdWriteData('0'+datas[1]); //十位
-
- LcdWriteCom(0xC3); //写地址 80表示初始地址
- LcdWriteData('0'+datas[2]); //个位
-
- LcdWriteCom(0xC4); //写地址 80表示初始地址
- LcdWriteData('.'); //显示 ‘.’
-
- LcdWriteCom(0xC5); //写地址 80表示初始地址
- LcdWriteData('0'+datas[3]); //显示小数点
-
- LcdWriteCom(0xC6); //写地址 80表示初始地址
- LcdWriteData('0'+datas[4]); //显示小数点
-
- LcdWriteCom(0xC7); //写地址 80表示初始地址
- LcdWriteData('C');
- }
-
- void LcdDisplay3(float num)
- {
- unsigned char datas[] = {0, 0, 0, 0, 0}; //定义数组
- int temp;
- // LcdInit(); //初始化LCD
-
- LcdWriteCom(0xC8); //写地址 80表示初始地址
- LcdWriteData('+'); //显示正
- temp=num*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算加上0.5,还是在小数点后面。
-
- datas[0] = temp / 10000;
- datas[1] = temp % 10000 / 1000;
- datas[2] = temp % 1000 / 100;
- datas[3] = temp % 100 / 10;
- datas[4] = temp % 10;
-
- LcdWriteCom(0xC9); //写地址 80表示初始地址
- LcdWriteData('0'+datas[0]); //百位
-
-
- LcdWriteCom(0xCA); //写地址 80表示初始地址
- LcdWriteData('0'+datas[1]); //十位
-
- LcdWriteCom(0xCB); //写地址 80表示初始地址
- LcdWriteData('0'+datas[2]); //个位
-
- LcdWriteCom(0xCC); //写地址 80表示初始地址
- LcdWriteData('.'); //显示 ‘.’
-
- LcdWriteCom(0xCD); //写地址 80表示初始地址
- LcdWriteData('0'+datas[3]); //显示小数点
-
- LcdWriteCom(0xCE); //写地址 80表示初始地址
- LcdWriteData('0'+datas[4]); //显示小数点
-
- LcdWriteCom(0xCF); //写地址 80表示初始地址
- LcdWriteData('C');
- }
复制代码
完整的Word格式文档51黑下载地址:
基于51单片机的PID调温(热得快).doc
(4.46 MB, 下载次数: 279)
|