ADC 与电流采样:多通道同步采样技术

系列:电机控制系列 - 第 7 篇 目标平台:STM32F407ZGT6 阅读时间:30 分钟

前言

电流采样是 FOC 的眼睛

  • ❌ 采样不准 → FOC 失效
  • ❌ 采样太慢 → 带宽受限
  • ❌ 采样不同步 → 误差放大

本篇目标

  • ✅ 掌握 ADC 多通道同步采样
  • ✅ 理解采样时刻选择
  • ✅ 实现电流校准

一、电流采样方式

1.1 采样电阻法

低侧采样(常用):

      U_dc
       │
     ┌─┴─┐
     │ Q1│  上桥臂
     └─┬─┘
       ├──── A 相
     ┌─┴─┐
     │ Q4│  下桥臂
     └─┬─┘
       │
     ┌─┴─┐
     │Rs │  采样电阻
     └─┬─┘
       │
     GND
 
 I_a = V_Rs / Rs

优点

  • ✅ 成本低
  • ✅ 简单可靠
  • ❌ 只能采样导通时刻

高侧采样

      U_dc
       │
     ┌─┴─┐
     │Rs │  采样电阻
     └─┬─┘
     ┌─┴─┐
     │ Q1│
     └─┬─┘
       └──── A 相

优点

  • ✅ 可连续采样
  • ❌ 共模电压高(需要隔离运放)
  • ❌ 成本高

1.2 霍尔传感器

闭环霍尔(高精度):

       ┌─────┐
   ────┤ 霍尔 ├──── I_in
       │传感器│
       └──┬──┘
          │
       输出电压 ∝ 电流

优点

  • ✅ 隔离、安全
  • ✅ 精度高(0.1%)
  • ❌ 成本高(¥50-200)

二、ADC 配置

2.1 三重 ADC 同步模式

STM32F407 ADC 特性

  • 3 个 ADC(ADC1, ADC2, ADC3)
  • 最高 2.4 MSPS
  • 12-bit 分辨率

三重同步模式

 触发信号 ────┬─── ADC1 → CH1 (电流 A)
              ├─── ADC2 → CH2 (电流 B)
              └─── ADC3 → CH3 (电流 C)
 
 同时采样,同时转换

2.2 寄存器配置

 /**
  * @brief ADC 三重模式初始化
  */
 void ADC_Triple_Init(void) {
     // 1. 使能时钟
     RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_ADC2EN | RCC_APB2ENR_ADC3EN;
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN;
     
     // 2. GPIO 配置(PA1, PA2, PA3)
     GPIOA->MODER |= (3 << GPIO_MODER_MODE1_Pos) |  // 模拟模式
                     (3 << GPIO_MODER_MODE2_Pos) |
                     (3 << GPIO_MODER_MODE3_Pos);
     
     // 3. ADC 通用配置
     ADC->CCR |= (0 << ADC_CCR_ADCPRE_Pos) |   // PCLK2 / 2 = 42 MHz
                 (3 << ADC_CCR_MULTI_Pos) |     // 三重模式
                 ADC_CCR_DDS;                    // DMA 使能
     
     // 4. ADC1 配置(主 ADC)
     ADC1->CR1 |= ADC_CR1_SCAN;                 // 扫描模式
     ADC1->SQR3 = 1;                            // 通道 1
     ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_ADON;
     
     // 5. ADC2 配置(从 ADC)
     ADC2->CR1 |= ADC_CR1_SCAN;
     ADC2->SQR3 = 2;  // 通道 2
     ADC2->CR2 |= ADC_CR2_DMA | ADC_CR2_ADON;
     
     // 6. ADC3 配置(从 ADC)
     ADC3->CR1 |= ADC_CR1_SCAN;
     ADC3->SQR3 = 3;  // 通道 3
     ADC3->CR2 |= ADC_CR2_DMA | ADC_CR2_ADON;
     
     // 7. DMA 配置
     RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
     DMA2_Stream0->PAR = (uint32_t)&(ADC->CDR);  // 公共数据寄存器
     DMA2_Stream0->M0AR = (uint32_t)adc_buffer;  // 缓冲区
     DMA2_Stream0->NDTR = 3;                      // 3 个数据
     DMA2_Stream0->CR |= DMA_SxCR_CIRC |          // 循环模式
                         DMA_SxCR_MINC |          // 内存递增
                         (1 << DMA_SxCR_MSIZE_Pos) | // 16-bit
                         DMA_SxCR_EN;
 }

2.3 触发方式

