一、问题现象描述
在某些应用场景如空调,电动自行车,水泵等,因为成本的关系,通常使用基于下桥的三相电阻采样方案,此时只有下桥开通时才有电流流过检流电阻,因此只有在下桥开通时才能采样到有效电流。

使用传统的三相电流重构算法,在高调制率 (大约大于 80%) 时因下桥开通时间太短,所以无法采到有效三相电流,从而导致电机无法正常运行。
母线为 5A 电流的采样波形
此时通常通过限制调制率 (也就是将三项的 PWM 峰值控制在一定范围内) 的方法来保证电机可靠运行,但这样会限制母线电压利用率,从而影响电机运行效率。
二、相位选择处理
2.1 原理解析
下图为下桥三相 PWM 控制输出波形:

较为简单常用的办法是:
当 T1 <= T2 和 T3 时,只采样 U 相和 V 相电流,那么 W 相电流也就等于:
= W 相电流
= U 相电流
= V 相电流
当 T2 <= T1 和 T3 时,只采样 U 相和 W 相电流,那么 V 相电流也就等于:
当 T3 <= T1 和 T2 时,只采样 V 相和 W 相电流,那么 U 相电流也就等于:
2.2 代码演示
t_SVPWM_GenRegS.s32_DutyA 是 A 相上管的开管时间。
上管开管时间越长,下管开管时间越短,所以我们取上管开管最长的相不采集,使用互补计算,全部代码如下:
cpp
f_MC_ADC_Channel_Change()
{
uint8_t adc_sample = 0;
if ((t_SVPWM_GenRegS.s32_DutyA <= t_SVPWM_GenRegS.s32_DutyC) && (t_SVPWM_GenRegS.s32_DutyA >= t_SVPWM_GenRegS.s32_DutyB))
{
adc_sample = 2;
}
else if ((t_SVPWM_GenRegS.s32_DutyB <= t_SVPWM_GenRegS.s32_DutyA) && (t_SVPWM_GenRegS.s32_DutyB >= t_SVPWM_GenRegS.s32_DutyC))
{
adc_sample = 3;
}
else if ((t_SVPWM_GenRegS.s32_DutyC <= t_SVPWM_GenRegS.s32_DutyA) && (t_SVPWM_GenRegS.s32_DutyC >= t_SVPWM_GenRegS.s32_DutyB))
{
adc_sample = 1;
}
if (adc_sample < 10)
{
switch (adc_sample)
{
case 1:
s32_Temp_Phae_Cur_C = 0 - s32_Temp_Phae_Cur_A - s32_Temp_Phae_Cur_B;
break;
case 2:
s32_Temp_Phae_Cur_A = 0 - s32_Temp_Phae_Cur_C - s32_Temp_Phae_Cur_B;
break;
case 3:
s32_Temp_Phae_Cur_B = 0 - s32_Temp_Phae_Cur_A - s32_Temp_Phae_Cur_C;
break;
}
}
}
2.3 效果演示
在使用相位选择的逻辑后,我们的采样波形略好一点,但仍然不是正弦波。
母线为 5A 电流的采样波形
三、采样窗口时间选择
3.1 中心对齐模式
我们使用中心对齐模式进行 PWM 波形的输出。
在凌欧的芯片中,PWM 中心对齐模式是从负数开始计数的。

假设 m_EPWM_PERIOD 是PWM 定时器在中心对齐模式下的半周期计数值,那么我们整个就是就是这样的规律:
-
- EPWM_PERIOD (起始点) → 0 (中心点) → + EPWM_PERIOD (结束点)
3.2 采样窗口选择
在选择每个PWM 周期的采样窗口 (采样时刻) 时,并不是下管打开的时候就是最佳的采样时刻。
而是应该避开:
- 刚发生 PWM 翻转 (dv/dt、di/dt)
- 死区刚结束 / 管子刚导通
- 电流振铃还没消
一个采样窗口是 某一相导通且电流稳定的最小连续时间。
这个时间至少要包含,振铃时间、死区/开管时间、ADC采样保持时间。当我们
3.1 三项完整窗口采样
如果我们有一个完整的采样窗口,也就是:
= PWM 周期计数
= 三项中最大的计数
= 窗口时间 (振铃、死区和开关管时间)
很显然,我们最佳的采样的时间是 PWM 周期中的起始点,这里举例开关管的振铃最小,因为我们是中心对齐模式,所以在公式上,我们采样窗口就是:
也就是每个 PWM 周期的起点采样,采样时刻如下图所示:

