4. ADC多通道同步采样

任务目标

本案例在STM32F407平台上,使用ADC1的注入组同步采样两相电机电流(IU、IV)和母线电压(VBUS),并通过DMA将结果自动搬运至内存,供FOC算法主循环直接读取。采样触发源选择定时器1的更新事件,确保与PWM载波周期同步。

硬件连接与前置条件

  • 电流传感器输出接ADC1_IN0(PA0,对应IU)和ADC1_IN1(PA1,对应IV)
  • 母线电压分压后接ADC1_IN2(PA2,对应VBUS)
  • 定时器1已配置为中央对齐PWM模式,频率10kHz(载波周期100µs)
  • 使用STM32CubeMX生成初始化代码,或手动配置以下寄存器

关键完整代码

// adc_sync_sample.h
#ifndef ADC_SYNC_SAMPLE_H
#define ADC_SYNC_SAMPLE_H

#include "stm32f4xx.h"

#define ADC_BUF_LEN 3
typedef struct {
    uint16_t iu;      // 原始ADC值,需转换为实际电流
    uint16_t iv;
    uint16_t vbus;
} ADC_3chSample_t;

extern volatile ADC_3chSample_t g_adcSample;
extern volatile uint8_t g_adcDmaDoneFlag;

void ADC_SyncSample_Init(void);
void ADC_SyncSample_Start(void);

#endif
// adc_sync_sample.c
#include "adc_sync_sample.h"

volatile ADC_3chSample_t g_adcSample;
volatile uint8_t g_adcDmaDoneFlag = 0;

static DMA_Stream_TypeDef* const ADC_DMA_STREAM = DMA2_Stream4;
static const uint32_t ADC_DMA_CHANNEL = DMA_Channel_0;

void ADC_SyncSample_Init(void) {
    // 1. 使能时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN | RCC_AHB1ENR_GPIOAEN;
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_TIM1EN;

    // 2. GPIO配置:PA0/PA1/PA2为模拟输入
    GPIOA->MODER |= (3U << 0) | (3U << 2) | (3U << 4);  // 模拟模式

    // 3. DMA配置:外设到内存,循环模式
    DMA2->LISR = 0;  // 清除标志
    DMA2_Stream4->CR = 0;
    DMA2_Stream4->PAR = (uint32_t)&ADC1->JDR1;  // 注入组数据寄存器
    DMA2_Stream4->M0AR = (uint32_t)&g_adcSample;
    DMA2_Stream4->NDTR = ADC_BUF_LEN;
    DMA2_Stream4->CR = DMA_SxCR_CHSEL_0 |          // 通道0
                       DMA_SxCR_PL_1 |              // 高优先级
                       DMA_SxCR_MSIZE_0 |           // 内存16位
                       DMA_SxCR_PSIZE_0 |           // 外设16位
                       DMA_SxCR_MINC |              // 内存递增
                       DMA_SxCR_CIRC |              // 循环模式
                       DMA_SxCR_TCIE |              // 传输完成中断
                       DMA_SxCR_EN;

    // 4. ADC1配置:注入组3通道,由TIM1触发
    ADC1->CR1 = 0;
    ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_EXTEN_0 | ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1;  // TIM1 CC1触发
    ADC1->CR1 |= ADC_CR1_JAUTO;                    // 自动注入模式
    ADC1->CR1 |= ADC_CR1_SCAN;                     // 扫描模式

    // 注入组序列:3个通道
    ADC1->JSQR = (2U << 20) |                      // JL[1:0]=2,3个转换
                 (2U << 15) |                      // SQ3: 通道2 (VBUS)
                 (1U << 10) |                      // SQ2: 通道1 (IV)
                 (0U << 5);                        // SQ1: 通道0 (IU)

    // 采样时间:480周期(约5.3µs @ 90MHz ADC时钟)
    ADC1->SMPR1 |= (7U << 6) | (7U << 9) | (7U << 12);  // 通道0/1/2

    // 5. DMA中断使能
    NVIC_EnableIRQ(DMA2_Stream4_IRQn);
}

void ADC_SyncSample_Start(void) {
    g_adcDmaDoneFlag = 0;
    ADC1->CR2 |= ADC_CR2_ADON;   // 启动ADC
    ADC1->CR2 |= ADC_CR2_SWSTART; // 首次触发
}

// DMA传输完成中断:每完成一轮3通道采样触发
void DMA2_Stream4_IRQHandler(void) {
    if (DMA2->LISR & DMA_LISR_TCIF4) {
        DMA2->LIFCR |= DMA_LIFCR_CTCIF4;
        g_adcDmaDoneFlag = 1;  // 置标志,主循环可读取新数据
    }
}

运行与验证

  1. 初始化调用:在系统初始化阶段调用ADC_SyncSample_Init(),然后在PWM启动前调用ADC_SyncSample_Start()。
  2. 数据读取:主循环中检查g_adcDmaDoneFlag,置位后读取g_adcSample结构体,处理完毕后清除标志。
  3. 触发同步:TIM1的更新事件(或CC1事件)自动触发ADC注入组转换,无需软件干预。

验证方法:在IU通道(PA0)输入1.5V直流电压(ADC参考电压3.3V),观察g_adcSample.iu值应稳定在约1.5/3.3*4095 ≈ 1861(±10 LSB误差)。同时用示波器测量PA0引脚,确认采样时刻与PWM载波同步(每个PWM周期采样一次)。若DMA中断标志每100µs置位一次,说明同步采样链路工作正常。

常见问题

  • DMA不触发:检查DMA通道号是否与ADC1注入组匹配(F407为DMA2_Stream4_Channel0),并确认ADC_CR2的EXTSEL位选择正确。
  • 数据错位:JSQR中通道顺序必须与DMA搬运顺序一致(先IU、再IV、最后VBUS),否则FOC计算会使用错误数据。
  • 采样值跳变:检查采样时间是否足够(建议≥480周期),并确认传感器输出阻抗匹配。