3. 电流采样与重构

任务目标

在无刷电机FOC控制中,电流环需要精确的相电流反馈。本案例设计基于单电阻的母线电流采样方案,利用PWM中心对齐模式下各相占空比的时间关系,在一个PWM周期内通过两次ADC采样重构出两相电流,并推导第三相电流,同时完成采样时刻的自动校准。

硬件与采样原理

单电阻采样将采样电阻串联在逆变器直流母线负端,通过检测不同开关状态下流过母线的电流来重构相电流。在中心对齐PWM中,当某一相上桥臂导通、其余两相下桥臂导通时,母线电流等于该相电流;当两相上桥臂导通时,母线电流等于第三相(未导通相)电流的负值。

采样窗口的有效性取决于各相占空比之间的差值。若两相占空比过于接近,对应的开关状态持续时间过短,无法完成ADC采样。因此需要根据当前占空比矢量选择最优的两次采样点,并确保采样时刻避开开关噪声。

核心代码实现

// 单电阻电流采样与重构 - 基于STM32 HAL库
// 依赖:TIM1中心对齐PWM,ADC1注入组采样,DMA传输

#define PWM_PERIOD          2400    // 定时器周期(中心对齐模式下为半周期值)
#define ADC_SAMPLE_TIME_NS  500     // ADC采样时间约500ns
#define DEAD_TIME_NS        200     // 死区时间
#define MIN_SAMPLE_WINDOW   1200    // 最小可靠采样窗口(ns),需大于ADC采样+死区

typedef struct {
    uint16_t duty_a;    // A相占空比(0~PWM_PERIOD)
    uint16_t duty_b;
    uint16_t duty_c;
    float ia, ib, ic;   // 重构后的三相电流(A)
    uint8_t  sample_valid;
} CurrentSensor_t;

CurrentSensor_t motor_current;

// 根据三相占空比选择采样扇区,返回两次采样的定时器比较值
void SelectSamplePoint(uint16_t *ccr_sample1, uint16_t *ccr_sample2) {
    uint16_t d[3] = {motor_current.duty_a, motor_current.duty_b, motor_current.duty_c};
    uint8_t idx[3] = {0, 1, 2};
    
    // 冒泡排序:d[0]最小,d[2]最大
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2 - i; j++) {
            if (d[j] > d[j+1]) {
                uint16_t tmp = d[j]; d[j] = d[j+1]; d[j+1] = tmp;
                uint8_t tmpi = idx[j]; idx[j] = idx[j+1]; idx[j+1] = tmpi;
            }
        }
    }
    
    uint16_t mid = d[1];
    uint16_t min_val = d[0];
    uint16_t max_val = d[2];
    
    // 采样窗口检查:中间相与最小/最大相的差值需足够大
    uint16_t window1 = mid - min_val;   // 对应开关状态:中间相上桥导通
    uint16_t window2 = max_val - mid;   // 对应开关状态:最大相上桥导通
    
    // 将占空比差值转换为时间(ns)
    float clk_ns = 1.0f / 72e6 * 1e9;   // 72MHz定时器时钟
    float t_window1 = window1 * clk_ns;
    float t_window2 = window2 * clk_ns;
    
    if (t_window1 < MIN_SAMPLE_WINDOW || t_window2 < MIN_SAMPLE_WINDOW) {
        motor_current.sample_valid = 0;  // 扇区过小,无法可靠采样
        return;
    }
    
    // 采样点设置在窗口中间,避开死区和开关噪声
    // 第一次采样:中间相上桥导通期间(对应电流为中间相电流)
    *ccr_sample1 = min_val + window1 / 2;
    // 第二次采样:最大相上桥导通期间(对应电流为最大相电流的负值)
    *ccr_sample2 = mid + window2 / 2;
    
    motor_current.sample_valid = 1;
}

