[活动专区] 【AT-START-M412测评】+ 无刷电机控制2-开始移植

[复制链接]
 楼主| 穿西装的强子 发表于 2025-5-31 15:31 | 显示全部楼层 |阅读模式
本帖最后由 穿西装的强子 于 2025-5-31 15:40 编辑

#申请原创# @21小跑堂
看了很多simple foc的代码,都是用arduino写的,可移植性和操作性就变小了,感觉没那么好用方便。
而且不确定AT32F412是否支持c++,所以还是准备移植基于c语言的foc代码。
按c语言的foc进行移植设计
一、软件框图

65933683a9a418a06c.png
二、软件设计
1. PWM配置

工作模式选择‌

  • 必须采用‌中央对齐模式‌(Center-Aligned Mode),以降低开关损耗并提高电流采样精度
  • 高级定时器(如TIM1/TIM8)需使能‌互补输出‌和‌刹车保护‌功能
频率与周期计算
  • PWM频率推荐‌10–20kHz‌(平衡开关损耗与动态响应)


死区时间设定‌
  • 防止上下桥臂直通,典型值‌≥500ns‌(依据MOS管开关延迟调整)
  • 还有一些使用Nmos和Pmos实现硬件的延迟,防止短路的情况

以下是pwm配置
  1. /* Time Base configuration */
  2.                 TMR_TimeBaseStructInit(&TMR_TimeBaseStructure);
  3.                 TMR_TimeBaseStructure.TMR_DIV = 0;
  4.                 TMR_TimeBaseStructure.TMR_CounterMode = TMR_CounterDIR_CenterAligned1;
  5.                 TMR_TimeBaseStructure.TMR_Period = PWM_PERIOD;
  6.                 TMR_TimeBaseStructure.TMR_ClockDivision = TMR_CKD_DIV1;
  7.                 TMR_TimeBaseStructure.TMR_RepetitionCounter = 0;

  8.                 TMR_TimeBaseInit(TMR1, &TMR_TimeBaseStructure);

  9.                 /* Channel 1, 2 and 3 Configuration in PWM mode */
  10.                 TMR_OCStructInit(&TMR_OCInitStructure);
  11.                 TMR_OCInitStructure.TMR_OCMode = TMR_OCMode_PWM2;
  12.                 TMR_OCInitStructure.TMR_OutputState = TMR_OutputState_Enable;
  13.                 TMR_OCInitStructure.TMR_OutputNState = TMR_OutputNState_Enable;
  14.                 TMR_OCInitStructure.TMR_Pulse = PWM_TIM_PULSE>>1;
  15.                 TMR_OCInitStructure.TMR_OCPolarity = TMR_OCPolarity_High;
  16.                 TMR_OCInitStructure.TMR_OCNPolarity = TMR_OCNPolarity_High;
  17.                 TMR_OCInitStructure.TMR_OCIdleState = TMR_OCIdleState_Reset;
  18.                 TMR_OCInitStructure.TMR_OCNIdleState = TMR_OCIdleState_Reset;

  19.                 TMR_OC1Init(TMR1, &TMR_OCInitStructure);
  20.                 TMR_OC2Init(TMR1, &TMR_OCInitStructure);
  21.                 TMR_OC3Init(TMR1, &TMR_OCInitStructure);
  22.                
  23.     /* Channel 4 Configuration in PWM mode */
  24.                 TMR_OCStructInit(&TMR_OCInitStructure);
  25.                 TMR_OCInitStructure.TMR_OCMode = TMR_OCMode_PWM1;
  26.                 TMR_OCInitStructure.TMR_OutputState = TMR_OutputState_Enable;
  27.                 TMR_OCInitStructure.TMR_OutputNState = TMR_OutputNState_Enable;
  28.                 TMR_OCInitStructure.TMR_Pulse = 1;
  29.                 TMR_OCInitStructure.TMR_OCPolarity = TMR_OCPolarity_High;
  30.                 TMR_OCInitStructure.TMR_OCNPolarity = TMR_OCNPolarity_High;
  31.                 TMR_OCInitStructure.TMR_OCIdleState = TMR_OCIdleState_Reset;
  32.                 TMR_OCInitStructure.TMR_OCNIdleState = TMR_OCIdleState_Reset;

  33.                 TMR_OC4Init(TMR1, &TMR_OCInitStructure);
  34.                
  35.                 /* Enables the TIM1 Preload on CC1 Register */
  36.                 TMR_OC1PreloadConfig(TMR1, TMR_OCPreload_Enable);
  37.                 /* Enables the TIM1 Preload on CC2 Register */
  38.                 TMR_OC2PreloadConfig(TMR1, TMR_OCPreload_Enable);
  39.                 /* Enables the TIM1 Preload on CC3 Register */
  40.                 TMR_OC3PreloadConfig(TMR1, TMR_OCPreload_Enable);
  41.                 /* Enables the TIM1 Preload on CC4 Register */
  42.                 TMR_OC4PreloadConfig(TMR1, TMR_OCPreload_Enable);


  43.                 /* Automatic Output enable, Break, dead time and lock configuration*/
  44.                 TMR_BRKDTStructInit(&TMR_BDTRInitStructure);
  45.                 TMR_BDTRInitStructure.TMR_OSIMRState = TMR_OSIMRState_Enable;
  46.                 TMR_BDTRInitStructure.TMR_OSIMIState = TMR_OSIMIState_Enable;
  47.                 TMR_BDTRInitStructure.TMR_LOCKgrade = TMR_LOCKgrade_1;
  48.                 TMR_BDTRInitStructure.TMR_DeadTime = DEADTIME;
  49.                 TMR_BDTRInitStructure.TMR_Break = TMR_Break_Enable;
  50.                 TMR_BDTRInitStructure.TMR_BreakPolarity = TMR_BreakPolarity_Low;
  51.                 TMR_BDTRInitStructure.TMR_AutomaticOutput = TMR_AutomaticOutput_Disable;

  52.                 TMR_BRKDTConfig(TMR1, &TMR_BDTRInitStructure);

  53.     TMR_ARPreloadConfig(TMR1,ENABLE);


  54.     TMR_SelectOutputTrigger(TMR1, TMR_TRGOSource_OC4Ref);
  55.     TMR_ClearITPendingBit(TMR1, TMR_INT_Break);
  56.     TMR_INTConfig(TMR1, TMR_INT_Break, ENABLE);


  57.                 /* TMR1 counter enable */
  58.                 TMR_Cmd(TMR1, ENABLE);

  59.                 /* Main Output Enable */
  60.                 TMR_CtrlPWMOutputs(TMR1, DISABLE);



