实验五STC15系列单片机快速控制比例双惯性对象的PID控制程序

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

/*T7(阶跃)或T8(连续)接T3作给定,被控对象板T5接T6作公共地,对象板T19接T2作反馈,短路块J6插上边,短路块J7插
右边,双刀双掷SW3掷上边,控制量T1接对象板的T16,且对象板的T17接T18(构成比例双惯性对象),采样周期3ms;给定电
压由左边5个数码管显示,被控对象的输出电压由右边5个数码管显示(均为1符号,2整数,2小数),PID参数由SW3SW2SW1选
SW3 SW2 SW1 Kp Ki Kd
on on on 0.100 0.00167 0.833
on on off 0.200 0.00334 1.67
on off on 0.500 0.00835 4.17
on off off 1.000 0.0167 8.33
off on on 0.100 0.00625 0.900
off on off 0.200 0.0133 2.00
off off on 0.500 0.0357 5.50
off off off 1.000 0.0769 12.0 */
#include <15f2k.h>
#include
#define u8c unsigned char code//存放于代码中的无符号8位字符型缩写定义
#define u8 unsigned char //无符号8位字符型缩写定义
#define SW XBYTE[0xBFFF]//8位拨码开关输入缓冲读端口地址(A14=0)
#define Displayseg XBYTE[0x7FFF]//数码管段码锁存器写端口地址(A15=0)
#define Displaybit XBYTE[0xBFFF]//数码管位码锁存器写端口地址(A14=0)
#define A0r -5.079 //给定1(伏),-12V -> +0.24V -> +0.1V
#define Amr 5.069 //给定2(伏),+12V -> -0.24V -> +4.9V
#define N0r 0x50 //给定1对应AD值,256*0.1/5.0=18,有点误差
#define Nmr 0xbe //给定2对应AD值,256*4.9/5.0=243,有点误差
#define A0m -5.115 //反馈1(伏),+12V -> -0.24V -> +4.9V
#define Amm 5.064 //反馈2(伏),-12V -> +0.24V -> +0.1V
#define N0m 0x53 //反馈1对应AD值,256*4.9/5.0=240,有点误差
#define Nmm 0xc4 //反馈2对应AD值,256*0.1/5.0=16,有点误差
#define T 0.001 //采样周期常数定义(秒)
bit new_cycle_flag=0; //有新采样数据标志,1有新数据
sbit LSIGN=P1^6; //左边的符号位数码管公共端连接引脚,=0可能亮
sbit RSIGN=P1^7; //右边的符号位数码管公共端连接引脚,=0可能亮
u8 rkTH,mkTH,i; //给定电压AD值、反馈电压AD值及中间变量i定义
u8 dispbuf[10],status; //10个数码管显示段码缓存区数组定义及内部AD状态
int DAkT,j; //控制量存放整型变量定义及中间变量定义
float ekT,ekT_T; //当前电压误差、上次电压误差浮点变量定义(伏)
float Kp,Ki,Kd; //数字PID控制系数
float Axr,a1r,b1r; //标度变换后的给定量及给定量标度变换的斜率和截距
float Axm,a1m,b1m; //标度变换后的反馈量及反馈量标度变换的斜率和截距
float pPkT,pIkT,pDkT,pIkT_T,pkT;//PID各分量、上次积分量及总控制量
u8 M=0,disp_gengxin=0; //扫描显示位计数、显

