基于51单片机的闭环控制 附带源程序文档格式.docx
《基于51单片机的闭环控制 附带源程序文档格式.docx》由会员分享,可在线阅读,更多相关《基于51单片机的闭环控制 附带源程序文档格式.docx(18页珍藏版)》请在冰豆网上搜索。
而软件部分,是对硬件端口所体现的信号,加以采集、分析、处理,最终实现控制器所要实现的各项功能,达到控制器自动对电机速度的有效控制。
但是此设计中电机只需要正转,所以相对来说简单点。
3硬件设计
8051单片机简介
AT89C52是一个低电压,高性能CMOS8位单片机,片内含8kbytes的可反复擦写的Flash只读程序存储器和256bytes的随机存取数据存储器(RAM),器件采用ATMEL公司的高密度、非易失性存储技术生产,兼容标准MCS-51指令系统,片内置通用8位中央处理器和Flash存储单元,AT89C52单片机在电子行业中有着广泛的应用。
AT89C52有40个引脚,32个外部双向输入/输出(I/O)端口,同时内含2个外中断口,3个16位可编程定时计数器,2个全双工串行通信口,2个读写口线,AT89C52可以按照常规方法进行编程,也可以在线编程。
其将通用的微处理器和Flash存储器结合在一起,特别是可反复擦写的Flash存储器可有效地降低开发成本。
部分功能如下:
(1)8kB可反复擦写(大于1000次)FlashROM;
(2)32个双向I/O口;
(3)256x8bit内部RAM;
(4)3个16位可编程定时/计数器中断;
(5)时钟频率0-24MHz;
(6)2个串行中断,可编程UART串行通道;
(7)2个外部中断源,共8个中断源;
(8)2个读写中断口线,3级加密位;
直流电机
设计中采用直流电机,自带高精度的磁编码器,性能介绍如图一所示,
接线说明如图二所示:
性能介绍图一
接线图二
L298N
恒压恒流桥式2A驱动芯片L298N,简称H桥。
L298是SGS公司的产品,比较常见的是15脚Multiwatt封装的L298N,内部同样包含4通道逻辑驱动电路。
可以方便的驱动两个直流电机,或一个两相步进电机。
L298N芯片可以驱动两个二相电机,也可以驱动一个四相电机,输出电压最高可达50V,可以直接通过电源来调节输出电压;
可以直接用单片机的IO口提供信号;
而且电路简单,使用比较方便。
L298N可接受标准TTL逻辑电平信号VSS,VSS可接4.5~7V电压。
4脚VS接电源电压,VS电压范围VIH为+2.5~46V。
输出电流可达2.5A,可驱动电感性负载。
1脚和15脚下管的发射极分别单独引出以便接入电流采样电阻,形成电流传感信号。
L298可驱动2个电动机,OUT1,OUT2和OUT3,OUT4之间可分别接电动机,本实验装置我们选用驱动一台电动机。
5,7,10,12脚接输入控制电平,控制电机的正反转。
EnA,EnB接控制使能端,控制电机的停转。
L298芯片驱动电路图如下:
图1L298芯片驱动电路图
系统总体电路
图中控制器模块为系统的核心部件,电位器和显示器是用来实现人机交换功能,其中通过键盘将需要设置的参数和状态输入到单片机中,并且通过控制器显示到显示器上。
在运行过程中控制器产生PWM脉冲送到电机驱动电路中,经过放大后控制直流电机转速,同时利用速度检测模块将当前转速反馈到控制器中,控制器经过数字PID运算后改变PWM脉冲的占空比,实现电机转速实时控制的目的。
PWM脉冲
图1系统方案框图
4软件设计
PID算法
PI调节器是电力拖动自动控制中最常用的的一种,在微机数字控制系统中,当采样频率足够高时,可以先按模拟系统的设计方法设计,然后再离散化,得到数字控制器的算法。
PI调节器的传递函数如式所示:
()
若输入误差函数为e(t),输出函数为u(t),则e(t)和u(t)的关系时域表达式可写成:
()
式子中
为比例系数,
为比例系数。
将式转化为差分方程,得到数字PI调节器的表达式,其第k拍输出为:
式中
为采样周期。
增量式算法只需要当前的和上一拍的偏差即可计算出输出值。
增量式PI调节器算法为:
()
在控制系统中,常需要对调节器的输出实施限幅。
在数字控制算法中,要对u限幅,只需要在程序中设置限幅值。
不考虑限幅时,位置式和增量式两种算法完全等同,考虑限幅时,则两者略有差异。
增量式PI调节器算法只需要输出限幅,而位置式算法必须同时设置积分限幅和输出限幅。
若没有积分限幅,积分项可能很大,将产生较大的退饱和超调。
算法模块如下:
inte=0,e1=0,e2=0;
floatuk=0,uk1=,duk=;
//PID输出值
floatkp=5,ki=,kd=;
//PID控制系数
上面是初始化部分
voidPIDControl()//PID偏差计算
{
e=temp-num;
//变量temp是AD采集过来的值,num是通过M法测出点值
duk=(kp*(e-e1)+ki*e+kd*(e-2e1+e2))
uk=uk1+duk;
out=(int)uk;
if(out>
250)
{
out=100;
}
elseif(out<
0)
out=0;
uk1=uk;
e2=e1;
e1=e;
value=out;
//value最后送给PWM。
}
这部分是子程序
M法测速
在一定时间Tc内测取旋转编码器输出脉冲个数M1,用以计算这段时间内的转速,称作M法测速。
把M1除以Tc就可以得到旋转编码器输出脉冲的频率f1=M1/Tc,所以又称频率法。
电机每转一圈共产生Z个脉冲(Z=倍频系数*编码器光栅数),把f1除以Z就得到在单位时间内电机的转速。
在习惯上,时间Tc以s为单位,而转速是以r/min为单位,则电动机的转速为:
n=60M1/ZTc()
由于Z和Tc都是常数,因此转速n与计数值M1成正比,故此测速方法称M法测速。
具体程序部分用外部中断计数和定时器来完成,程序如下:
voidexter0()interrupt0//外部中断脉冲计数
{//M法测速度(外部中断0和定时器0用在M法测速上)
Inlpuse++;
voidtimer0()interrupt1
{
TH0=0x3c;
//重装初值
TL0=0xb0;
time++;
if(time>
=20)//1s钟读取一次转速(2*60ms)
{EX0=0;
TR0=0;
num=Inlpuse;
//计算转速
Inlpuse=0;
PIDControl();
//100ms控制一次
Inlpuse=0;
EX0=1;
TR0=1;
}
参数设定
在程序中修改PID调节算法中的比例系列、积分系数和微分系数可以得到不同特性的转速曲线。
参数改变,转速响应的超调量和调整时间发生变化。
系统的稳定性和快速性是一对矛盾,因此我们必须选择一个合适的PI参数。
在调试过程中,当令kp=5、ki=、kd=时系统的快速性和稳定性达到最佳状态。
电位器的AD采集模块
AD采集模块利用是我是直接调用了开发板里面的程序,这个模块可以也可以自己制作。
我用的XPT2046是一款4线制电阻式触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。
此芯片支持从到的低电压I/O接口。
XPT2046能通过执行两次A/D转换查出被按屏幕位置,除此之外,还可以测量加在触摸屏上的压力。
主要特性如下:
(1)工作电压范围为到
(2)支持的数字I/O口
(3)内建参考电压
(4)内建结温测量功能
(5)触摸压力测量,具有自动省电功能
(6)采用3线制SPI通信接口
程序见最后的程序清单。
5课题总结
从课题选定开始,先是看了一遍课本以及网上找了对应的论文,然后看了老师发的试验资料以及各个元器件的原理和用法,最后把硬件焊接起来,然后想如何测测速,如何给定,如何用PID进行控制,最后如何通过PID去控制PWM的占空比。
在调试的过程中遇到一个很简单和致命的问题,就是我用的开发板和L298N开始的时候没有共接地导致调速一直失败,最后通过用示波器观测PWM输出的波形发现了问题,当真正的实现调速的时候感觉很好,终于通过自己的努力完成了一项有点技术含量的事情。
继续加油。
全部程序如下:
对于程序,每个读者的I/0口或者用的ad采集方式不一样,程序可能不同,但是解决闭环问题的核心是读取给定值用AD转换,读取测速值用外部中断和定时器中段,PWM输出也用一个定时器。
#include"
"
//--定义使用的IO--//
#defineGPIO_DIGP0
sbitPWM=P1^0;
//PID偏
intout=0;
sbitIN2=P1^1;
//--定义一个全局变量--//
unsignedintvalue,timer1;
uintnum;
uintInlpuse=0,num=0;
//脉冲计数单元
uinttemp,count,temp1;
unsignedinttime=0;
sbitzhongduan=P3^2;
sbitLSA=P2^2;
sbitLSB=P2^3;
sbitLSC=P2^4;
//--定义全局变量--//
unsignedcharcodeDIG_CODE[17]={
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码
ucharDisplayData[8];
//用来存放要显示的8位数的值
voidDigDisplay(void);
voidPIDControl()//PID偏差计算
out=0;
voidSetSpeed(void)
temp1=Read_AD_Data(0x94);
//AIN0电位器(100us采集一次
temp=2*temp1>
>
5;
//temp的变化范围在0-250之间
voidDigDisplay(void)
unsignedchari;
unsignedintj;
DisplayData[7]=DIG_CODE[num%10000/1000];
DisplayData[6]=DIG_CODE[num%1000/100];
DisplayData[5]=DIG_CODE[num%100/10];
DisplayData[4]=DIG_CODE[num%10/1];
DisplayData[3]=DIG_CODE[temp%10000/1000];
DisplayData[2]=DIG_CODE[temp%1000/100];
DisplayData[1]=DIG_CODE[temp%100/10];
DisplayData[0]=DIG_CODE[temp%10/1];
for(i=0;
i<
8;
i++)
switch(i)//位选,选择点亮的数码管,
{
case(0):
LSA=0;
LSB=0;
LSC=0;
break;
//显示第0位
case
(1):
LSA=1;
//显示第1位
case
(2):
LSB=1;
//显示第2位
case(3):
//显示第3位
case(4):
LSC=1;
//显示第4位
case(5):
//显示第5位
case(6):
//显示第6位
case(7):
//显示第7位
}
GPIO_DIG=DisplayData[i];
//发送段码
j=50;
//扫描间隔时间设定
while(j--);
GPIO_DIG=0x00;
//消隐
{//M法测速度(外部中断0和定时器0用在M法测速上)
voidT1_time()interrupt3
count1++;
if(count1>
=100)count1=0;
//计时100us*100=10ms=100Hz
if(count1<
value)pwm=1;
//占空比
elsepwm=0;
voidSystemInit()
{
TMOD=0x21;
//设定时器0为工作方式1,定时器1为工作方式2(自动重装初值)
//设定50ms一次中断
TH1=0x9c;
//设定100us一次中断
TL1=0x9c;
EA=1;
//开总中断
ET0=1;
//开定时器0中断
ET1=1;
//开定时器1中断
//开外部中断0
IT0=1;
//启动下降沿触发有效
TR1=1;
//启动定时器1
TR0=1;
//启动定时器0
voidmain()
SystemInit();
while
(1)
DigDisplay();
AD采集模块:
(这一部分程序只是AD采集模块,用的芯片是XPT2046,用spi通信方式,最后改变电位器可以改变temp的值,这部分程序读者可以自己解决,)
#ifndef__XPT2046_H_
#define__XPT2046_H_
//---包含头文件---//
#include<
//---重定义关键词---//
#ifndefuchar
#defineucharunsignedchar
#endif
#ifndefuint
#defineuintunsignedint
#ifndefulong
#defineulongunsignedlong
//---定义使用的IO口---//
sbitDOUT=P3^7;
//输出
sbitCLK=P3^6;
//时钟
sbitDIN=P3^4;
//输入
sbitCS=P3^5;
//片选
uintRead_AD_Data(ucharcmd);
uintSPI_Read(void);
voidSPI_Write(uchardat);
/****
*函数名:
TSPI_Start
*输入:
无
*输出:
*功能:
初始化触摸SPI
****/
voidSPI_Start(void)
CLK=0;
CS=1;
DIN=1;
CLK=1;
CS=0;
SPI_Write
dat:
写入数据
使用SPI写入数据
voidSPI_Write(uchardat)
uchari;
i<
i++)
DIN=dat>
7;
//放置最高位
dat<
<
=1;
CLK=0;
//上升沿放置数据
CLK=1;
SPI_Read
读取到的数据
使用SPI读取数据
uintSPI_Read(void)
uinti,dat=0;
12;
i++)//接收12位数据
dat|=DOUT;
returndat;
Read_AD_Data
cmd:
读取的X或者Y
endValue:
最终信号处理后返回的值
读取触摸数据
uintRead_AD_Data(ucharcmd)
uintAD_Value;
SPI_Write(cmd);
for(i=6;
i>
0;
i--);
//延时等待转换结果
//发送一个时钟周期,清除BUSY
_nop_();
AD_Value=SPI_Read();
returnAD_Value;