2.ADC的配置

采样时机对齐‌
  • ADC采样点必须设在PWM波形的‌波谷处‌(中央对齐模式中点),避开开关噪声干扰
  • 使用定时器‌TRGO触发信号‌联动ADC启动,而非软件触发

以下是ADC配置

  1. ADC_InitType ADC_InitStructure;
  2.         
  3.     /* ADCCLK = PCLK2/6 */
  4.     RCC_ADCCLKConfig(RCC_APB2CLK_Div6);
  5.    
  6.   
  7. //使用双ADC模式,ADC1为主,ADC2为从。当ADC转换配置成由外部事件触发时,用户必须设置成仅触发主ADC,从ADC设置成软件触发,这样可以防止意外的触发从转换。
  8. //但是,主和从ADC的外部触发必须同时被激活,要调用 ADC_ExternalTrigConvCmd(ADC2, ENABLE);

  9.                 //ADC1配置
  10.                 ADC_Reset(ADC1);
  11.     ADC_StructInit(&ADC_InitStructure);
  12.                 ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult;        //ADC1工作在注入模式
  13.                 ADC_InitStructure.ADC_ScanMode = ENABLE;                      //模数转换工作在扫描模式(多通道)还是单次(单通道)模式
  14.                 ADC_InitStructure.ADC_ContinuousMode = ENABLE;          //模数转换工作在扫描模式(多通道)还是单次(单通道)模式
  15.                 ADC_InitStructure.ADC_ExternalTrig = ADC_ExternalTrig_None;//转换由软件启动
  16.                 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据左对齐
  17.                 ADC_InitStructure.ADC_NumOfChannel = 1;               //规定了顺序进行规则转换的ADC通道的数目。这个数目的取值范围是1到16
  18.                 ADC_Init(ADC1, &ADC_InitStructure);

  19.     //ADC规则通道设置
  20.                 ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_13_5);

  21.     //ADC2配置
  22.                 ADC_Reset(ADC2);
  23.     ADC_StructInit(&ADC_InitStructure);
  24.     ADC_InitStructure.ADC_Mode = ADC_Mode_InjecSimult;  //ADC2工作在注入模式
  25.     ADC_InitStructure.ADC_ScanMode = ENABLE;           
  26.     ADC_InitStructure.ADC_ContinuousMode = DISABLE;     //连续转换模式,触发后就会一直转换
  27.     ADC_InitStructure.ADC_ExternalTrig = ADC_ExternalTrig_None;  //双ADC模式的从ADC必须设置为软件触发
  28.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  29.     ADC_InitStructure.ADC_NumOfChannel = 1;               
  30.     ADC_Init(ADC2, &ADC_InitStructure);
  31.      
  32.                
  33.     ADC_Ctrl(ADC1, ENABLE);                                   //ADC1使能
  34.     ADC_RstCalibration(ADC1);                        //复位校准寄存器
  35.     while(ADC_GetResetCalibrationStatus(ADC1));       //等待校准寄存器复位完成
  36.     ADC_StartCalibration(ADC1);                        //ADC1开始校准
  37.     while(ADC_GetCalibrationStatus(ADC1));            //等待校准完成

  38.     ADC_Ctrl(ADC2, ENABLE);                             //ADC2使能
  39.     ADC_RstCalibration(ADC2);                        //复位校准寄存器
  40.     while(ADC_GetResetCalibrationStatus(ADC2));        //等待校准寄存器复位完成
  41.     ADC_StartCalibration(ADC2);                        //ADC2开始校准  
  42.     while(ADC_GetCalibrationStatus(ADC2));                   //等待校准完成

  43.     ADC_get_offset();

  44.     //ADC1 注入通道配置
  45.                 ADC_InjectedSequencerLengthConfig(ADC1,1);
  46.                 ADC_InjectedChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_7_5);     //A相电流
  47.                
  48.     //ADC2 注入通道配置
  49.     ADC_InjectedSequencerLengthConfig(ADC2,1);        //设置ADC2注入组通道数量
  50.     ADC_InjectedChannelConfig(ADC2, ADC_Channel_5, 1,ADC_SampleTime_7_5);       //B相电流  

  51.     ADC_ExternalTrigInjectedConvCtrl(ADC2,ENABLE);          //使能外部信号触发注入组转换的功能
  52.                 ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjec_TMR1_CC4);    //ADC1注入组转换的触发信号选择,注入组转换由TIM1的CC4触发
  53.     ADC_ClearFlag(ADC1, ADC_FLAG_JEC);  
  54.           ADC_INTConfig(ADC1, ADC_INT_JEC, ENABLE);                                        //这里才能打开注入组转换完成中断
  55.                
  56.                 ADC_SoftwareStartConvCtrl(ADC1, ENABLE);

