Lamdeli

Microchip带霍尔正弦波电机控制(AN1017)在STM32F103R8T上实现

1
阅读(8886)

为了加深对SVPWM控制算法的理解,本人对Microchip应用笔记AN1017的程序移植在STM32f103R8T上进行测试。以下是程序的流程和对SVPWM控制程序的理解。

系统上电后先进行初始化,包括:

(1) PWM配置。

(2) TIM3输入捕获模式初始化。

采用高级定时器TIM1产生PWM驱动马达时,可以用另一个通用TIMx(TIM2TIM3TIM4TIM5)定时器作为“接口定时器”来连接霍尔传感器。3个定时器输入脚(CC1CC2CC3)通过一个异或门连接到TI1输入通道(通过设置TIMx_CR2寄存器中的TI1S位来选择),“接口定时器”捕获这个信号。配置了CC1捕获中断后,每当3个输入之一变化时,都能进入TIM3_CC1中断。可在中断服务程序中读出三个引脚的霍尔信号值,计算转子位置。

(3) TIM2基本定时模式初始化。

(4) PID参数初始化。

电机启动后,主程序根据TM2中设置的变量进行计时。每隔2ms进行一次PID控制。(为了简化控制,默认电机的转向为正转而不发生改变,且不做电机堵住保护)。电机各个流程的控制功能在中断服务程序中ISR钟实现:

 

中断

调用频率

执行操作

pwm

20k

使用pwm产生正弦波

TIM2TimeUpdate

1k

l  使用TIM3的数据进行测量转速计算

l  进行计时

TIM3IC1

每个霍尔信号跳变

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变量是用065535所表示的电角度。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个电角度(下文提及),需要PeriodTIM3的时基单元(设定为1us),当Period大于50000时,会发生溢出事件。而电机电角度=电机机械角度*电机极对数,所以计算机械角转速时,需要用电角度的转速/极对数。电机的转速计算:

image001.png

TS:TIM3捕获时基单元,为10-6

      p:电机极对数,本实验使用8极电机,即p=4

image005.png

TIM2每次定时中断中计算电角度的增量,假设在每个定时周期内,电机都以当前周期所计算的转身匀速运行。则映射到0~65535表示为:

image006.png

        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=012……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),在主程序中加到上一次的输出控制量,将输出控制量写入svpwmuout参数,使电机达到期望转速。增量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_C1PrevError_C2为两个全局变量,分别记录前两次及前一次的期望值和实际测量值的误差。

      在出程序中,调整PID系数,直到电机稳定转动。然而在测试板上运行以上程序,电机运行一会后便发热十分厉害,后续将查看电机的相电流以查找原因。