||
本工作笔记将着重介绍例程的PID速度调节部分。例程的T0、T1的应用前面几篇有比较详细的软件解析,此处略
CCU6.C
void CC6_vInit(void) ; CCU6初始化函数
{
略。。。。。。。。。。。。
}
void CC6_viNodeI3(void) interrupt CCU6_NodeI3_INT // CCU6节点3中断函数;
{
extern code unsigned char HallPatt[]; //霍尔状态数组表;
extern code unsigned char OutputPatt[]; //开关状态数组表;
unsigned char temp; //临时变量;
unsigned int feedback_period; //反馈周期;
extern unsigned int mod; //占空比;
extern float feedback_speed; //浮点的反馈速度;
extern bit large_negative_error; //超限的负误差;
extern unsigned char rev_count; //回转计数值;
extern unsigned int accumulate_period; //累计周期;
extern unsigned char first_hall; //启动霍尔;
SFR_PAGE(_cc3, SST2); //切换到第3页;
If (CCU6_ISL & 0x10) //如果是 CC62通道上升沿;
{ //是CC62通道中断;
SFR_PAGE(_cc0, noSST); //切换到第0页;
CCU6_ISRL = 0x10; //清除CC62R中断标志;
#define MCM_transfer 0x80 //定义自动完成影射到实际寄存器的传输;
feedback_speed=0; //反馈速度;
#ifdef brake_for_negative_error //为负误差为减速跳过更新;
temp = (P2_DATA & 0x7); //截取当前的霍尔状态;
CCU6_MCMOUTSH = HallPatt[temp] | MCM_transfer; //更新霍尔序列,自动传输
CCU6_MCMOUTSL = OutputPatt[temp] | MCM_transfer;//更新开关序列,自动传输
#endif
CC6_vStopTmr_CC6_TIMER_12(); //停止T12
CC6_vResetTmr_CC6_TIMER_12(); //复位T12
CC6_vStartTmr_CC6_TIMER_12(); //启动T12
}
SFR_PAGE(_cc3, noSST); //切换到第3页
If (CCU6_ISH & 0x10) //如果是正确的霍尔事件CHE
{ //是正确的霍尔事件CHE
SFR_PAGE(_cc0, noSST); //切换到第0页
CCU6_ISRH = 0x10; //清除CHE标志位
temp = CCU6_MCMOUTSH & 0x07; //截取预期霍尔状态;
CCU6_MCMOUTSL = OutputPatt[temp] | MCM_transfer; //查霍尔状态表写入影射寄存器,并自动更新实际寄存器
CCU6_MCMOUTSH = HallPatt[temp] | MCM_transfer; //查开关状态表写入影射寄存器,并自动更新实际寄存器
#ifdef brake_for_negative_error
(large_negative_error)?0x15: //大的负误差 0x15
#endif
SFR_PAGE(_cc1, noSST); //切换到第1页
feedback_period=CC6_uwGetChannelRegister_CC6_CHANNEL_0(); //CC60通道捕获的是当前霍尔移动的工作周期
SFR_PAGE(_cc0, noSST); //切换到第0页
accumulate_period+=feedback_period; //累计周期对于当前反馈周期累计;
rev_count++; //转动位置计数加1;
if ( temp == 1 ) //霍尔状态指定状态为1时;
{
if ( rev_count==6 ) //回转一圈计数如果等于6,
feedback_speed = 1 / (float) accumulate_period;反馈速度等于累积周期的倒数
accumulate_period=0; //累积周期回零
rev_count=0; //霍尔计数
}
}
SFR_PAGE(_cc0, RST2);
}
CCU6节点3的中断函数对于两个中断源进行判断,一个是上面的CC62通道的比较中断,一个是下面的正确的霍尔事件中断,62通道的比较值大于电机最低维持速度的运行周期,只要电机再运转,就能够产生霍尔事件,产生CC60通道的捕获操作,以及硬件复位T12,就无法产生CC62通道的比较中断。只有电机停止,T12得不到复位,才可能递增到62通道的比较值,产生62通道比较中断。不存在同时响应两个中断,所以采用软件的分析的方法。
该例程设置为霍尔传感器模式,该模式定义CC60通道为捕获模式,定义CC62通道为比较模式。
CC60寄存器捕获的(保存)是T12的计数,在正确的霍尔事件的跳变沿产生捕获,显然捕获的是一个完整的霍尔工作周期,捕获时刻只和霍尔事件的跳变沿发生关系,和读取60通道的时间早晚无关,可以保证测量速度的准确性。捕获完成以后T12计数器被硬件复位,为下一轮霍尔工作周期的计时作好准备。
CC62通道比较的是T12的计数值,通常作为超时触发处理的中断源。如果给定速度比较低,占空比减少会造成电机停止,也就是电机将检测不到正确的霍尔事件,T12无复位操作,T12的计数会持续增加.如果我们比电机最低维持速度的工作周期还长的时间,设置为CC62通道的比较值,一旦T12的计数值大于该值,将触发62通道比较中断.
CC62通道比较中断将试图重新启动电机,电机此时占空比比较低,系统保持在该状态也是安全的。一旦给定电机速度调节增加,占空比自然加大,电机自然会重新启动成功,恢复正常运行。也就是给定速度调节,不但能够调节电机速度快慢,而且能控制电机的启动和停止,是最简便的操纵方法。
因此重新启动的状态必然和初始启动的状态的设置十分相同。另外原程序CC62通道的设定值设置比较低,比正常初始启动还先期动作,令人不明所以。现在可以在初始启动时将T12关闭,完成正常初始启动以后,再启动T12。CC62通道的比较值略低,可以有更快的启动响应。因为62通道不具备硬件复位T12的功能,所以最后有三条相关联的软件复位的操作指令。
正确的霍尔事件中断除了完成正常移动霍尔以及移动开关序列的操作,是大家熟悉的内容不需要介绍。另外就是计算反馈速度,为PID速度调节的计算准备条件。因为电机回转一圈经过6个霍尔工作周期,而CC60通道每次只能捕获其中之一。所以它对于6个霍尔的工作周期进行累计。因为当前速度是回转一圈的累计周期的倒数,所以只需要进行一个简单的除法运算。以任何一个指定霍尔位置回转一圈,速度计算的结果都是相同的,该程序指定霍位置为1,没有其他的涵义。另外是电机回转一圈的平均速度。当前测量速度在PID速度调节系统里是作为反馈速度出现的,因此赋值给反馈速度变量。
解析未完,9月16日继续.
基于XC866的PID速度调节程序解析(2)
void T01_viTmr1(void) interrupt T1INT // T1的中断服务;
{ //每0.05秒中断一次;
static unsigned char sampling_time; //取样时间;
float error; //误差;
float error_previous=error; //前一次的误差;
float error_integral; //积分误差;
float total_term; //总体周期;
unsigned int mod; //占空比;
//static float previous_reference_speed; //前一次基准速度;
// float error_band; //误差范围;
bit large_negative_error; //超限负误差控制位;
float feedback_speed; //反馈速度;
extern float reference_speed; //基准速度;
#define Kp 700000 //定义PID的比例系数
#define Ki 60000 //定义PID的积分系数
#define Kd 300000 //定义PID的微分系数
#define max_mod 0xFF //定义最大容许占空比;
#define min_mod 0x00 //定义最小容许占空比;
#define anti_windup 1.0 //负的终结值;
if ( ++sampling_time == 8 ) //时间计次没到8,跳过。
{ //时间计次等于8,进行PID的计算;
sampling_time=0; //计次回0;为下轮计次作准备;
error=reference_speed-feedback_speed; //基准减去反馈等于当前误差
total_term = Kp * error + Ki * error_integral + Kd * ( error - error_previous )
//依据当前误差以及PID系数,计算出PID总体误差
if ( total_term>-70.0 ) //如果超速补偿电机反映的迟钝;
error_integral += error; //积分误差累计当前误差;
if ( error_integral> anti_windup ) //积分误差大于最大容许误差;
error_integral = 0; //积分误差等于0
{
mod = (total_term<0) ? min_mod : (unsigned int) total_term;
根据总体误差进行占空比的转换
if(mod>max_mod) //占空比大于最大容许占空比;
mod=max_mod; //空比等于最大容许占空比;
}
CC6_vLoadChannelShadowRegister_CC6_CHANNEL_3(mod);
//以占空比,修改CC63通道影射寄存器PWM值;
CC6_vEnableShadowTransfer_CC6_TIMER_13();
//使能影射转移到CC63通道实际的比较寄存器;
#ifdef brake_for_negative_error
large_negative_error = (error<-0.0015 && error_previous<-0.0015);
large_negative_error = (total_term<-70.0);
#endif
error_previous=error; //前一次误差等于当前误差
}
}
T1的中断服务程序主要依据设定速度减去反馈速度,获得当前速度误差。然后以当前误差及PID系数,经过PID公式计算出总体误差,以及根据总体误差以及限位条件进行校正,即属于分离式限位PID。然后依据校正结果,更新PWM占空比,以此稳定电机的转速。因为每条计算都有详细的说明,非常清晰故不另外说明。经过模拟参数的运算程序工作一切正常。因为它采用浮点运算,所以计算结果非常精细。如果以每次定时都进行PID运算(频度最高),经过实测也只占定时周期的17%,运算速度完全满足我们系统的使用要求。并且为更加复杂的应用提供足够的运行空间。目前对于#标注的程序目的不明确,因为是可选项,我们当前没有选择。
MAIN_vInit();
{
#define MCM_transfer0x80 //设置自动进行影射传输标志
for (j = 0; j< 0xFFFF; j++); //软件定时,等待电源电压稳定;
CCU6_MCMOUTSL = 0x15 | MCM_transfer;//加载低侧的开关;给自举
for (j = 0; j< 0xFFFF; j++); //电容器充电;等待充电结束;
i = (P2_DATA & 0x7); //读取电机启始位置的霍尔代码;
CCU6_MCMOUTSH = HallPatt | MCM_transfer; //以当前代码检索霍尔代码表,并且更新;
CCU6_MCMOUTSL = OutputPatt | MCM_transfer;//以当前状态检索开关代码表,并且更新;
I = HallPatt & 0x07; //获得预期霍尔状态;
CCU6_MCMOUTSH = HallPatt; //映射寄存器下一个状态值;
CCU6_MCMOUTSL = OutputPatt; //映射寄存器下一个状态值;
IO_vSetPin(speed_port); //设置速度控制引脚P0.3=1;
T01_vLoadTmrH(0,0xE0); //T0高8位初始值;
T01_vLoadTmrL(0,0); //T0低8位初始值;
T01_vStartTmr(0); //启动T0
T01_vStartTmr(1); //启动T1
feedback_speed = 0.0; //反馈速度初始值
reference_speed=0.0002; //基准速度在0.00015 to 0.0006
error_integral = 0.0; //积分误差
error_previous = 0.0; //累计误差
mod = 0x50; //启动占空比;
rev_count = 0; //转速
accumulate_period = 0; //采样周期
}
while( 1 ) //等待中断;
if (TF1 ==1 ) //是否产生T1的中断
{ }
}
只作略微的调整,T1初始化暂时停止计时,待电机运行初始化完成才开启T1,红字表示增加的命令.电机运行初始需要对于外部驱动电路的自举电容器充电;等待充电结束,才能启动运行.此时不需要T1的与.CCU6_MCMOUTSL = 0x15 或者CCU6_MCMOUTSL = 0x2A,是使上桥壁全部接通,或者下桥壁全部接通,都不能使电机运行. while( 1 ) 循环是用户主程序,可以对基准速度进行设置,原文略.