STM32 HAL库ADC多通道DMA扫描模式:采样顺序与注入通道混用配置详解
在多路模拟量采集场景中,ADC多通道DMA扫描是最常用的方案。但很多开发者对“采样顺序到底由谁决定”“注入通道如何插队”“DMA数据怎么和通道对应”等问题一知半解。本文以STM32F4/F1系列为例,讲清原理并给出可直接复用的配置与代码。
一、原理讲解:扫描序列与注入插队
1.1 规则通道扫描顺序
ADC的规则通道组最多16个通道,扫描顺序由SQR寄存器(规则序列寄存器)决定,而非通道号大小。HAL库中通过ADC_ChannelConfTypeDef.Rank指定顺序,Rank=1最先转换。
- 扫描模式(Scan)使能后,ADC按Rank顺序依次转换所有已配置通道
- 每个通道转换完成后产生EOC,DMA在每次EOC时搬运一次DR寄存器
- DMA缓冲区下标与Rank顺序一一对应,与通道号无关
1.2 注入通道的“插队”机制
注入通道组最多4个通道,由JSQR寄存器定义顺序。其核心特性是:
- 注入转换可被软件(JSWSTART)或外部触发启动
- 若规则转换正在进行,注入请求会暂停规则序列,先完成注入组,再恢复规则序列
- 注入结果存放在独立的JDR1~JDR4寄存器,不占用规则DMA通道
- 注入组可配置为“自动注入”(JAUTO),在规则序列结束后自动执行
这一机制非常适合“慢速轮询+快速事件采样”的混合场景,例如电机控制中规则通道慢速采集温度,注入通道由PWM触发采集母线电流。
二、CubeMX配置步骤
- 使能ADC1,勾选
Scan Conversion Mode与Continuous Conversion Mode(按需) -
Number of Conversion设为规则通道数,例如3 - 在Rank列表中依次添加通道并设置采样时间,注意Rank顺序即采样顺序
- DMA Settings中添加ADC1的DMA请求,模式选
Circular,数据宽度Half Word - 使能
Injected Conversion Mode,配置注入通道数与Rank - 注入触发源选择(如
Timer 2 Trigger Out),规则触发源按需配置
三、完整代码示例
#include "adc.h"
#define RULE_CH_NUM 3
#define INJ_CH_NUM 2
uint16_t rule_buf[RULE_CH_NUM]; // 与Rank顺序对应
uint16_t inj_buf[INJ_CH_NUM]; // 与注入Rank顺序对应
ADC_HandleTypeDef hadc1;
void ADC_MultiChannel_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = RULE_CH_NUM;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = EOC_SEQ_CONV;
HAL_ADC_Init(&hadc1);
/* Rank1: CH0 最先采样 */
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* Rank2: CH1 */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* Rank3: CH4 */
sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = 3;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
/* 注入通道配置:CH5、CH6 */
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = ADC_INJECTED_RANK_1;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfig);
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = ADC_INJECTED_RANK_2;
HAL_ADCEx_InjectedConfigChannel(&hadc1, &sConfig);
}
void ADC_Start(void)
{
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)rule_buf, RULE_CH_NUM);
HAL_ADCEx_InjectedStart_IT(&hadc1); // 注入组中断方式启动
}
/* 注入组转换完成回调 */
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if (hadc->Instance == ADC1) {
inj_buf[0] = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_1);
inj_buf[1] = HAL_ADCEx_InjectedGetValue(hadc, ADC_INJECTED_RANK_2);
/* 在此处理注入采样结果,如过流保护判断 */
}
}
四、注意事项与避坑要点
-
DMA缓冲区顺序由Rank决定:
rule_buf[0]对应Rank1通道,而非CH0。改通道时务必同步检查Rank。 - 注入通道不参与规则DMA:注入结果只能通过JDR寄存器或中断回调读取,不要指望它出现在DMA缓冲区。
-
EOCSelection选择:多通道DMA建议用
EOC_SEQ_CONV(序列结束触发),否则每次转换都触发DMA,需确认与DMA请求配置匹配。 - 注入与规则共用采样时间寄存器:同一通道若同时出现在两组,采样时间以最后配置为准。
-
注入启动时机:
HAL_ADCEx_InjectedStart_IT在规则转换进行中调用会立即插队,注意保护关键规则采样。 - DMA循环模式下的数据一致性:Circular模式下读取缓冲区建议加临界区或双缓冲,避免读到半更新数据。
- F1与F4差异:F1系列注入通道配置函数与寄存器位定义略有不同,移植时需核对参考手册。
掌握扫描序列与注入插队的本质——顺序由SQR/JSQR寄存器决定,DMA只忠实搬运规则序列结果——就能灵活设计采样调度,满足复杂嵌入式系统的多速率采集需求。
暂无评论,来抢沙发~