在很多嵌入式项目中,ADC 不仅需要采到正确的数据,还需要在准确的时刻开始采样。例如电机电流检测、PWM 同步采样、周期性传感器采集等应用,都希望 ADC 的采样周期稳定,并且尽量不受软件执行时间影响。此时可以使用定时器作为 ADC 的外部触发源,让定时器通过芯片内部硬件连接启动规则组转换。
这种方式不需要 CPU 周期性调用软件启动函数。程序只需配置好 TIM0 和 ADC,定时器到达指定时刻后便会产生触发信号,ADC 收到信号后自动完成一次规则组采样。本文介绍规则组可选择的三种外部触发源:TIM0.CHx(2)、TIM0.CHx(3) 和 TIM0.TRGO2。
一、ADC 外部触发的基本原理
ADC 的规则组既可以由软件启动,也可以由外设信号启动。选择外部触发后,TIM0 产生的通道输出或同步输出会通过芯片内部触发网络送入 ADC。ADC 检测到有效触发后,按照已经配置好的规则序列依次采样,完成一次转换流程。
这里的"外部触发"是相对于 ADC 模块而言,并不表示一定要从芯片外部接入一根信号线。TIM0 与 ADC 之间使用的是芯片内部硬件连接,因此触发速度快、周期稳定,也不会因为程序中断或任务调度而产生明显抖动。