定时器触发(推荐):

 // TIM1 CC4 事件触发 ADC
 TIM1->CCR4 = TIM1->ARR / 2;  // PWM 中点触发
 TIM1->CR2 |= (1 << TIM_CR2_MMS_Pos);  // CC4 为触发输出
 
 ADC1->CR2 |= (2 << ADC_CR2_EXTSEL_Pos) |  // TIM1_CC4 触发
              ADC_CR2_EXTEN;                // 上升沿

为什么在中点采样?

  • PWM 开关瞬间 → 电流振荡
  • 中点采样 → 稳定、准确

三、采样时刻优化

3.1 PWM 同步采样

 PWM 波形:
      ┌───────┐
      │       │
 ─────┘       └─────
      └── T ──┘
 
 最佳采样点:
      ┌───────┐
      │   ▲   │  ← 中点采样
 ─────┘   │   └─────
          │
        触发 ADC

代码实现

 // TIM1 CC4 配置为 PWM 中点触发
 void TIM1_Set_ADC_Trigger(void) {
     // CC4 比较值 = ARR / 2(PWM 周期中点)
     TIM1->CCR4 = TIM1->ARR / 2;
     
     // CC4 输出比较模式
     TIM1->CCMR2 |= (1 << TIM_CCMR2_OC4M_Pos);  // 匹配时触发
     
     // 使能 CC4
     TIM1->CCER |= TIM_CCER_CC4E;
 }

3.2 死区期间采样

问题:死区期间电流不可测

解决:避开死区

// 计算安全采样窗口
float T_dead = 500e-9f;  // 500ns
float T_settle = 200e-9f;  // 稳定时间 200ns

// 采样点应在 PWM 中点 ± (T_dead + T_settle)

四、电流校准

4.1 零点校准

typedef struct {
    float offset[3];  // 零点偏置
    float gain[3];    // 增益
} Current_Calib_t;

/**
 * @brief 零点校准(电机不通电)
 */
void Current_Calibrate_Zero(Current_Calib_t *calib, uint16_t *adc_raw) {
    // 采样 1000 次取平均
    int32_t sum[3] = {0};

    for (int i = 0; i < 1000; i++) {
        ADC_Start();
        while (!ADC_Complete());
        sum[0] += adc_raw[0];
        sum[1] += adc_raw[1];
        sum[2] += adc_raw[2];
    }

    calib->offset[0] = sum[0] / 1000.0f;
    calib->offset[1] = sum[1] / 1000.0f;
    calib->offset[2] = sum[2] / 1000.0f;
}

4.2 增益校准

/**
 * @brief 增益校准(施加已知电流)
 */
void Current_Calibrate_Gain(Current_Calib_t *calib, float I_known) {
    uint16_t adc_raw[3];

    // 施加已知电流 I_known
    // ...

    // 采样
    ADC_Read(adc_raw);

    // 计算增益
    float adc_delta = adc_raw[0] - calib->offset[0];
    calib->gain[0] = I_known / adc_delta;

    // 同理计算 B、C 相
}

4.3 实际电流计算

void Current_Get_Real(Current_Calib_t *calib, uint16_t *adc_raw,
                      float *ia, float *ib, float *ic) {
    *ia = (adc_raw[0] - calib->offset[0]) * calib->gain[0];
    *ib = (adc_raw[1] - calib->offset[1]) * calib->gain[1];
    *ic = (adc_raw[2] - calib->offset[2]) * calib->gain[2];

    // 基尔霍夫电流定律:ia + ib + ic = 0
    // 可用于校验
}

五、常见问题

5.1 采样噪声大

解决方法

  1. 硬件滤波(RC 低通)
  2. 软件滤波(移动平均)
  3. PCB 布局优化
// 移动平均滤波
#define FILTER_SIZE 5
float Current_Filter(float new_val) {
    static float buffer[FILTER_SIZE] = {0};
    static int index = 0;

    buffer[index] = new_val;
    index = (index + 1) % FILTER_SIZE;

    float sum = 0;
    for (int i = 0; i < FILTER_SIZE; i++) {
        sum += buffer[i];
    }

    return sum / FILTER_SIZE;
}

5.2 采样不同步

解决方法

  • ✅ 使用三重 ADC 模式
  • ✅ 硬件触发
  • ❌ 软件触发(不同步)

六、总结

电流采样要点

  1. 低侧采样(性价比高)
  2. 三重 ADC 同步采样
  3. PWM 中点触发
  4. 零点 + 增益校准

下一篇编码器与位置检测

最后修改:2026 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