3.foc算法
坐标变换核心流程
  1. A[Clarke变换] --> B[Park变换]
  2.   B --> C[PI调节器]
  3.   C --> D[反Park变换]
  4.   D --> E[SVPWM生成]
关键算法实现细节
电流环控制,q轴电流和d轴电流pid运算
  1. float IQ_PID_Proc(float feedback)
  2. {
  3.           float Err = 0.0f;
  4.           float P_term = 0.0f;
  5.           float I_term = 0.0f;
  6.           float output = 0.0f;
  7.         
  8.           Err = Foc_input.Iq_ref - feedback;
  9.         
  10.           P_term = Err*Iq_PID.P_Gain;
  11.           I_term = Err*Iq_PID.I_Gain;
  12.         
  13.           Iq_PID.I_Sum += I_term;
  14.           if(Iq_PID.I_Sum>Iq_PID.I_Sum_max) Iq_PID.I_Sum = Iq_PID.I_Sum_max;
  15.           else if(Iq_PID.I_Sum<Iq_PID.I_Sum_min)  Iq_PID.I_Sum = Iq_PID.I_Sum_min;
  16.         
  17.           output = P_term + Iq_PID.I_Sum;
  18.           if(output>Iq_PID.Max_Output) output = Iq_PID.Max_Output;
  19.           else if(output<Iq_PID.Min_Output)  output = Iq_PID.Min_Output;
  20.         
  21.           return output;
  22. }
