【AT-START-M412测评】+ 无刷电机控制2-开始移植
本帖最后由 穿西装的强子 于 2025-5-31 15:40 编辑#申请原创# @21小跑堂
看了很多simple foc的代码,都是用arduino写的,可移植性和操作性就变小了,感觉没那么好用方便。
而且不确定AT32F412是否支持c++,所以还是准备移植基于c语言的foc代码。
按c语言的foc进行移植设计
一、软件框图
二、软件设计
1. PWM配置
工作模式选择
[*]必须采用中央对齐模式(Center-Aligned Mode),以降低开关损耗并提高电流采样精度
[*]高级定时器(如TIM1/TIM8)需使能互补输出和刹车保护功能
频率与周期计算
[*]PWM频率推荐10–20kHz(平衡开关损耗与动态响应)
死区时间设定
[*]防止上下桥臂直通,典型值≥500ns(依据MOS管开关延迟调整)
[*]还有一些使用Nmos和Pmos实现硬件的延迟,防止短路的情况
以下是pwm配置
/* Time Base configuration */
TMR_TimeBaseStructInit(&TMR_TimeBaseStructure);
TMR_TimeBaseStructure.TMR_DIV = 0;
TMR_TimeBaseStructure.TMR_CounterMode = TMR_CounterDIR_CenterAligned1;
TMR_TimeBaseStructure.TMR_Period = PWM_PERIOD;
TMR_TimeBaseStructure.TMR_ClockDivision = TMR_CKD_DIV1;
TMR_TimeBaseStructure.TMR_RepetitionCounter = 0;
TMR_TimeBaseInit(TMR1, &TMR_TimeBaseStructure);
/* Channel 1, 2 and 3 Configuration in PWM mode */
TMR_OCStructInit(&TMR_OCInitStructure);
TMR_OCInitStructure.TMR_OCMode = TMR_OCMode_PWM2;
TMR_OCInitStructure.TMR_OutputState = TMR_OutputState_Enable;
TMR_OCInitStructure.TMR_OutputNState = TMR_OutputNState_Enable;
TMR_OCInitStructure.TMR_Pulse = PWM_TIM_PULSE>>1;
TMR_OCInitStructure.TMR_OCPolarity = TMR_OCPolarity_High;
TMR_OCInitStructure.TMR_OCNPolarity = TMR_OCNPolarity_High;
TMR_OCInitStructure.TMR_OCIdleState = TMR_OCIdleState_Reset;
TMR_OCInitStructure.TMR_OCNIdleState = TMR_OCIdleState_Reset;
TMR_OC1Init(TMR1, &TMR_OCInitStructure);
TMR_OC2Init(TMR1, &TMR_OCInitStructure);
TMR_OC3Init(TMR1, &TMR_OCInitStructure);
/* Channel 4 Configuration in PWM mode */
TMR_OCStructInit(&TMR_OCInitStructure);
TMR_OCInitStructure.TMR_OCMode = TMR_OCMode_PWM1;
TMR_OCInitStructure.TMR_OutputState = TMR_OutputState_Enable;
TMR_OCInitStructure.TMR_OutputNState = TMR_OutputNState_Enable;
TMR_OCInitStructure.TMR_Pulse = 1;
TMR_OCInitStructure.TMR_OCPolarity = TMR_OCPolarity_High;
TMR_OCInitStructure.TMR_OCNPolarity = TMR_OCNPolarity_High;
TMR_OCInitStructure.TMR_OCIdleState = TMR_OCIdleState_Reset;
TMR_OCInitStructure.TMR_OCNIdleState = TMR_OCIdleState_Reset;
TMR_OC4Init(TMR1, &TMR_OCInitStructure);
/* Enables the TIM1 Preload on CC1 Register */
TMR_OC1PreloadConfig(TMR1, TMR_OCPreload_Enable);
/* Enables the TIM1 Preload on CC2 Register */
TMR_OC2PreloadConfig(TMR1, TMR_OCPreload_Enable);
/* Enables the TIM1 Preload on CC3 Register */
TMR_OC3PreloadConfig(TMR1, TMR_OCPreload_Enable);
/* Enables the TIM1 Preload on CC4 Register */
TMR_OC4PreloadConfig(TMR1, TMR_OCPreload_Enable);
/* Automatic Output enable, Break, dead time and lock configuration*/
TMR_BRKDTStructInit(&TMR_BDTRInitStructure);
TMR_BDTRInitStructure.TMR_OSIMRState = TMR_OSIMRState_Enable;
TMR_BDTRInitStructure.TMR_OSIMIState = TMR_OSIMIState_Enable;
TMR_BDTRInitStructure.TMR_LOCKgrade = TMR_LOCKgrade_1;
TMR_BDTRInitStructure.TMR_DeadTime = DEADTIME;
TMR_BDTRInitStructure.TMR_Break = TMR_Break_Enable;
TMR_BDTRInitStructure.TMR_BreakPolarity = TMR_BreakPolarity_Low;
TMR_BDTRInitStructure.TMR_AutomaticOutput = TMR_AutomaticOutput_Disable;
TMR_BRKDTConfig(TMR1, &TMR_BDTRInitStructure);
TMR_ARPreloadConfig(TMR1,ENABLE);
TMR_SelectOutputTrigger(TMR1, TMR_TRGOSource_OC4Ref);
TMR_ClearITPendingBit(TMR1, TMR_INT_Break);
TMR_INTConfig(TMR1, TMR_INT_Break, ENABLE);
/* TMR1 counter enable */
TMR_Cmd(TMR1, ENABLE);
/* Main Output Enable */
TMR_CtrlPWMOutputs(TMR1, DISABLE);
2.ADC的配置
采样时机对齐
[*]ADC采样点必须设在PWM波形的波谷处(中央对齐模式中点),避开开关噪声干扰
[*]使用定时器TRGO触发信号联动ADC启动,而非软件触发
[*]
以下是ADC配置
ADC_InitType ADC_InitStructure;
/* ADCCLK = PCLK2/6 */
RCC_ADCCLKConfig(RCC_APB2CLK_Div6);
//使用双ADC模式,ADC1为主,ADC2为从。当ADC转换配置成由外部事件触发时,用户必须设置成仅触发主ADC,从ADC设置成软件触发,这样可以防止意外的触发从转换。
//但是,主和从ADC的外部触发必须同时被激活,要调用 ADC_ExternalTrigConvCmd(ADC2, ENABLE);
//ADC1配置
ADC_Reset(ADC1);
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult; //ADC1工作在注入模式
ADC_InitStructure.ADC_ScanMode = ENABLE; //模数转换工作在扫描模式(多通道)还是单次(单通道)模式
ADC_InitStructure.ADC_ContinuousMode = ENABLE; //模数转换工作在扫描模式(多通道)还是单次(单通道)模式
ADC_InitStructure.ADC_ExternalTrig = ADC_ExternalTrig_None;//转换由软件启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据左对齐
ADC_InitStructure.ADC_NumOfChannel = 1; //规定了顺序进行规则转换的ADC通道的数目。这个数目的取值范围是1到16
ADC_Init(ADC1, &ADC_InitStructure);
//ADC规则通道设置
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_13_5);
//ADC2配置
ADC_Reset(ADC2);
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_InjecSimult;//ADC2工作在注入模式
ADC_InitStructure.ADC_ScanMode = ENABLE;
ADC_InitStructure.ADC_ContinuousMode = DISABLE; //连续转换模式,触发后就会一直转换
ADC_InitStructure.ADC_ExternalTrig = ADC_ExternalTrig_None;//双ADC模式的从ADC必须设置为软件触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NumOfChannel = 1;
ADC_Init(ADC2, &ADC_InitStructure);
ADC_Ctrl(ADC1, ENABLE); //ADC1使能
ADC_RstCalibration(ADC1); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准寄存器复位完成
ADC_StartCalibration(ADC1); //ADC1开始校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
ADC_Ctrl(ADC2, ENABLE); //ADC2使能
ADC_RstCalibration(ADC2); //复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC2)); //等待校准寄存器复位完成
ADC_StartCalibration(ADC2); //ADC2开始校准
while(ADC_GetCalibrationStatus(ADC2)); //等待校准完成
ADC_get_offset();
//ADC1 注入通道配置
ADC_InjectedSequencerLengthConfig(ADC1,1);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_7_5); //A相电流
//ADC2 注入通道配置
ADC_InjectedSequencerLengthConfig(ADC2,1); //设置ADC2注入组通道数量
ADC_InjectedChannelConfig(ADC2, ADC_Channel_5, 1,ADC_SampleTime_7_5); //B相电流
ADC_ExternalTrigInjectedConvCtrl(ADC2,ENABLE); //使能外部信号触发注入组转换的功能
ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjec_TMR1_CC4); //ADC1注入组转换的触发信号选择,注入组转换由TIM1的CC4触发
ADC_ClearFlag(ADC1, ADC_FLAG_JEC);
ADC_INTConfig(ADC1, ADC_INT_JEC, ENABLE); //这里才能打开注入组转换完成中断
ADC_SoftwareStartConvCtrl(ADC1, ENABLE);
3.foc算法
坐标变换核心流程 A --> B
B --> C
C --> D[反Park变换]
D --> E关键算法实现细节电流环控制,q轴电流和d轴电流pid运算float IQ_PID_Proc(float feedback)
{
float Err = 0.0f;
float P_term = 0.0f;
float I_term = 0.0f;
float output = 0.0f;
Err = Foc_input.Iq_ref - feedback;
P_term = Err*Iq_PID.P_Gain;
I_term = Err*Iq_PID.I_Gain;
Iq_PID.I_Sum += I_term;
if(Iq_PID.I_Sum>Iq_PID.I_Sum_max) Iq_PID.I_Sum = Iq_PID.I_Sum_max;
else if(Iq_PID.I_Sum<Iq_PID.I_Sum_min)Iq_PID.I_Sum = Iq_PID.I_Sum_min;
output = P_term + Iq_PID.I_Sum;
if(output>Iq_PID.Max_Output) output = Iq_PID.Max_Output;
else if(output<Iq_PID.Min_Output)output = Iq_PID.Min_Output;
return output;
}速度观测器计算float VoltRs;
float VoltdPhi;
float g_fluxfluxR = 0.0f;
float sin_theta = 0.0f;
float cos_theta = 0.0f;
//calc alpha asix flux
VoltRs = Foc_observer.Rs * Foc_calc.Ialpha;
VoltdPhi = Foc_calc.Valpha- VoltRs;
VoltdPhi += fluxR_in_wb * Foc_observer.Err * Foc_observer.Gain;
flux_in_wb += VoltdPhi * Foc_observer.Ctrl_ts;
//calc beta asix flux
VoltRs = Foc_observer.Rs * Foc_calc.Ibeta;
VoltdPhi = Foc_calc.Vbeta- VoltRs;
VoltdPhi += fluxR_in_wb * Foc_observer.Err * Foc_observer.Gain;
flux_in_wb += VoltdPhi * Foc_observer.Ctrl_ts;
//calc flux in stator
fluxS_in_wb = Foc_observer.Ls * Foc_calc.Ialpha;
fluxS_in_wb = Foc_observer.Ls * Foc_calc.Ibeta;
//calc flux in rotor
fluxR_in_wb = flux_in_wb - fluxS_in_wb;
fluxR_in_wb = flux_in_wb - fluxS_in_wb;
g_fluxfluxR = fluxR_in_wb*fluxR_in_wb + fluxR_in_wb*fluxR_in_wb;
Foc_observer.Err = Foc_observer.Flux * Foc_observer.Flux - g_fluxfluxR;
sin_theta = arm_sin_f32(Foc_observer.Theta);
cos_theta = arm_cos_f32(Foc_observer.Theta);
Foc_observer.PLL_Err = fluxR_in_wb * cos_theta - fluxR_in_wb * sin_theta;
Foc_observer.PLL_Interg += Foc_observer.PLL_Err * Foc_observer.PLL_ki;
Foc_observer.PLL_Ui = Foc_observer.PLL_Err * Foc_observer.PLL_kp + Foc_observer.PLL_Interg;
Foc_observer.Theta += Foc_observer.PLL_Ui;
if(Foc_observer.Theta<0.0f)
{
Foc_observer.Theta+=MATH_2PI;
}
else if(Foc_observer.Theta>MATH_2PI)
{
Foc_observer.Theta-=MATH_2PI;
}
if(speed_calc_cnt<10)
{
speed_acc += Foc_observer.PLL_Ui;
speed_calc_cnt++;
}
else
{
speed_now = speed_acc/(0.001f*MATH_2PI);
Foc_observer.speed_hz = Foc_observer.speed_hz * 0.99f + speed_now * 0.01f;
speed_acc = 0.0f;
speed_calc_cnt = 0;
}clark变换计算 Foc_calc.Ialpha = (2.0f*Foc_input.Ia - Foc_input.Ib - Foc_input.Ic)/3.0f;
Foc_calc.Ibeta = SQRT3*(Foc_input.Ib - Foc_input.Ic)/3.0f; park变换 Foc_calc.sin_Theta = arm_sin_f32(Foc_input.Theta);
Foc_calc.cos_Theta = arm_cos_f32(Foc_input.Theta);
Foc_calc.Id = Foc_calc.Ialpha*Foc_calc.cos_Theta + Foc_calc.Ibeta*Foc_calc.sin_Theta;
Foc_calc.Iq = -Foc_calc.Ialpha*Foc_calc.sin_Theta + Foc_calc.Ibeta*Foc_calc.cos_Theta;逆park变换Foc_calc.Valpha = Foc_calc.Vd*Foc_calc.cos_Theta - Foc_calc.Vq*Foc_calc.sin_Theta;
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,则对Tx 和 Ty 进行归一化处理,确保时间不超过周期限制。
不同扇区将 Ta,Tb, Tc 映射到三相输出 Tcmp1, Tcmp2, Tcmp3 上。
通过 switch 语句根据不同扇区选择正确的映射方式。
将计算得到的三相占空比赋值给输出结构体 Foc_output,输出到PWM上。
uint8_t sector = 0;
float Tcmp1,Tcmp2,Tcmp3,Tx,Ty,f_temp,Ta,Tb,Tc;
float modulate_param = 0.0f;
sector = 0;
Tcmp1 = 0.0f;
Tcmp2 = 0.0f;
Tcmp3 = 0.0f;
modulate_param = Foc_input.TPWM / Foc_input.Vbus;
if(Foc_calc.Vbeta > 0.0f) {
sector = 1;
}
if ((SQRT3 * Foc_calc.Valpha - Foc_calc.Vbeta) / 2.0f > 0.0f) {
sector += 2;
}
if ((-SQRT3 * Foc_calc.Valpha - Foc_calc.Vbeta) / 2.0f > 0.0f) {
sector += 4;
}
switch(sector)
{
case 1: //2扇区
Tx = (-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
Ty = (3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
break;
case 2: //6扇区
Tx = (3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
Ty = -(SQRT3 * Foc_calc.Vbeta * modulate_param);
break;
case 3: //1扇区
Tx = -((-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
Ty = SQRT3 * Foc_calc.Vbeta * modulate_param;
break;
case 4: //4扇区
Tx = -(SQRT3 * Foc_calc.Vbeta * modulate_param);
Ty = (-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param;
break;
case 5: //3扇区
Tx = SQRT3 * Foc_calc.Vbeta * modulate_param;
Ty = -((3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f *modulate_param);
break;
default: //5扇区
Tx = -((3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
Ty = -((-3.0f * Foc_calc.Valpha + SQRT3 * Foc_calc.Vbeta)/2.0f * modulate_param);
break;
}
f_temp = Tx + Ty;
if(f_temp > Foc_input.TPWM)
{
Tx = Tx/f_temp*Foc_input.TPWM;
Ty = Ty/f_temp*Foc_input.TPWM;
}
Ta = (Foc_input.TPWM - (Tx + Ty)) / 4.0f;
Tb = Tx / 2.0f + Ta;
Tc = Ty / 2.0f + Tb;
switch (sector)
{
case 1:
Tcmp1 = Tb;
Tcmp2 = Ta;
Tcmp3 = Tc;
break;
case 2:
Tcmp1 = Ta;
Tcmp2 = Tc;
Tcmp3 = Tb;
break;
case 3:
Tcmp1 = Ta;
Tcmp2 = Tb;
Tcmp3 = Tc;
break;
case 4:
Tcmp1 = Tc;
Tcmp2 = Tb;
Tcmp3 = Ta;
break;
case 5:
Tcmp1 = Tc;
Tcmp2 = Ta;
Tcmp3 = Tb;
break;
case 6:
Tcmp1 = Tb;
Tcmp2 = Tc;
Tcmp3 = Ta;
break;
}
Foc_output.Tcmp1 = Tcmp1;
Foc_output.Tcmp2 = Tcmp2;
Foc_output.Tcmp3 = Tcmp3;
将这些移植到AT32M412中进行验证。
代码可以用代码编辑空间,这样网页显示支持性更好。
页:
[1]