Microchip带霍尔正弦波电机控制(AN1017)在STM32F103R8T上实现
1赞为了加深对SVPWM控制算法的理解,本人对Microchip应用笔记AN1017的程序移植在STM32f103R8T上进行测试。以下是程序的流程和对SVPWM控制程序的理解。
系统上电后先进行初始化,包括:
(1) PWM配置。
(2) TIM3输入捕获模式初始化。
采用高级定时器TIM1产生PWM驱动马达时,可以用另一个通用TIMx(TIM2、TIM3、TIM4或TIM5)定时器作为“接口定时器”来连接霍尔传感器。3个定时器输入脚(CC1、CC2、CC3)通过一个异或门连接到TI1输入通道(通过设置TIMx_CR2寄存器中的TI1S位来选择),“接口定时器”捕获这个信号。配置了CC1捕获中断后,每当3个输入之一变化时,都能进入TIM3_CC1中断。可在中断服务程序中读出三个引脚的霍尔信号值,计算转子位置。
(3) TIM2基本定时模式初始化。
(4) PID参数初始化。
电机启动后,主程序根据TM2中设置的变量进行计时。每隔2ms进行一次PID控制。(为了简化控制,默认电机的转向为正转而不发生改变,且不做电机堵住保护)。电机各个流程的控制功能在中断服务程序中ISR钟实现:
中断 | 调用频率 | 执行操作 |
pwm | 20k | 使用pwm产生正弦波 |
TIM2(TimeUpdate) | 1k | l 使用TIM3的数据进行测量转速计算 l 进行计时 |
TIM3(IC1) | 每个霍尔信号跳变 | l 计算转子位置 l 实现正弦波指针与转子位置同步 |
Pwm中断服务程序:
void TIM1_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM1, TIM_FLAG_Update)!=RESET) { Phase += Phase_inc; BLDC_SVPWM(ControlOutput, Phase); TIM_ClearFlag(TIM1, TIM_FLAG_Update); } }
其中Phase变量是用0—65535所表示的电角度。Phase_inc变量是每次PWM周期后电角度的增量。
TIM2中断服务程序:
void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_Update)!=RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); if(ActualPeriod < LastPeriod) Period = 50000 + ActualPeriod - LastPeriod ; else Period = ActualPeriod - LastPeriod; if(Period > 60000 ) Period = 60000; if(Period < 3749) Period = 3749; measure_v = (uint16_t)(15000000.0f / Period); Phase_inc = 3276800.0 / Period; TIM2count++; } }
ActualPerid是前一次捕获到霍尔信号跳变到设定值时,TIM3定时器的值。LastPeriod是本捕获到霍尔信号跳变到设定值时,TIM3定时器的值。通过计算前后两次的增量,计算电机的转速。即电机运行360个电角度(下文提及),需要Period个TIM3的时基单元(设定为1us),当Period大于50000时,会发生溢出事件。而电机电角度=电机机械角度*电机极对数,所以计算机械角转速时,需要用电角度的转速/极对数。电机的转速计算:
TS:TIM3捕获时基单元,为10-6。
p:电机极对数,本实验使用8极电机,即p=4。
在TIM2每次定时中断中计算电角度的增量,假设在每个定时周期内,电机都以当前周期所计算的转身匀速运行。则映射到0~65535表示为:
:pwm周期,为
TIM3中断服务程序:
void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_CC1)!=RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); Get_HallValue(); Sector = SectorTable[HallValue]; Phase = PhaseValues[Sector]; if(HallValue == 2) { LastPeriod = ActualPeriod; ActualPeriod = TIM_GetCapture1(TIM3); } } }
当检测到霍尔信号跳变,在中断程序中先读出霍尔信号值。在根据具体的霍尔信号值查表得到电角度。算出两次相同电角度之间的周期。(实测中若算电机运转过程中相邻两个霍尔信号的周期,会测得两次信号有较大差异,但没有找出原因)。
SVM程序分析(节选其中一个扇区):
if(Angle < VECTOR2) { angle2 = Angle - VECTOR1; // Reference SVM Angle to the current sector angle1 = SIXTY_DEG - angle2; // Calculate second Angle referenced to sector t1 = sinetable[(unsigned char)(angle1 >> 6)]; // Look up values from table. t2 = sinetable[(unsigned char)(angle2 >> 6)]; // Scale t1 for the duty cycle range. t1 = ((long)t1*(long)TS) >> 15; t1 = ((long)t1*(long)uout) >> 15; // Scale t2 time t2 = ((long)t2*(long)TS) >> 15; t2 = ((long)t2 *(long)uout) >> 15; if(t1 > TS) t1 = t1 / (t1 + t2) * TS; if(t2 > TS) t2 = t2 / (t1 + t2) * TS; half_t0 = (TS - t1 - t2) >> 1; // Calculate half_t0 null time from period and t1,t2 // Calculate duty cycles for Sector 1 (0 - 59 degrees) TIM1->CCR1 = t1 + t2 + half_t0; TIM1->CCR2 = t2 + half_t0; TIM1->CCR3 = half_t0; }
在svpwm程序中,首先根据电角度读出所在扇区(以60。)为一区间。查表获得该电角度正弦值。因为使用了1024个正弦波指针,为简化浮点数计算,采用Q15格式,所以每个正弦波指针所表示的值是215* sin(n*360/1024)(n=0,1,2……1024)。具体如下:
const int sinetable[] =
{0,201,401,602,803,1003,1204,1404,1605,1805,2005,2206,2406,2606,2806,3006,3205,3405,3605,3804,4003,4202,4401,4600,4799,4997,5195,5393,5591,5789,5986,6183,6380,6577,6773,6970,7166,7361,7557,7752,7947,8141,8335,8529,8723,8916,9109,9302,9494,9686,9877,10068,10259,10449,10639,10829,11018,11207,11395,11583,11771,11958,12144,12331,12516,12701,12886,13070,13254,13437,13620,13802,13984,14165,14346,14526,14706,14885,15063,15241,15419,15595,15772,15947,16122,16297,16470,16643,16816,16988,17159,17330,17500,17669,17838,18006,18173,18340,18506,18671,18835,18999,19162,19325,19487,19647,19808,19967,20126,20284,20441,20598,20753,20908,21062,21216,21368,21520,21671,21821,21970,22119,22266,22413,22559,22704,22848,22992,23134,23276,23417,23557,23696,23834,23971,24107,24243,24377,24511,24644,24776,24906,25036,25165,25293,25420,25547,25672,25796,25919,26042,26163,26283,26403,26521,26638,26755,26870,26984,27098,27210,27321,27431,27541,27649,27756,27862,27967,28071,28174,28276,28377};
如上文所描述,360分为6个区间,每个区间为60。。因此根据电角度得出具体区间后,再转化为60。的角度计算。因为只列出1024*60/360=170个正弦值。
根据svpwm的控制算法算出各个桥臂场效应管的通断时间。通过调整uout可调节电机转速。
数字PID程序,试验中采用增量式PID,即每次根据当前的检测结果,算出PID增量,加到上一次的输出量当中。公式如下:
公式计算所得出的∆u(k),在主程序中加到上一次的输出控制量,将输出控制量写入svpwm的uout参数,使电机达到期望转速。增量PID的程序通过改写STM32F10x_DSP_Lib库中的PID源程序实现。
int16_t DoFullPID(int In, int Ref, int16_t *Coeff) { int32_t Kp, Ki, Kd; float PID_b; Error = Ref - In; if((PreOutput >= UMMAX && Error >= 0) || (PreOutput <= UMMIN && Error <= 0)) { PID_b=0; } else { if(fabs(Error) < 2.0) PID_b = 0.8; else PID_b = 0.99; } Kp = Coeff[0]; Ki = Coeff[1]; Kd = Coeff[2]; Inc_Output = Kp * (Error - PrevError_C1); Inc_Output += Ki * Error; Inc_Output += Kd * (Error - 2 * PrevError_C1 + PrevError_C2); Output = PreOutput + Inc_Output; if(Output > UMMAX) Output = UMMAX; if(Output < UMMIN) Output = UMMIN; PrevError_C2 = PrevError_C1; PrevError_C1 = Error; PreOutput = Output; return (Output); }
PrevError_C1,PrevError_C2为两个全局变量,分别记录前两次及前一次的期望值和实际测量值的误差。
在出程序中,调整P,I,D系数,直到电机稳定转动。然而在测试板上运行以上程序,电机运行一会后便发热十分厉害,后续将查看电机的相电流以查找原因。