3.2 三项半个窗口采样
如果我们 PWM 在开始不足一个采样窗口,但是大于半个窗口:
= PWM 周期计数
= 三项中最大的计数
= 窗口时间 (振铃、死区和开关管时间)
此时我们的采样方式计算为:
也就是下图这个区域:

3.3 两项完整窗口采样
如果三项半个窗口都不满足,我们只能在两项窗口区采集。

3.4 全部代码
我们把两种采样方式融合到一个函数中:
cpp
f_MC_ADC_Channel_Change()
{
uint8_t adc_sample = 0;
if ((t_SVPWM_GenRegS.s32_DutyA <= t_SVPWM_GenRegS.s32_DutyC) && (t_SVPWM_GenRegS.s32_DutyA >= t_SVPWM_GenRegS.s32_DutyB))
{
adc_sample = 2;
}
else if ((t_SVPWM_GenRegS.s32_DutyB <= t_SVPWM_GenRegS.s32_DutyA) && (t_SVPWM_GenRegS.s32_DutyB >= t_SVPWM_GenRegS.s32_DutyC))
{
adc_sample = 3;
}
else if ((t_SVPWM_GenRegS.s32_DutyC <= t_SVPWM_GenRegS.s32_DutyA) && (t_SVPWM_GenRegS.s32_DutyC >= t_SVPWM_GenRegS.s32_DutyB))
{
adc_sample = 1;
}
if (adc_sample < 10)
{
switch (adc_sample)
{
case 1:
s32_Temp_Phae_Cur_C = 0 - s32_Temp_Phae_Cur_A - s32_Temp_Phae_Cur_B;
break;
case 2:
s32_Temp_Phae_Cur_A = 0 - s32_Temp_Phae_Cur_C - s32_Temp_Phae_Cur_B;
break;
case 3:
s32_Temp_Phae_Cur_B = 0 - s32_Temp_Phae_Cur_A - s32_Temp_Phae_Cur_C;
break;
}
}
#define RINGING_TIME 2 // 振铃
#define TON_TIME 2 // 死区加开管时间
#define SAMPLE_TIME 1 // 采样+关管时间
if ((m_EPWM_PERIOD - tmax) > ((RINGING_TIME + TON_TIME) * PWM_1US))
{
temp = 1 - m_EPWM_PERIOD;
MCPWM_TMR0 = (u16)(temp);
}
else if ((m_EPWM_PERIOD - tmax) > (((RINGING_TIME + TON_TIME) + SAMPLE_TIME) / 2 * PWM_1US))
{
temp = ((RINGING_TIME + TON_TIME) * PWM_1US + tmax - m_EPWM_PERIOD * 2 + 1);
if (temp < 1 - m_EPWM_PERIOD)
temp = 1 - m_EPWM_PERIOD;
}
else
{
if ((tmax - tmin) > (((RINGING_TIME + TON_TIME) + SAMPLE_TIME) * PWM_1US /* - 30*/))
{
temp = ((-tmax) + ((RINGING_TIME + TON_TIME) * PWM_1US));
t1 = temp;
if (temp < 1 - m_EPWM_PERIOD)
temp = 1 - m_EPWM_PERIOD;
MCPWM_TMR0 = (uint16_t)temp;
}
else // 无法采样
{
temp = 1 - m_EPWM_PERIOD;
MCPWM_TMR0 = (u16)(temp);
adc_sample += 0x10;
}
}
}
3.5 效果演示
可见,较为完整的还原出了正弦波:
母线为 5A 电流的采样波形