示更新计数
u8c SG[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//0-9段码表
u8c BT[10]={0xfe,0xfd,0xfb,0xf7,0xff,0xef,0xdf,0xbf,0x7f,0xff};//位码表
float code Kptab[8]={0.1,0.2,0.5,1.0,0.1,0.2,0.5,1.0};
float code Kitab[8]={0.00167,0.00334,0.00835,0.0167,0.00625,0.0133,0.0357,0.0769};
float code Kdtab[8]={0.833,1.67,4.17,8.33,0.9,2.0,5.5,12.0};
/*****线性参数标度变换初始化******/
void Scale_value_init(void) //暂时用ekT;ekT_T;pkT;pPkT
{ ekT=Amr;ekT_T=A0r;pkT=Nmr;pPkT=N0r;
a1r=(ekT-ekT_T)/(pkT-pPkT); //教材中的式(7-18):a1=(Am-A0)/(Nm-N0)
b1r=ekT_T-(ekT-ekT_T)*pPkT/(pkT-pPkT);//教材中的式(7-18):b1=A0-(Am-A0)*N0/(Nm-N0)
ekT=Amm;ekT_T=A0m;pkT=Nmm;pPkT=N0m;
a1m=(ekT-ekT_T)/(pkT-pPkT); //教材中的式(7-18):a1=(Am-A0)/(Nm-N0)
b1m=ekT_T-(ekT-ekT_T)*pPkT/(pkT-pPkT);//教材中的式(7-18):b1=A0-(Am-A0)*N0/(Nm-N0)
}
/***********PWM初始化************/ //SYSclk=11.0592MHz时
void PWM_init(void) //SYSclk/2,PWM频率为21.6KHz(纹波2mV),SYSclk/4,PWM频率为10.8KHz(纹波6mV)
{ CMOD=0x82; //CIDL - - - CPS2 CPS1 CPS0 ECF=10000010B,空闲停止计数,SYSclk/2,禁止PCA溢出中断
CL=0x00,CH=0x00; //清0计数器(8位PWM模式下为当前计数值)
CCAP2L=0x80,CCAP2H=0x80; //设置8位PWM模式下模块2的当前比较门限值和下次重装的比较门限值(CL计数溢出时重装)
CCAPM2=0x42; //- ECOM2 CAPP2 CAPN2 MAT2 TOG2 PWM2 ECCF2=01000010B,P3.7为PWM输出(比较模式),禁止CCF2中断
PCA_PWM2=0x00; //EBS2_1 EBS2_0 - - - - EPC2H EPC2L=00000000B,8位PWM,重装的门限第8位为0,门限第8位为0
CCON=0x40; //CF CR - - - CCF2 CCF1 CCF0=00000000B,4个状态标志清0,开启PCA计数器计数
}
/**********内部AD初始化**********/
void AD_init(void) //MCKO_S1 MCKO_S0 ADRJ TX_RX - CLKS2 CLKS1 CLKS0
{ CLK_DIV&=0xDF; //ADRJ=0,ADC_RES[7:0]存高8位ADC结果,ADC_RESL[1:0]存低2位ADC结果
ADC_CONTR=0x80; //开A/D转换电源
for(j=0;j<10000;j++); //适当延时,一般延时1ms以内即可
P1ASF=0x03; //开启P1.0和P1.1作为0通道和1通道模拟量输入通道
ADC_CONTR=0xE0; //ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0=1 11 0 0 000B
} //选择90个时钟周期转换1次,暂时不启动AD转换,选择0通道,转换时间90/11059200Hz=8.138微秒
/***********T0T2初始化***********/
void T0T2_init(void) //10数码管扫描1周AD转换1次,每管显1ms,AD周期1ms
{ AUXR |=0xc0; //T0、T1定时器时钟1T模式,T0定时1ms,AD时间63.7us
TMOD=0x20; //设置定时器模式,T0方式0定时,T1方式2定时
SCON=0x40; //8位UART波特率可变(T2溢出率/4)
TL0=0xcd; //频率=11059200/(65536-0xd4cd)=1000.018Hz
TH0=

0xd4; //定时周期=1/1000.018=0.001ms
AUXR &=0xfb; //定时器2时钟为Fosc/12,即12T模式
T2L=0xfa; //11059200/12/(65536-0xfffa)=153600Hz
T2H=0xff; //波特率=153600Hz/4=38400bps
AUXR |=0x11; //串口1选择定时器2为波特率发生器(S1ST2=1),定时器2开始计时(T2R=1)
EA=1; //允许中断
ES=0; //禁止串行通信中断(串行发送结束中断)
ET0=1; //允许T0中断
TR0=1; //定时器0开始计时
}
/****位置型数字PID算法子程序*****/
void PID(void) //采用与教材中的式(8-15)相同的PID算法
{ pPkT=Kp*ekT; //控制量的比例部分
pIkT=pIkT_T; //控制量的积分部分
pIkT+=Ki*ekT; //要积分
pDkT=Kd*(ekT-ekT_T); //控制量的微分部分
pkT=pPkT+pIkT+pDkT; //教材中的式(8-15)
DAkT=(int)pkT; //将浮点控制量pkT赋予输出控制量DAkT
}
/***控制量的双极性DA输出子程序***/
void PWM(void) //+0x72时Ux9=21mV;
{ DAkT+=0x72; //双极性控制量代表的0需要DA值平移到+129
if(DAkT>255) CCAP2H=0xff; //基本满足(0,CL)<(EPC2L,CCAP2L)则输出0.39%,控制量正饱和则DA输出最高约+10.914V
else if(DAkT<0) CCAP2H=0x00; //完全满足(0,CL)>=(EPC2L,CCAP2L)则固定输出高100%,控制量负饱和则DA输出最高约-11.0V
else CCAP2H=(u8)DAkT; //刷新下次重装的比较门限值N,DAkT小于0x72,输出负,DAkT大于0x72,输出正
}
/****给定电压显示缓存区的更新*****/
void disp_g(void) //给定电压显示缓存区的更新
{ j=(int)(Axr*100); //给定电压的100倍取整数
if(j<0){j=-j,dispbuf[9]=0x40;}//如果给定电压的100倍是负数,取绝对值,符号位数码管对应送负号的段码
else dispbuf[9]=0x00; //是正数,符号位数码管对应送正号的段码
dispbuf[8]=SG[j/1000]; //电压十位数码管显示缓存更新段码
dispbuf[7]=SG[j/100%10]|0x80; //电压个位数码管显示缓存更新段码(含小数点)
dispbuf[6]=SG[j/10%10]; //电压小数后第1位数码管显示缓存更新段码
dispbuf[5]=SG[j%10]; //电压小数后第2位更新
}
/****反馈电压显示缓存区的更新*****/
void disp_f(void) //反馈电压显示缓存区的更新
{ j=(int)(Axm*100); //反馈电压的100倍取整数
if(j<0){j=-j,dispbuf[4]=0x40;}//如果反馈电压的100倍是负数,取绝对值,符号位数码管对应送负号的段码
else dispbuf[4]=0x00; //是正数,符号位数码管对应送正号的段码
dispbuf[3]=SG[j/1000]; //电压十位数码管显示缓存更新段码
dispbuf[2]=SG[j/100%10]|0x80; //电压个位数码管显示缓

存更新段码(含小数点)
dispbuf[1]=SG[j/10%10]; //电压小数后第1位数码管显示缓存更新段码
dispbuf[0]=SG[j%10]; //电压小数后第2位更新
}
/*************主程序*************/
void main(void) //
{ PWM_init(); //
// Kp=Kptab[SW&0x07],Ki=Kitab[SW&0x07],Kd=Kdtab[SW&0x07];//查表获得PID参数
Kp=10.0,Ki=0.3333,Kd=66.67; //设计获得PID参数
Scale_value_init(); //线性参数标度变换中给定和反馈的斜率和截距计算,
ekT_T=0.0,pIkT_T=0.0; //上次误差和上次控制量积分部分初始化为0.0
AD_init(); //内部AD初始化
T0T2_init(); //T0、T1,T2初始化(11.0592MHz)
do
{ if(new_cycle_flag==1) //有新采样数据标志
{ Axr=a1r*(float)rkTH+b1r; //依据采样的给定值计算给定量,-11.0V~+10.914V
Axm=a1m*(float)mkTH+b1m; //依据采样的反馈值计算反馈量,-11.0V~+10.914V
ekT=Axr-Axm; //计算当前误差量,以V为单位
/*以V为单位误差量,-11V->0x11,+11.3V->0xf0,0V->0x7f,AD灵敏度(0x7f-0x11)/11=10/V,设误差10V,则误差的AD值100;若DA输出10V,
由VX9=11(2*N-256)/256=10,得N=244.364,控制量DAkT=244.364-128=116.364,如果不做任何控制,则误差数字量应该放大11.6364倍*/
ekT*=11.82483; //116.364/10=11.6364,故误差电压需要先修正
PID(); //PID控制时要,无PID控制时去掉
//DAkT=ekT; //无PID控制时要加上,PID控制时去掉
PWM(); //将控制量输出
disp_gengxin+=1; //1,...,40(0),1,...,40(0)
if(disp_gengxin==20) disp_g();//是1,给定电压显示缓存区的更新
else if(disp_gengxin==40)
{ disp_f(); //是3,反馈电压显示缓存区的更新
disp_gengxin=0; //清0
}
TI=0; //清除发送结束状态标志
ES=1; //允许串行通信中断(串行发送结束中断)
SBUF=rkTH; //发送给定值
new_cycle_flag=0; //新采样周期标志清0
ekT_T=ekT,pIkT_T=pIkT; //迭代移位,为下一次控制作准备
RSIGN=1; //关闭右边符号位显示
LSIGN=1; //关闭左边符号位显示
Displaybit=0xff; //关闭8个数码管的显示
Displayseg=dispbuf[M]; //查显示缓存表送数显的段端口
Displaybit=BT[M]; //查位码表送数显的位端口
if(M==4) RSIGN=0; //扫描到右边符号位则该位显示
if(M==9) LSIGN=0; //扫描到左边符号位则该位显示
M++; //修改下一次的显示位
if(M==10) M=0;
}
}while(1);
}
/*********T0中断服务程序*********/
void t0() interrupt 1 using 1 //1ms中断,自动重装
{ ADC_CONTR

|=0x08; //到了1ms,启动0输入通道AD转换
new_cycle_flag=1; //M清0且置有新采样数据标志
status=0; //状态变量先清0
while(status==0) status=ADC_CONTR & 0x10;//转换结束否?
ADC_CONTR=0xE1; //ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0=11100001B,ADC_FLAG清0,不启,1通道
rkTH=ADC_RES; //读取0通道AD结果的高8位,90/11059200Hz=8.138微秒
ADC_CONTR|=0x08; //启动1输入通道AD转换
status=0; //状态变量先清0
while(status==0) status=ADC_CONTR & 0x10;//转换结束否?
ADC_CONTR=0xE0; //ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0=11100000B,ADC_FLAG清0,不启,0通道
mkTH=ADC_RES; //读取1通道AD结果的高8位,90/11059200Hz=8.138微秒
}
/******串行通信中断服务程序******/
void txd() interrupt 4 using 2 //每10ms需要发送2个字节,其中第2字节靠串行发送中断完成
{ TI=0; //清除发送结束状态标志
ES=0; //发送到最后1个字节,禁止发送结束中断
SBUF=mkTH; //发送报文第2字节,即反馈量的AD值
}

相关文档
最新文档