STM32 ADC 注入通道实战:给紧急采样开一条 VIP 通道
我早年做过一个电机驱动板,母线电流用 ADC 规则通道连续采,在后台画波形、算 RMS。一切正常,直到有次负载卡死,电流"唰"地窜上去。我代码里明明写了"电流超阈值就关 PWM",结果 MOS 管还是冒了烟。后来抓波形才看懂:规则通道那会儿正在扫四个通道,要等这一轮扫完才轮到电流通道,而这一轮扫完的功夫,电流已经从 5A 飙到 30A 了——保护窗口就这么被排队等没了。
那次之后我才知道 ADC 还有个注入通道(Injected Channel):它就是给这种"现在、立刻、马上要采一下"的信号开的一条 VIP 通道,规则通道再忙也得给它让路。
一、为什么需要注入通道
先搞清楚两个概念。ADC 的通道分两组:
- 规则通道(Regular):正常排队的通道,按你在 CubeMX 里排的 Rank 顺序,一个接一个转。开了连续模式就一直转,不停。你平时
HAL_ADC_Start采的就是它。 - 注入通道(Injected):VIP 通道。它平时不转,一旦注入触发来了,当前正在进行的那个规则转换一结束,ADC 立刻切到注入组,把注入通道采完,然后自动回到规则组接着转。整个过程硬件自己调度,你代码不用插手。
关键点:注入通道的结果存在自己的JDRx寄存器里,绝不覆盖规则通道的DR。所以规则通道在后台稳稳采波形,注入通道在前面插一脚采个紧急信号,两边数据谁也不污染谁。
什么时候用得上?凡是"规则通道正在连续干活,但有个信号必须立刻采一下"的场景:
- 过流 / 过压保护(上面那个惨案)
- 阈值报警(温度、电压越限瞬间采样确认)
- 按键中断里想立刻读一下某个模拟量消抖
- 任何"优先级比常规采集高"的采样
实际用法通常是:用TIM3 触发规则通道做等间隔连续采集(波形),同时再开一条由 TIM2(或外部事件)触发的注入通道,专门负责过流保护这类紧急采样——一个 ADC 硬件,常规采集和紧急采集两不误。
二、CubeMX 怎么配
ADC 配置界面里,通道 Rank 是分两组的:上面Regular Conversion(规则组),下面Injected Conversion(注入组)。
规则组
- External Trigger = TIM3_TRGO,边沿 Rising,Continuous = Disable,DMA 开启双缓冲。
注入组(本篇重点)
- 勾上
Injected Conversion Mode。 - Number Of Conversions(注入组):填你要插队的通道数,比如 1(就采过流那一路)。
- JEXTSEL(注入外部触发源):选另一个定时器的 TRGO,比如 TIM2_TRGO,或者外部触发
EXTI line 15。★ 注意别和规则组抢同一个 TIM3 TRGO,否则两个触发源打架。 - JEXTEN(注入触发边沿):选Rising Edge。★ 这一项必须开,否则注入触发信号进不来,注入通道永远不会启动——这是后面坑 1 要说的。
- 注入组的 Rank、采样时间独立配,和规则组互不干扰。
- 开启注入转换完成中断:在 NVIC 里把
ADC的JEOC(或ADC_EOSMP/JEOS)中断打开,这样采完能进回调函数。
配完生成代码,下面就是让两路同时跑的最少代码。
三、最少的代码
规则通道用 TIM3 触发 + DMA 双缓冲;注入通道用HAL_ADCEx_InjectedStart_IT启动,之后就交给硬件和中断,你不用再管它什么时候采。
#defineOVERCURRENT_THRESH3500// 过流阈值(12位满量程4095下的当量,按实际标定)// 规则通道:TIM3 触发连续采集波形,DMA 双缓冲搬HAL_TIM_Base_Start(&htim3);HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_buf,2*ADC_BUF_LEN);// 注入通道:由 CubeMX 配的 JEXTSEL 触发源自动驱动// 这里只负责"启动 + 开中断",具体什么时候采由硬件触发决定HAL_ADCEx_InjectedStart_IT(&hadc1);// 注入转换完成 —— 紧急采样到了,进这里处理voidHAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc->Instance==ADC1){// ADC_INJECTED_RANK_1 对应注入组里 Rank 1 的通道uint16_tcur_protect=HAL_ADCEx_InjectedGetValue(&hadc1,ADC_INJECTED_RANK_1);if(cur_protect>OVERCURRENT_THRESH){motor_emergency_stop();// 过流!立刻关 PWM、封 MOS}}}这段在干嘛:规则通道在后台用 TIM3 稳稳采波形,CPU 基本不操心;一旦过流信号(JEXTSEL 那个触发源)来了,硬件自动暂停规则序列、插队采一下注入通道,采完进回调,你在里面读JDRx判断阈值,超了就保护。规则通道的连续采集不被你的代码打断,只是被硬件暂时"借道"一下,采完自动续上。
读注入结果有个专门函数HAL_ADCEx_InjectedGetValue,第二个参数传ADC_INJECTED_RANK_1/2/3/4,对应注入组里排第几的通道。千万别拿规则通道的HAL_ADC_GetValue去读,那是DR,读出来不是注入的值。
四、真正会坑你的几个点
1. JEXTEN 边沿没开。最高频的坑。CubeMX 里注入触发边沿默认可能是 Disabled,你配了 JEXTSEL 触发源,结果注入死活不触发。记住:JEXTEN 必须选 Rising(或 Falling),注入才有触发。
2. 注入和规则用同一触发源打架。两个都想用 TIM3 TRGO,结果规则通道被注入触发源干扰,采样节奏乱套。注入组老老实实换一个触发源(TIM2 TRGO、TIM4 TRGO、或者外部 EXTI 都行),别和规则组抢。
3. 读错寄存器。注入结果在JDRx,你图省事用HAL_ADC_GetValue读DR,得到的要么是规则通道的值、要么是 0,紧急判断全错。注入专用值,必须用HAL_ADCEx_InjectedGetValue。
4. 注入打断规则通道导致 DMA 错位。这是最阴的一个。注入插队后,不同 STM32 型号对"规则序列接下来怎么走"行为不同:有的从被打断那个通道继续,有的从头重启整个规则序列。如果你的规则组是多通道扫描 + DMA 双缓冲,序列重启意味着 DMA 缓冲里的前后顺序对不上,波形数据错位。务必翻你芯片的参考手册 ADC 章节,确认注入后的规则行为,拼接 DMA 缓冲时按实际行为来。
5. 注入 Rank 顺序 = JDRx 下标。注入组里 Rank 1 对应ADC_INJECTED_RANK_1、Rank 2 对应ADC_INJECTED_RANK_2。配的时候 Rank 顺序和读的时候 rank 参数对不上,就读串了通道——明明想读电流,读出来的是温度。
6. 注入触发太频繁打乱规则节奏。保护信号要是抖(比如触点弹跳、干扰毛刺),注入触发一个接一个来,规则通道被反复借道,正常波形采集基本废了。触发源前面加硬件滤波 / 软件去抖,或者只在状态机允许时使能注入中断。
7. 注入组长度配错。Number Of Conversions(注入组)填的和你实际排的通道数不一致,ADC 转换次数对不上,JDRx里只有部分有效,读越界 rank 拿到 0。
8. 自动注入(AUTO_INJECT)模式误解。CubeMX 里有个Auto Injected Conversion,开了它会在每个规则序列结束后自动跑一遍注入组——这不是"插队",是"规则序列尾巴上附加一段"。它和"外部触发随时插队"是两码事。你要的是随时插队,就别开 AUTO_INJECT,靠 JEXTSEL 触发源来驱动。
五、完整例程骨架
两路并进的最小系统:规则通道 TIM3 触发 + DMA 双缓冲连续采波形;注入通道用 TIM2(或外部事件)触发 + 中断,随时插队采过流,回调里读JDRx做保护判断。规则通道负责"看趋势",注入通道负责"保命",各干各的。
// 启动三连:TIM3 跑规则触发,DMA 搬规则数据,注入通道待命(由 JEXTSEL 触发)HAL_TIM_Base_Start(&htim3);HAL_ADC_Start_DMA(&hadc1,(uint32_t*)adc_buf,2*ADC_BUF_LEN);HAL_ADCEx_InjectedStart_IT(&hadc1);// 之后:主循环处理规则波形;HAL_ADCEx_InjectedConvCpltCallback 里做紧急保护规则通道管"常规采集",注入通道管"紧急插队"——两路各司其职,一个 ADC 芯干两件事。
适合:过流 / 过压保护、阈值报警、按键即时采样、需要在连续采集中随时插一下的场景。不适合:纯单通道低速采样,那规则通道
HAL_ADC_GetValue一把梭就够了,杀鸡不用开 VIP 通道。
进阶玩法:如果还需要双 ADC 同步模式(Dual ADC)——两个 ADC 同时采样,做 IQ 两路或差分信号,再配上本篇的注入通道,就能搭出一套"同步采样 + 紧急保护"的高端采集架构。