速度观测器计算
  1.   float VoltRs[2];
  2.     float VoltdPhi[2];
  3.            
  4.           float g_fluxfluxR = 0.0f;
  5.           float sin_theta = 0.0f;
  6.           float cos_theta = 0.0f;
  7.         
  8.     //calc alpha asix flux
  9.     VoltRs[0] = Foc_observer.Rs * Foc_calc.Ialpha;
  10.           VoltdPhi[0] = Foc_calc.Valpha  - VoltRs[0];
  11.           VoltdPhi[0] += fluxR_in_wb[0] * Foc_observer.Err * Foc_observer.Gain;
  12.           flux_in_wb[0] += VoltdPhi[0] * Foc_observer.Ctrl_ts;
  13.         
  14.           //calc beta asix flux
  15.           VoltRs[1] = Foc_observer.Rs * Foc_calc.Ibeta;
  16.           VoltdPhi[1] = Foc_calc.Vbeta  - VoltRs[1];
  17.           VoltdPhi[1] += fluxR_in_wb[1] * Foc_observer.Err * Foc_observer.Gain;
  18.           flux_in_wb[1] += VoltdPhi[1] * Foc_observer.Ctrl_ts;
  19.         
  20.           //calc flux in stator
  21.     fluxS_in_wb[0] = Foc_observer.Ls * Foc_calc.Ialpha;
  22.     fluxS_in_wb[1] = Foc_observer.Ls * Foc_calc.Ibeta;
  23.                
  24.                 //calc flux in rotor
  25.     fluxR_in_wb[0] = flux_in_wb[0] - fluxS_in_wb[0];
  26.     fluxR_in_wb[1] = flux_in_wb[1] - fluxS_in_wb[1];
  27.                
  28.                 g_fluxfluxR = fluxR_in_wb[0]*fluxR_in_wb[0] + fluxR_in_wb[1]*fluxR_in_wb[1];

  29.     Foc_observer.Err = Foc_observer.Flux * Foc_observer.Flux - g_fluxfluxR;
  30.                
  31.                 sin_theta = arm_sin_f32(Foc_observer.Theta);
  32.                 cos_theta = arm_cos_f32(Foc_observer.Theta);        

  33.           Foc_observer.PLL_Err = fluxR_in_wb[1] * cos_theta - fluxR_in_wb[0] * sin_theta;
  34.     Foc_observer.PLL_Interg += Foc_observer.PLL_Err * Foc_observer.PLL_ki;        
  35.     Foc_observer.PLL_Ui = Foc_observer.PLL_Err * Foc_observer.PLL_kp + Foc_observer.PLL_Interg;   
  36.                
  37.                 Foc_observer.Theta += Foc_observer.PLL_Ui;
  38.                
  39.                
  40.                 if(Foc_observer.Theta<0.0f)
  41.                 {
  42.                           Foc_observer.Theta+=MATH_2PI;  
  43.                 }
  44.                 else if(Foc_observer.Theta>MATH_2PI)
  45.                 {
  46.                           Foc_observer.Theta-=MATH_2PI;  
  47.                 }
  48.                
  49.                 if(speed_calc_cnt<10)
  50.                 {
  51.                           speed_acc += Foc_observer.PLL_Ui;
  52.                           speed_calc_cnt++;
  53.                 }
  54.                 else
  55.                 {
  56.                                 speed_now = speed_acc/(0.001f*MATH_2PI);
  57.                                 Foc_observer.speed_hz = Foc_observer.speed_hz * 0.99f + speed_now * 0.01f;
  58.                                    
  59.                           speed_acc = 0.0f;
  60.                           speed_calc_cnt = 0;
  61.                 }
clark变换计算
  1. Foc_calc.Ialpha = (2.0f*Foc_input.Ia - Foc_input.Ib - Foc_input.Ic)/3.0f;
  2.                             Foc_calc.Ibeta = SQRT3*(Foc_input.Ib - Foc_input.Ic)/3.0f;
park变换
  1. Foc_calc.sin_Theta = arm_sin_f32(Foc_input.Theta);
  2.                             Foc_calc.cos_Theta = arm_cos_f32(Foc_input.Theta);
  3.                             Foc_calc.Id = Foc_calc.Ialpha*Foc_calc.cos_Theta + Foc_calc.Ibeta*Foc_calc.sin_Theta;
  4.                             Foc_calc.Iq = -Foc_calc.Ialpha*Foc_calc.sin_Theta + Foc_calc.Ibeta*Foc_calc.cos_Theta;
逆park变换
  1. Foc_calc.Valpha =        Foc_calc.Vd*Foc_calc.cos_Theta - Foc_calc.Vq*Foc_calc.sin_Theta;        
  2.           Foc_calc.Vbeta = Foc_calc.Vd*Foc_calc.sin_Theta + Foc_calc.Vq*Foc_calc.cos_Theta;               
SVPWM计算
据 Valpha 和 Vbeta 的值判断当前处于哪个扇区(共6个扇区)。

利用两个条件判断来确定具体扇区编号(1~6),通过累加的方式赋值给 sector。

根据不同的扇区,分别计算 Tx 和 Ty 的值。

每个扇区的公式不同,但本质是基于 SVPWM 矢量分解原理计算出对应的时间值。

如果 Tx + Ty 超过 PWM 周期 TPWM,则对TxTy 进行归一化处理,确保时间不超过周期限制。

不同扇区将 Ta,Tb, Tc 映射到三相输出 Tcmp1, Tcmp2, Tcmp3 上。

通过 switch 语句根据不同扇区选择正确的映射方式。

