4. 电流采样与重构

任务目标

在STM32 FOC控制中,电机相电流是电流环反馈的关键输入。本案例实现基于定时器触发ADC注入组采样的双电阻电流重构方案:在PWM中心对齐计数模式下,利用两个下桥臂导通时刻采样U、V相电流,并通过基尔霍夫定律重构W相电流,最终验证采样时序正确性与重构精度。

硬件与依赖

  • 硬件:STM32G431(或同类带ADC注入组的FOC专用MCU)、DRV8323或类似栅极驱动器、双电阻采样板(采样电阻位于下桥臂GND侧)
  • 软件:STM32CubeMX生成的HAL工程,启用TIM1高级定时器(中心对齐PWM模式)、ADC1注入组、DMA

采样原理与时序

采用七段式SVPWM,PWM周期中心处两个零矢量(000/111)作用,此时下桥臂全部导通,电流经下桥臂续流,采样电阻两端电压正比于相电流。在中心对齐计数的上溢/下溢中断点触发ADC注入组采样,可同时采集U、V两相电流。W相电流由iw = -(iu + iv)计算。采样窗口需避开PWM边沿附近的开关噪声,因此ADC触发点应设置在计数顶点或底点并插入适当死区补偿。

关键完整代码

// adc_current_sensor.h
#ifndef ADC_CURRENT_SENSOR_H
#define ADC_CURRENT_SENSOR_H

#include "main.h"

typedef struct {
    float iu;      // U相电流 (A)
    float iv;      // V相电流 (A)
    float iw;      // W相电流 (A)
    float iu_offset;  // U相零电流偏置 (ADC码)
    float iv_offset;  // V相零电流偏置 (ADC码)
    float adc_to_amp; // ADC码转电流系数 (A/LSB)
} CurrentSensor_t;

void CurrentSensor_Init(CurrentSensor_t *sensor);
void CurrentSensor_ReadPhaseCurrents(CurrentSensor_t *sensor);
void CurrentSensor_CalibrateOffsets(CurrentSensor_t *sensor);

#endif
// adc_current_sensor.c
#include "adc_current_sensor.h"
#include "tim.h"
#include "adc.h"

extern ADC_HandleTypeDef hadc1;
extern TIM_HandleTypeDef htim1;

static volatile uint32_t adc_buffer[2];  // DMA存储注入组转换结果

void CurrentSensor_Init(CurrentSensor_t *sensor) {
    // 假设供电电压3.3V,运放增益10倍,采样电阻0.01Ω
    // ADC 12位分辨率,满量程4096
    // 电流系数 = Vref / (4096 * Rshunt * Gain) = 3.3 / (4096 * 0.01 * 10)
    sensor->adc_to_amp = 3.3f / (4096.0f * 0.01f * 10.0f);
    sensor->iu_offset = 2048.0f;  // 零电流时ADC中点(需校准)
    sensor->iv_offset = 2048.0f;
    sensor->iu = 0.0f;
    sensor->iv = 0.0f;
    sensor->iw = 0.0f;
}

void CurrentSensor_CalibrateOffsets(CurrentSensor_t *sensor) {
    // 电机静止且下桥臂全部导通时采样,取多次平均作为零电流偏置
    uint32_t sum_u = 0, sum_v = 0;
    const int samples = 100;
    for (int i = 0; i < samples; i++) {
        HAL_ADCEx_InjectedStart(&hadc1);  // 软件触发一次注入组转换
        HAL_ADCEx_InjectedPollForConversion(&hadc1, 10);
        sum_u += HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1);
        sum_v += HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_2);
    }
    sensor->iu_offset = (float)sum_u / samples;
    sensor->iv_offset = (float)sum_v / samples;
}

