第一次接触ST官方FOC库中的PWMC_SetPhaseVoltage函数时,我被里面复杂的数学运算绕得头晕。后来发现,理解这个函数的关键在于抓住SVPWM(空间矢量脉宽调制)的核心思想。简单来说,SVPWM就是把电机控制中的电压矢量,通过逆变器的六个开关状态表现出来。
想象一下,电机需要的电压矢量就像是指南针的指针,可以指向任意方向。而我们的逆变器只有六个基本开关状态(对应六个基本电压矢量),就像钟表上的六个整点刻度。SVPWM要做的,就是用这些"整点刻度"来合成任意角度的"指针位置"。
具体到代码实现,第一步就是要判断目标矢量落在哪个扇区。ST库中这段计算特别巧妙:
c复制wUAlpha = Valfa_beta.qV_Component1 * (int32_t)pHandle->hT_Sqrt3;
wUBeta = -(Valfa_beta.qV_Component2 * (int32_t)(pHandle->hPWMperiod)) * 2;
wX = wUBeta;
wY = (wUBeta + wUAlpha) / 2;
wZ = (wUBeta - wUAlpha) / 2;
这里hT_Sqrt3实际上是2/√3,用于归一化处理。wX、wY、wZ这三个变量包含了判断扇区的全部信息。通过它们的正负组合,就能准确锁定矢量所在的扇区。
我在调试时发现一个有趣的现象:虽然数学上需要比较√3Vα和Vβ的大小,但代码中却用wY和wZ的符号来判断。这其实是把数学运算巧妙地转换成了整数运算,避免了浮点计算,大大提高了执行效率。
确定扇区后,接下来就要计算两个相邻基本矢量的作用时间T4和T6。这部分涉及到最核心的SVPWM算法原理。
在电机控制中,我们希望合成的电压矢量幅值不超过逆变器能提供的最大值。这个最大值对应的是正六边形的内切圆半径。通过几何关系可以推导出归一化因子2/√3,这就是代码中hT_Sqrt3的由来。
实际计算时,ST库采用了非常巧妙的数学变换:
code复制T4 = (√3VαT + VβT)/2 = -wZ
T6 = -VβT = wX
看到这个等式时我恍然大悟——原来前面计算的wX和wZ不仅用于扇区判断,还直接参与了作用时间计算!这种"一鱼两吃"的设计既节省了计算量,又保证了精度。
我在实际调试中发现,当电压矢量接近扇区边界时,T4+T6可能会超过周期T。这时就需要进行过调制处理,即等比例缩小T4和T6,确保T0=T-T4-T6不小于零。ST库中通过限制寄存器值的方式隐式实现了这一点。
理解了矢量作用时间后,接下来就是如何把它们转化为实际的PWM信号。ST库采用的是中央对齐模式,这种模式有个特点:PWM周期是ARR值的两倍。
举个例子,如果设置ARR=1000,那么完整的PWM周期是从0计数到1000,再递减回0,共2000个计数时钟。这种模式能有效减小电机电流纹波,是电机控制的常用方式。
在代码中,三相PWM的比较值计算如下:
c复制wTimePhA = (int32_t)(pHandle->PWMperiod)/4 + ((wX - wZ)/(int32_t)262144);
wTimePhB = wTimePhA + wZ/131072;
wTimePhC = wTimePhB - wX/131072;
初看这些魔数131072和262144会觉得莫名其妙。其实它们对应的是Q15格式到Q0格式的转换。因为ADC采样值通常是16位左对齐的(Q15),而PWM寄存器需要的是整数(Q0),所以要进行这样的移位运算。
我在优化代码时发现,这些除法运算可以用右移操作代替:
c复制wTimePhA = (pHandle->PWMperiod >> 2) + ((wX - wZ) >> 18);
wTimePhB = wTimePhA + (wZ >> 17);
wTimePhC = wTimePhB - (wX >> 17);
这样修改后,执行效率明显提升,特别是在没有硬件除法器的MCU上。
不同扇区的PWM波形生成方式各不相同,这是SVPWM实现中最容易出错的部分。以第一扇区为例,其开关顺序是0-4-6-7-6-4-0。这种七段式调制既能保证输出电压对称,又能减少开关损耗。
在中央对齐模式下,PWM比较值的设置需要特别注意计数方向。当计数器递减时,比较事件发生在CCR值处;递增时也发生在CCR值处。因此,实际生成的PWM波形与CCR值的关系需要仔细推导。
ST库中配置TIM1的代码很典型:
c复制TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_CENTER_UP;
TIM_InitStruct.Autoreload = ((PWM_PERIOD_CYCLES) / 2);
这里ARR设置为PWM周期的一半,正是因为中央对齐模式的实际周期是ARR的两倍。我在调试时曾经错误地将ARR设为完整周期值,结果导致PWM频率只有预期的一半。
理解每个扇区的波形特征很重要。比如在第一扇区:
这种不对称性正是SVPWM精确控制电机转矩的关键所在。
在电机控制项目中调试PWMC_SetPhaseVoltage函数时,我总结出几个实用技巧:
首先是用示波器观察三相PWM波形。正常情况下应该看到中央对齐的对称波形,且不同扇区切换时波形变化平滑。如果发现波形跳动或畸变,很可能是扇区判断或时间计算有误。
其次是关注ADC采样点设置。ST库中通过pSetADCSamplingPoint函数指针来设置不同扇区的采样点。这是因为在不同扇区,电流采样的最佳时机不同。我在调试无感FOC时,就曾因为采样点设置不当导致电流波形失真。
最后要注意Q格式的转换问题。电机控制算法中大量使用定点数运算,各种Q格式之间的转换很容易出错。建议在关键计算步骤添加断言检查,确保数值范围合理。比如:
c复制assert(wX >= 0 && wX < (1<<20));
assert(wZ >= - (1<<19) && wZ < (1<<19));
将SVPWM的理论公式转化为实际代码时,需要考虑很多工程细节。比如:
定时器配置:不仅要设置中央对齐模式,还要配置死区时间。死区时间太短会导致上下管直通,太长又会引入波形失真。
电压限制:当调制比接近1时,需要进行过调制处理。ST库中通过限制T4+T6不超过周期T来实现这一点。
计算精度:在低速大转矩场景下,电压矢量幅值很小,计算误差会变得明显。这时可以采用更高精度的定点数格式,比如Q31。
执行效率:SVPWM计算需要在每个PWM周期完成,因此要尽量优化计算流程。除了用移位代替除法外,还可以使用查表法计算三角函数。
通过深入分析PWMC_SetPhaseVoltage函数,我深刻体会到优秀的电机控制代码是如何在数学精度和工程实现之间找到平衡点的。这不仅是算法的艺术,更是工程实践的结晶。