二、三个外部触发源分别表示什么
1. TIM0.CHx(2):TIM0 通道 2 输出
CHx(2) 表示 TIM0 的第 2 个通道输出,可以理解为 TIM0_CH2。定时器通道通常具有输出比较和 PWM 功能:计数器 CNT 按设定的周期运行,当计数值与通道比较寄存器 CCR2 的数值满足比较条件时,通道输出状态会按照所选模式变化。该通道信号经内部硬件送到 ADC 后,就可以作为规则组触发源。
使用 CH2 的主要优点是采样位置可调。改变 CCR2 的值,就可以改变一个定时器周期内的触发时刻。例如把 CCR2 设置在周期中间,可以让 ADC 在 PWM 周期中部附近采样;如果需要提前或延后采样,只需相应调整比较值。
2. TIM0.CHx(3):TIM0 通道 3 输出
CHx(3) 表示 TIM0 的第 3 个通道输出,也就是 TIM0_CH3。它的工作方式与 CH2 类似,触发位置由通道 3 的比较寄存器 CCR3 以及输出比较模式决定。ADC 选择这一触发源后,TIM0_CH3 的内部输出信号负责启动规则组转换。
CH2 和 CH3 可以配置成相同的比较值,也可以设置成不同的比较值。当两者设置为不同位置时,可以在同一个定时器周期中形成不同的时间基准,便于根据应用需求选择合适的 ADC 采样相位。
3. TIM0.TRGO2:TIM0 第二触发输出
TRGO2 是 TIM0 的第二路触发输出信号,主要用于把定时器内部事件同步给 ADC 等其他外设。TRGO2 本身是一个可配置的输出通道,其具体来源由 TIMx_CR2 寄存器中的 MMS2 位域决定。配置时可以根据芯片支持情况选择更新事件、比较事件或其他定时器内部信号。
本文示例把 Update 更新事件选择为 TRGO2 的输出源。这样 TIM0 每产生一次更新事件,就会通过 TRGO2 向 ADC 发送一次触发。与 CH2、CH3 相比,TRGO2 更适合只关心固定采样周期、不需要单独输出 PWM 通道的场景。
|---------|------------|--------------------|
| 选择值 | 外部触发源 | 主要用途 |
| 0 | TIM0.CH2 | 通过 CCR2 调整周期内的采样位置 |
| 1 | TIM0.CH3 | 通过 CCR3 调整周期内的采样位置 |
| 2 | TIM0.TRGO2 | 通过定时器内部事件产生周期触发 |
三、ADC 规则组的公共配置
三种触发方式的 ADC 基础配置相同,只需要根据实际需求选择 ADC_RegularTrigger。以单通道规则组采样为例,可以使用 12 位分辨率、数据右对齐,规则序列长度设置为 1,并关闭连续转换模式。关闭连续转换后,每收到一次外部触发,ADC 执行一次规则组转换。
ADC_InitStructure.ADC_Resolution = ADC_RESOLUTION_12BIT;
ADC_InitStructure.ADC_Alignment = ADC_DATA_ALIGN_RIGHT;
ADC_InitStructure.ADC_RegularEnable = ENABLE;
ADC_InitStructure.ADC_ContinuousModeEn = DISABLE;
ADC_InitStructure.ADC_RegularSequenceLength = 1;
/* 根据所使用的触发源选择其中一项 */
ADC_InitStructure.ADC_RegularTrigger = ADC_TRIGGER_EXTERNAL_0; // TIM0.CH2
ADC_InitStructure.ADC_RegularTrigger = ADC_TRIGGER_EXTERNAL_1; // TIM0.CH3
ADC_InitStructure.ADC_RegularTrigger = ADC_TRIGGER_EXTERNAL_2; // TIM0.TRGO2
实际工程中三行触发选择代码只能启用一行。配置完成后,再设置 ADC 通道、采样时间以及是否使用中断或 DMA 即可。
四、CH2 和 CH3 的简单配置
CH2、CH3 可以采用输出比较模式,也可以像下面这样使用 PWM1 模式。首先配置 TIM0 的计数周期和分频系数,然后为相应通道设置比较值。示例将比较值放在周期中间,实际使用时可以分别修改 CCR2、CCR3 对应的脉冲值。
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInit(TIM0, &TIM_TimeBaseInitStructure);
TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OC_InitStructure.TIM_Pulse = arr / 2;
TIM_OC2Init(TIM0, &TIM_OC_InitStructure); // 使用 CH2 时配置
TIM_OC3Init(TIM0, &TIM_OC_InitStructure); // 使用 CH3 时配置
TIM_Cmd(TIM0, ENABLE);
TIM_CtrlPWMOutputs(TIM0, ENABLE);
如果只使用 CH2,就重点配置 TIM_OC2Init;如果只使用 CH3,则配置 TIM_OC3Init。随后在 ADC 初始化中分别选择 EXTERNAL_0 或 EXTERNAL_1。是否必须把对应 GPIO 配置成定时器复用输出,应结合芯片内部触发机制和实际调试需求判断;仅供内部触发使用时,通常不需要把信号从引脚引出。
五、TRGO2 的简单配置
使用 TRGO2 时,先完成 TIM0 基本计数配置,再通过输出触发选择函数设置 TRGO2 的来源。下面选择 Update 更新事件,然后启动定时器;ADC 端选择 EXTERNAL_2 即可。
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInit(TIM0, &TIM_TimeBaseInitStructure);
TIM_SelectOutputTrigger(TIM0, TIM_TRGO2Source_Update);
TIM_Cmd(TIM0, ENABLE);
ADC_InitStructure.ADC_RegularTrigger = ADC_TRIGGER_EXTERNAL_2;
TRGO2 的触发周期由 TIM0 的时钟、PSC 和 ARR 共同决定。定时器更新频率通常可按 fTIM ÷(PSC+1)÷(ARR+1)计算,因此改变 PSC 或 ARR 就可以调整 ADC 的周期采样频率。
六、三种触发源如何选择
如果需要把 ADC 采样点放在 PWM 周期内的某个指定位置,可以选择 CH2 或 CH3,并通过对应的比较值调整采样相位。如果只需要按照固定周期启动 ADC,不关心某个 PWM 通道的比较位置,则可以选择 TRGO2,并把合适的定时器内部事件配置为 TRGO2 输出源。
总体来说,CH2、CH3 提供了更灵活的周期内采样位置,TRGO2 则适合建立清晰的外设同步关系。三者都由 TIM0 通过内部硬件触发 ADC,使用时只要正确配置定时器信号源,并在 ADC 中选择相应的外部触发编号即可。