void CurrentSensor_ReadPhaseCurrents(CurrentSensor_t *sensor) {
    // 读取DMA搬运的注入组转换结果(由TIM1触发)
    uint32_t adc_u = adc_buffer[0];
    uint32_t adc_v = adc_buffer[1];

    // 转换为实际电流值(考虑方向:下桥臂采样,电流流入为正)
    sensor->iu = (adc_u - sensor->iu_offset) * sensor->adc_to_amp;
    sensor->iv = (adc_v - sensor->iv_offset) * sensor->adc_to_amp;
    sensor->iw = -(sensor->iu + sensor->iv);  // 基尔霍夫重构
}

// 在TIM1周期中断中调用(中心对齐模式,上溢/下溢均触发)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim == &htim1) {
        // 读取最新采样结果(DMA已自动更新)
        // 此处可加入电流环控制代码
    }
}
// main.c 中的初始化与调用示例
#include "adc_current_sensor.h"

CurrentSensor_t current_sensor;

void FOC_CurrentLoop_Init(void) {
    // 1. 初始化传感器参数
    CurrentSensor_Init(&current_sensor);

    // 2. 校准零电流偏置(电机上电静止时执行一次)
    CurrentSensor_CalibrateOffsets(&current_sensor);

    // 3. 配置TIM1触发ADC注入组
    // 在CubeMX中配置:TIM1_TRGO2事件触发ADC1注入组
    // ADC1注入组使能DMA循环模式,数据宽度32位,内存地址指向adc_buffer

    // 4. 启动PWM和ADC
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);  // 使能互补通道
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);
    HAL_ADCEx_InjectedStart_DMA(&hadc1, (uint32_t *)adc_buffer, 2);
    HAL_TIM_Base_Start_IT(&htim1);  // 使能更新中断用于控制节拍
}

// 在TIM1更新中断中周期调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim == &htim1) {
        CurrentSensor_ReadPhaseCurrents(&current_sensor);
        // 此处可读取 current_sensor.iu/iv/iw 用于电流环PI控制
    }
}

CubeMX关键配置

  • TIM1:PWM Generation CH1/CH2/CH3及互补输出,计数模式为Center Aligned mode 1,周期值根据PWM频率设定(如20kHz对应PSC=0, ARR=1700 @170MHz)
  • ADC1:注入组2个通道(对应U、V相采样电阻经运放后的电压),采样时间设为最大(如47.5周期)以增强抗噪;触发源选择Timer 1 Trigger 2 event
  • DMA:ADC1注入组请求映射到DMA1 Channel1,循环模式,数据宽度Word
  • GPIO:采样电阻运放输出连接至ADC输入引脚(如PA0、PA1)

验证方法与判定标准

验证采样时序:在电机静止且下桥臂全部导通(输出零矢量000)时,用示波器测量采样电阻电压波形,同时观察ADC注入组转换完成标志。在TIM1中心计数顶点附近应能看到ADC触发脉冲,且转换结果稳定无毛刺。

验证重构精度:向电机注入已知直流电流(如通过外部电源在电机绕组中通入1A直流),读取current_sensor.iu、current_sensor.iv、current_sensor.iw值。判定标准:

  • 三相电流之和 |iu + iv + iw| < 0.05A(验证基尔霍夫重构一致性)
  • 各相读数与外部注入电流值的误差在±5%以内(验证ADC转换精度)
  • 当外部电流为0时,三相读数均接近0(验证偏置校准有效)

预期输出示例(注入1A直流时):iu=0.98A, iv=-0.51A, iw=-0.47A,三相和接近0。此结果由给定电流分配关系直接推出,实际值取决于电机绕组连接方式。

常见问题

  • 采样点落在开关噪声区:调整TIM1的TRGO2触发位置(可配置为上升沿/下降沿),或增大ADC采样时间以滤除尖峰
  • 重构电流在低调制比时失真:当PWM占空比接近0%或100%时,下桥臂导通时间不足以保证ADC采样窗口,此时需切换为三电阻采样或插入测量矢量
  • 零电流偏置漂移:运放温漂会导致偏置变化,建议在电机每次启动前重新执行CurrentSensor_CalibrateOffsets()