将计算得到的三相占空比赋值给输出结构体 Foc_output,输出到PWM上。

  1. uint8_t sector = 0;
  2.   float Tcmp1,Tcmp2,Tcmp3,Tx,Ty,f_temp,Ta,Tb,Tc;
  3.         float modulate_param = 0.0f;
  4.         
  5.   sector = 0;
  6.   Tcmp1 = 0.0f;
  7.   Tcmp2 = 0.0f;
  8.   Tcmp3 = 0.0f;        
  9.         modulate_param = Foc_input.TPWM / Foc_input.Vbus;
  10.         
  11.   if(Foc_calc.Vbeta > 0.0f) {
  12.     sector = 1;
  13.   }
  14.   
  15.   if ((SQRT3 * Foc_calc.Valpha - Foc_calc.Vbeta) / 2.0f > 0.0f) {
  16.     sector += 2;
  17.   }
  18.   
  19.   if ((-SQRT3 * Foc_calc.Valpha - Foc_calc.Vbeta) / 2.0f > 0.0f) {
  20.     sector += 4;
  21.   }
  22.   
  23.   switch(sector)
  24.         {
  25.                         case 1: //2扇区
  26.                                                 Tx = (-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
  27.                                                 Ty = (3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
  28.                                                 break;
  29.                                 
  30.                         case 2: //6扇区
  31.                                                 Tx = (3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
  32.                                                 Ty = -(SQRT3 * Foc_calc.Vbeta * modulate_param);
  33.                                                 break;
  34.                                 
  35.                         case 3: //1扇区
  36.                                                 Tx = -((-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
  37.                                                 Ty = SQRT3 * Foc_calc.Vbeta * modulate_param;
  38.                                                 break;
  39.                                 
  40.                         case 4: //4扇区
  41.                                                 Tx = -(SQRT3 * Foc_calc.Vbeta * modulate_param);
  42.                                                 Ty = (-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
  43.                                                 break;
  44.                                 
  45.                         case 5: //3扇区
  46.                                                 Tx = SQRT3 * Foc_calc.Vbeta * modulate_param;
  47.                                                 Ty = -((3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f *modulate_param);
  48.                                                 break;
  49.                                 
  50.                         default: //5扇区
  51.                                                 Tx = -((3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
  52.                                                 Ty = -((-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
  53.                                                 break;
  54.   }
  55.   
  56.   f_temp = Tx + Ty;
  57.   if(f_temp > Foc_input.TPWM)
  58.         {
  59.                         Tx = Tx/f_temp*Foc_input.TPWM;
  60.                         Ty = Ty/f_temp*Foc_input.TPWM;
  61.   }
  62.   
  63.   Ta = (Foc_input.TPWM - (Tx + Ty)) / 4.0f;
  64.   Tb = Tx / 2.0f + Ta;
  65.   Tc = Ty / 2.0f + Tb;
  66.   switch (sector)
  67.         {
  68.                         case 1:
  69.                                         Tcmp1 = Tb;
  70.                                         Tcmp2 = Ta;
  71.                                         Tcmp3 = Tc;
  72.                                         break;
  73.                                 
  74.                         case 2:
  75.                                         Tcmp1 = Ta;
  76.                                         Tcmp2 = Tc;
  77.                                         Tcmp3 = Tb;
  78.                                         break;
  79.                                 
  80.                         case 3:
  81.                                         Tcmp1 = Ta;
  82.                                         Tcmp2 = Tb;
  83.                                         Tcmp3 = Tc;
  84.                                         break;
  85.                                 
  86.                         case 4:
  87.                                         Tcmp1 = Tc;
  88.                                         Tcmp2 = Tb;
  89.                                         Tcmp3 = Ta;
  90.                                         break;
  91.                                 
  92.                         case 5:
  93.                                         Tcmp1 = Tc;
  94.                                         Tcmp2 = Ta;
  95.                                         Tcmp3 = Tb;
  96.                                         break;
  97.                                 
  98.                         case 6:
  99.                                         Tcmp1 = Tb;
  100.                                         Tcmp2 = Tc;
  101.                                         Tcmp3 = Ta;
  102.                                         break;
  103.   }
  104.   
  105.   Foc_output.Tcmp1 = Tcmp1;
  106.   Foc_output.Tcmp2 = Tcmp2;
  107.   Foc_output.Tcmp3 = Tcmp3;
  108.         
将这些移植到AT32M412中进行验证。



chenjun89 发表于 2025-6-4 22:00 来自手机 | 显示全部楼层
代码可以用代码编辑空间,这样网页显示支持性更好。
星辰相随 发表于 2025-7-18 12:55 | 显示全部楼层
楼主是把simple foc的代码转换成了C移植的吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

258

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部

61

主题

258

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部