一、ADC扫描模式与DMA传输原理

STM32的ADC支持多通道扫描模式,可自动按预设顺序转换多个通道。规则通道组最多16个通道,注入通道组最多4个通道。扫描模式下,ADC依次转换序列中的每个通道,每次转换结果存入对应的数据寄存器。若启用DMA,则每次转换完成触发DMA请求,将数据搬运到内存数组,避免CPU干预。

  • 规则通道序列:由SQR1/SQR2/SQR3寄存器配置,决定转换顺序。
  • 注入通道序列:由JSQR寄存器配置,可打断规则转换,常用于紧急事件采样。
  • DMA循环模式:适合连续采样,自动覆盖旧数据。

二、采样顺序配置要点

采样顺序由序列寄存器中的SQx字段决定,编号从1到16。HAL库通过ADC_ChannelConfTypeDef的Rank成员设置顺序。注意:

  • Rank必须连续且从1开始,不可跳跃。
  • 规则组和注入组独立配置,互不影响。
  • 注入通道可配置触发源(如定时器),实现与规则通道的异步采样。

三、CubeMX配置步骤

  1. 启用ADC1,选择多通道扫描模式。
  2. 在“Rank”中依次添加通道,设置采样时间。
  3. 使能DMA连续请求,模式设为Circular。
  4. 若使用注入通道,在“Injected”选项卡配置通道和触发源。
  5. 生成代码,HAL会自动初始化ADC和DMA。

四、完整代码示例

#include "main.h"

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

#define ADC_BUF_SIZE 3
uint16_t adc_buf[ADC_BUF_SIZE]; // 规则通道数据
volatile uint16_t inj_buf[2];   // 注入通道数据

void ADC_Init(void) {
    __HAL_RCC_ADC1_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    HAL_DMA_Init(&hdma_adc1);
    __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = 3;
    HAL_ADC_Init(&hadc1);

    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_0;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 2;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    sConfig.Channel = ADC_CHANNEL_2;
    sConfig.Rank = 3;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

    // 注入通道配置(可选)
    sConfig.Channel = ADC_CHANNEL_3;
    sConfig.Rank = ADC_INJECTED_RANK_1;
    sConfig.InjectedSamplingTime = ADC_SAMPLETIME_28CYCLES;
    sConfig.InjectedOffset = 0;
    sConfig.InjectedNbrOfConversion = 1;
    sConfig.AutoInjectedConv = DISABLE;
    sConfig.ExternalTrigInjecConv = ADC_INJECTED_SOFTWARE_START;
    HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfig);

    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_BUF_SIZE);
}

// 注入通道转换完成回调
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) {
    inj_buf[0] = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1);
}

五、注意事项

  • DMA缓冲区对齐:确保内存地址与数据宽度匹配,使用__attribute__((aligned(4)))避免对齐错误。
  • 注入通道触发:若使用外部触发,需配置触发源并启动注入转换,否则不会自动执行。
  • 采样时间:根据信号源阻抗调整采样时间,避免转换误差。
  • 规则与注入优先级:注入通道可打断规则转换,但需注意数据一致性,建议在规则DMA传输完成回调中处理注入数据。
  • HAL库版本差异:不同系列(如F1/F4/H7)的注入通道配置函数可能不同,请参考对应HAL文档。

通过合理配置扫描序列与DMA,结合注入通道的灵活触发,可构建高效、可靠的多通道数据采集系统。