一、引言

在嵌入式数据采集系统中,STM32的ADC多通道扫描+DMA模式是高效采集多路模拟量的标准方案。然而,许多开发者在使用HAL库时,常遇到采样数据与通道不对应、数值异常(如高位错位、符号错误)等问题。这往往源于对采样顺序和数据对齐机制理解不足。本文将系统分析这两个陷阱,并提供完整的排查与解决方案。

二、原理讲解

2.1 规则组序列与采样顺序

STM32的ADC规则组支持最多16个通道的序列转换。在扫描模式下,ADC按SQRx寄存器中配置的顺序依次转换。HAL库通过ADC_ChannelConfTypeDef的Rank参数指定顺序(1~16)。关键点:DMA传输的数据顺序严格遵循Rank顺序,而非通道号顺序。例如,若配置Rank1=Channel3、Rank2=Channel1,则DMA缓冲区第0个元素对应Channel3,第1个元素对应Channel1。

2.2 数据对齐与DMA传输宽度

ADC转换结果可配置为左对齐或右对齐(12位模式下)。HAL库默认右对齐,即结果存储在低12位。DMA传输时,若外设数据宽度(PeriphDataAlignment)与内存数据宽度(MemDataAlignment)不匹配,会导致数据错位。例如,ADC为12位右对齐,若DMA配置为半字(16位)传输,则每个结果占用2字节,内存中低12位有效;若误配为字节传输,则高4位丢失,数据严重错误。

2.3 多通道DMA的缓冲区映射

DMA循环模式下,每次ADC序列转换完成(EOC触发)后,DMA将结果搬运至内存。缓冲区大小应为通道数 × 数据宽度。若缓冲区定义过小或DMA传输长度错误,会导致数据覆盖或越界。

三、CubeMX配置步骤

  1. 在Analog中启用ADC1,选择通道(如IN1、IN4、IN5)。
  2. 参数设置:
    • Scan Conversion Mode: Enabled
    • Continuous Conversion Mode: Enabled(或根据需求)
    • DMA Continuous Requests: Enabled
    • Data Alignment: Right alignment
    • Number Of Conversion: 3(与通道数一致)
  3. 在Rank列表中按需排列通道顺序(如Rank1=IN1, Rank2=IN4, Rank3=IN5)。
  4. 在DMA Settings中添加ADC1的DMA请求:
    • Mode: Circular
    • Data Width: Half Word(外设与内存均设为Half Word)
  5. 生成代码。

四、完整代码示例

/* 定义缓冲区,大小为通道数 */
#define ADC_CHANNELS 3
uint16_t adc_buffer[ADC_CHANNELS];

/* ADC初始化与校准 */
void ADC_Init(void)
{
    hadc1.Instance = ADC1;
    hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
    hadc1.Init.ContinuousConvMode = ENABLE;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.NbrOfConversion = ADC_CHANNELS;
    HAL_ADC_Init(&hadc1);

    /* 配置通道顺序 */
    ADC_ChannelConfTypeDef sConfig = {0};
    sConfig.Channel = ADC_CHANNEL_1;
    sConfig.Rank = 1;
    sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
    HAL_ADC_ConfigChannel(&hadc1, &sConfig);

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

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

    /* 校准并启动DMA */
    HAL_ADCEx_Calibration_Start(&hadc1);
    HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_CHANNELS);
}

/* 主循环中读取数据 */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_DMA_Init();
    MX_ADC1_Init();
    ADC_Init();

    while (1)
    {
        /* adc_buffer[0]对应Rank1(IN1),[1]对应Rank2(IN4),[2]对应Rank3(IN5) */
        uint16_t ch1 = adc_buffer[0];
        uint16_t ch4 = adc_buffer[1];
        uint16_t ch5 = adc_buffer[2];
        HAL_Delay(100);
    }
}

五、常见陷阱与排查

5.1 采样顺序错乱

  • 现象:数据与预期通道不匹配。
  • 原因:Rank配置与缓冲区索引未对应;或CubeMX中Rank顺序与代码不一致。
  • 排查:检查SQR1/SQR2/SQR3寄存器值,确认Rank顺序;在DMA完成回调中打印缓冲区内容,对比已知电压。

5.2 数据对齐错误

  • 现象:数值偏大(左对齐未右移)或偏小(高位丢失)。
  • 原因:DataAlign配置为左对齐但未右移4位;或DMA数据宽度误配为字节。
  • 排查:确认ADC_CR2的ALIGN位;检查DMA初始化中PeriphDataAlignment和MemDataAlignment均为DMA_PDATAALIGN_HALFWORD。

5.3 DMA传输长度与缓冲区不匹配

  • 现象:数据错位、覆盖或HardFault。
  • 原因:HAL_ADC_Start_DMA的Length参数不等于通道数;缓冲区数组大小不足。
  • 排查:确保Length = NbrOfConversion;缓冲区定义为uint16_t且大小≥通道数。

5.4 未校准或采样时间过短

  • 现象:数据跳动大、精度低。
  • 原因:未执行ADC校准;采样时间小于信号源内阻要求。
  • 排查:上电后调用HAL_ADCEx_Calibration_Start;根据信号源阻抗增加采样周期。

六、注意事项

  • 多通道扫描时,必须启用DMA,否则EOC中断频繁且易丢失数据。
  • 若使用连续转换模式,DMA必须配置为循环模式,否则传输完一次后停止。
  • 缓冲区建议使用__attribute__((aligned(4)))确保对齐,避免DMA访问异常。
  • 调试时可用__HAL_DMA_GET_COUNTER检查剩余传输次数,判断DMA是否正常运行。
  • 对于高精度场景,可在DMA完成回调中做软件滤波,但注意回调执行时间。

七、总结

掌握ADC多通道DMA扫描模式的关键在于:明确Rank顺序决定数据排列、严格匹配DMA数据宽度与对齐方式、确保缓冲区与传输长度一致。通过本文的原理分析与代码示例,开发者可快速定位并解决采样顺序错乱与数据对齐陷阱,构建稳定可靠的多通道采集系统。