// ADC注入组转换完成回调(由DMA在两次采样完成后触发)
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc) {
    if (hadc->Instance != ADC1) return;
    
    uint32_t raw1 = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1);
    uint32_t raw2 = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_2);
    
    // 转换为电流值(假设采样电阻0.01Ω,运放增益50倍,ADC参考3.3V,12位)
    float v1 = (float)raw1 / 4095.0f * 3.3f;
    float v2 = (float)raw2 / 4095.0f * 3.3f;
    float i1 = v1 / (0.01f * 50.0f);   // 第一次采样电流
    float i2 = v2 / (0.01f * 50.0f);   // 第二次采样电流
    
    // 根据排序索引映射到实际相电流
    // 第一次采样:中间相上桥导通 → 母线电流 = 中间相电流
    // 第二次采样:最大相上桥导通 → 母线电流 = -最大相电流
    uint16_t d[3] = {motor_current.duty_a, motor_current.duty_b, motor_current.duty_c};
    uint8_t idx[3] = {0, 1, 2};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2 - i; j++) {
            if (d[j] > d[j+1]) {
                uint16_t tmp = d[j]; d[j] = d[j+1]; d[j+1] = tmp;
                uint8_t tmpi = idx[j]; idx[j] = idx[j+1]; idx[j+1] = tmpi;
            }
        }
    }
    
    float i_mid = i1;      // 中间相电流
    float i_max = -i2;     // 最大相电流(负号因为母线电流方向)
    
    // 写入对应相
    float *phase_currents[3] = {&motor_current.ia, &motor_current.ib, &motor_current.ic};
    *phase_currents[idx[1]] = i_mid;
    *phase_currents[idx[2]] = i_max;
    // 第三相电流 = -(ia + ib)
    *phase_currents[idx[0]] = -(i_mid + i_max);
}

// 在主循环中调用:更新占空比并触发采样
void CurrentSample_Update(uint16_t duty_a, uint16_t duty_b, uint16_t duty_c) {
    motor_current.duty_a = duty_a;
    motor_current.duty_b = duty_b;
    motor_current.duty_c = duty_c;
    
    uint16_t ccr1, ccr2;
    SelectSamplePoint(&ccr1, &ccr2);
    
    if (motor_current.sample_valid) {
        // 配置ADC注入触发源为TIM1的CC1和CC2事件
        TIM1->CCR1 = ccr1;   // 第一次采样时刻
        TIM1->CCR2 = ccr2;   // 第二次采样时刻
        // 使能ADC注入组转换
        HAL_ADCEx_InjectedStart_IT(&hadc1);
    }
}

采样时序校准

实际电路中,开关管驱动延迟、ADC采样保持电容充电时间会导致采样电流与理论值存在偏差。校准方法:在电机静止且无电流时,注入一个已知占空比(如50%),测量ADC读数作为零偏;然后逐步调整采样点位置(在窗口内前后移动),观察重构电流的纹波,选择纹波最小时的采样点作为最佳时刻。

运行与验证

  1. 硬件连接:将采样电阻(0.01Ω)接入逆变器负端与地之间,信号经差分运放(增益50倍)后连接ADC1的注入通道。
  2. 软件配置:TIM1配置为中央对齐模式(PWM模式1),更新事件在计数器上溢/下溢时产生;ADC1注入组配置为2个采样序列,触发源分别设为TIM1的CC1和CC2事件。
  3. 验证步骤:
    • 电机开环运行(给定固定占空比),用示波器测量采样电阻两端电压,确认波形为阶梯状母线电流。
    • 通过调试器读取motor_current.ia/ib/ic,验证三相电流之和近似为零(误差<5%)。
    • 在低速大电流工况下,检查sample_valid标志:若频繁为0,说明采样窗口不足,需提高PWM载波频率或增加最小采样窗口阈值。

通过条件:当电机以恒定负载运行时,重构的三相电流波形平滑、无畸变,且与相电流探头实测值偏差小于10%。

中心对齐PWM单电阻采样时序 t 载波 A相 B相 C相 S1 S2 窗口1 窗口2 S1期间:B相上桥导通 → 母线电流 = ib S2期间:C相上桥导通 → 母线电流 = -ic 扇区1 扇区2 扇区3

常见问题

  • 采样点靠近PWM边沿导致噪声:将采样点向窗口中心移动,并增加硬件RC滤波(截止频率约1MHz)。
  • 低调制比时窗口过小:当某两相占空比接近时,可改用移相PWM或增加最小脉宽限制。
  • ADC采样保持时间不足:确保ADC采样时间设置大于500ns,或降低定时器时钟频率。