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; // 置标志,主循环可读取新数据
}
}
运行与验证
- 初始化调用:在系统初始化阶段调用
ADC_SyncSample_Init(),然后在PWM启动前调用ADC_SyncSample_Start()。 - 数据读取:主循环中检查
g_adcDmaDoneFlag,置位后读取g_adcSample结构体,处理完毕后清除标志。 - 触发同步: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周期),并确认传感器输出阻抗匹配。