- /* 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[Clarke变换] --> B[Park变换]
- B --> C[PI调节器]
- C --> D[反Park变换]
- D --> E[SVPWM生成]
关键算法实现细节
电流环控制,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[2];
- float VoltdPhi[2];
-
- float g_fluxfluxR = 0.0f;
- float sin_theta = 0.0f;
- float cos_theta = 0.0f;
-
- //calc alpha asix flux
- VoltRs[0] = Foc_observer.Rs * Foc_calc.Ialpha;
- VoltdPhi[0] = Foc_calc.Valpha - VoltRs[0];
- VoltdPhi[0] += fluxR_in_wb[0] * Foc_observer.Err * Foc_observer.Gain;
- flux_in_wb[0] += VoltdPhi[0] * Foc_observer.Ctrl_ts;
-
- //calc beta asix flux
- VoltRs[1] = Foc_observer.Rs * Foc_calc.Ibeta;
- VoltdPhi[1] = Foc_calc.Vbeta - VoltRs[1];
- VoltdPhi[1] += fluxR_in_wb[1] * Foc_observer.Err * Foc_observer.Gain;
- flux_in_wb[1] += VoltdPhi[1] * Foc_observer.Ctrl_ts;
-
- //calc flux in stator
- fluxS_in_wb[0] = Foc_observer.Ls * Foc_calc.Ialpha;
- fluxS_in_wb[1] = Foc_observer.Ls * Foc_calc.Ibeta;
-
- //calc flux in rotor
- fluxR_in_wb[0] = flux_in_wb[0] - fluxS_in_wb[0];
- fluxR_in_wb[1] = flux_in_wb[1] - fluxS_in_wb[1];
-
- g_fluxfluxR = fluxR_in_wb[0]*fluxR_in_wb[0] + fluxR_in_wb[1]*fluxR_in_wb[1];
- 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[1] * cos_theta - fluxR_in_wb[0] * 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上。