问题现象与背景
在STM32项目中使用ADC多通道扫描+DMA传输时,常出现采样数据与通道不对应的情况。例如配置了IN0、IN1、IN2三个通道,DMA缓冲区却读到buf[0]对应IN1、buf[1]对应IN2、buf[2]对应IN0,或者数据整体偏移一位。这种错位在HAL库中尤为常见,根源多与ADC扫描顺序、DMA传输宽度、HAL回调处理不当有关。
原理讲解:ADC扫描与DMA如何协作
-
ADC扫描模式:使能
ScanConvMode后,ADC按Rank顺序依次转换多个通道,每个通道转换结果存入DR寄存器。 -
DMA请求:每次转换结束(EOC)产生DMA请求,DMA将
DR内容搬运到内存缓冲区。 - 关键点:DMA传输宽度必须与ADC数据对齐方式匹配。ADC结果通常为12位,HAL库默认右对齐,存入16位半字。若DMA配置为字节传输,会导致高低字节错位。
-
HAL库回调:
HAL_ADC_ConvCpltCallback在DMA传输完成时调用,但若在回调中直接读取缓冲区,需确保DMA传输完成中断已清除,否则可能读到旧数据。
常见错位原因分析
-
DMA传输宽度不匹配:ADC数据为16位,DMA配置为
DMA_PDATAALIGN_BYTE,导致每个通道数据被拆成两个字节,缓冲区错位。 -
ADC扫描顺序与缓冲区索引不一致:CubeMX中Rank顺序与代码中缓冲区索引未对应,例如Rank1为IN0,但代码认为
buf[0]是IN1。 - DMA循环模式与中断处理冲突:循环模式下DMA持续搬运,若在回调中处理数据时未考虑半传输/全传输中断,可能读到正在更新的数据。
- ADC校准或启动顺序错误:未等待ADC稳定就启动DMA,导致首次转换数据无效,后续数据整体偏移。
-
HAL库版本差异:部分HAL版本中
HAL_ADC_Start_DMA内部未正确配置DMA,需手动检查。
配置步骤与修正方案
步骤1:CubeMX中正确配置ADC与DMA
- ADC参数:
ScanConvMode = ENABLE,ContinuousConvMode = DISABLE(单次扫描)或ENABLE(连续),ExternalTrigConv = ADC_SOFTWARE_START。 - Rank设置:按实际通道顺序配置Rank1、Rank2...,并记录顺序。
- DMA设置:
Mode = Circular(连续模式)或Normal,Data Width = Half Word(关键!),Increment = Enable。
步骤2:代码中初始化与启动
#define ADC_CH_NUM 3
uint16_t adc_buf[ADC_CH_NUM];
void ADC_Init(void)
{
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 = ADC_CH_NUM;
HAL_ADC_Init(&hadc1);
// 配置通道Rank
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);
// 启动DMA
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buf, ADC_CH_NUM);
}
步骤3:DMA配置检查
在HAL_ADC_Start_DMA内部会调用HAL_DMA_Start_IT,需确保hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD,MemDataAlignment = DMA_MDATAALIGN_HALFWORD。若CubeMX生成代码有误,手动修改:
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
HAL_DMA_Init(&hdma_adc1);
步骤4:数据读取与校准
在HAL_ADC_ConvCpltCallback中处理数据,注意此时DMA已完成一轮传输,缓冲区数据有效。若使用连续模式,需在回调中及时复制数据,避免被下一轮覆盖。
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1) {
// 此时adc_buf[0]对应Rank1通道,adc_buf[1]对应Rank2...
// 可在此处进行数据处理
}
}
排查与修正实战
-
检查DMA传输宽度:用调试器查看
hdma_adc1.Init.PeriphDataAlignment是否为HALFWORD。 -
验证扫描顺序:在回调中打印
adc_buf值,对比已知电压的通道,确认索引对应关系。 -
使用半传输中断:若数据错位发生在半传输时,可启用
HAL_ADC_ConvHalfCpltCallback,分别处理前后半段。 -
关闭DMA循环模式测试:改为
Normal模式,单次采集后停止,排除循环覆盖干扰。 -
检查ADC校准:在启动前调用
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED),并等待完成。
注意事项
- 确保DMA缓冲区大小与通道数一致,且为
uint16_t类型。 - 连续模式下,回调中处理数据时间不宜过长,否则可能丢失后续数据。
- 若使用多个ADC,注意DMA通道冲突,每个ADC需独立DMA。
- HAL库版本不同,
HAL_ADC_Start_DMA内部实现有差异,建议查看对应版本源码确认DMA配置。 - 调试时可用固定电压输入各通道,通过串口打印缓冲区值,快速定位错位通道。
通过以上原理分析与配置修正,可有效解决STM32 HAL库ADC多通道DMA扫描模式下的数据错位问题,确保采样数据与通道严格对应。
暂无评论,来抢沙发~