2、PWM与ADC触发机制:PWM定时器如何触发ADC,实现电流采样同步

做电机控制的朋友都知道,电流采样是闭环控制的眼睛。但很多人一开始都会遇到一个问题:明明ADC采到了电流值,算出来的力矩却忽大忽小,电机跑起来一顿一顿的。

问题出在哪?大概率是采样时机没选对。

这一章我们就来聊聊,怎么用PWM定时器精准触发ADC,让电流采样和PWM开关动作保持同步。说白了,就是让ADC在“对的时间”干活。

2.1 为什么需要同步采样?

先看一个实际场景。三相逆变器驱动电机时,PWM在不停地开关。每次开关都会在母线上产生电流纹波和尖峰。如果你让ADC随便找个时间点采样,很可能采到的是纹波的波峰或波谷,而不是真实的平均电流。

我在项目里遇到过这种情况:用同一个电机跑同样的负载,不同批次的板子测出来的电流值能差20%。查了半天,发现是采样触发源配置不一样,导致采样点落在了不同的纹波位置上。

所以,同步采样的核心目标就两个:

  • 避开开关噪声——不在开关动作瞬间采样
  • 采到相电流中心值——在PWM载波波峰或波谷处采样,此时电流纹波最小,最接近真实值

核心原则:ADC采样时刻必须与PWM载波周期保持固定的相位关系。这个关系一旦确定,就不能随意改变。

2.2 PWM定时器的内部结构

要理解触发机制,得先看看PWM定时器长什么样。以STM32的通用定时器为例,它的核心部件包括:

部件 作用 与ADC触发的关系
计数器 (CNT) 从0向上计数到ARR,然后归零 计数过程就是载波周期
自动重装载寄存器 (ARR) 决定PWM周期 ARR值决定ADC触发频率
比较寄存器 (CCRx) 与CNT比较,产生PWM边沿 可配置为ADC触发源
触发输出 (TRGO) 定时器事件输出信号 直接连接到ADC的触发输入

你想想看,计数器从0走到ARR,再跳回0,这个过程就是一次完整的PWM载波周期。在这个周期里,我们可以选择任意一个时间点,让定时器发出一个触发信号给ADC。

2.3 三种常见的触发模式

根据项目经验,我总结出三种最常用的触发方式。每种都有它的适用场景。

2.3.1 更新事件触发 (UP事件)

计数器溢出归零时产生更新事件。这个事件通过TRGO输出,直接触发ADC。

  • 触发时机:载波波谷(计数器=0)
  • 优点:配置最简单,只需设置定时器的更新事件输出
  • 缺点:采样点固定,无法灵活调整
  • 适用场景:对采样时序要求不高的低速电机
// STM32 HAL库配置示例:更新事件触发ADC
htim1.Instance = TIM1;
htim1.Init.Period = 1000 - 1;  // PWM周期
// 使能更新事件TRGO输出
HAL_TIMEx_MasterConfigSynchronization(&htim1, 
    TIM_TRGO_UPDATE);  // 更新事件作为触发输出

2.3.2 比较事件触发 (OCxREF)

当计数器值与某个CCR寄存器相等时,产生比较事件。这个事件也可以作为TRGO输出。

  • 触发时机:载波周期内的任意位置(由CCR值决定)
  • 优点:采样点可编程,灵活度高
  • 缺点:需要额外占用一个比较通道
  • 适用场景:需要避开特定噪声窗口的高性能控制

我的习惯:做FOC控制时,我通常把采样点设在PWM周期的正中间(即CCR = ARR/2的位置)。这样既能避开开关边沿的噪声,又能采到相电流的平均值。

2.3.3 双触发模式 (更新+比较)

有些高端定时器支持在一个周期内产生两次触发:一次在波谷(更新事件),一次在波峰(比较事件)。

  • 触发时机:载波波谷和波峰各一次
  • 优点:采样率翻倍,适合高速电机
  • 缺点:ADC转换速度要够快,否则会丢数据
  • 适用场景:需要双采样进行纹波重构的场合

2.4 触发信号的硬件路径

光配置好定时器还不够,你得确保触发信号能正确到达ADC。不同芯片的路径不太一样,但大体结构是类似的。

下面这张图展示了典型的信号流向:

PWM定时器触发ADC硬件路径 PWM定时器 计数器 CNT 比较器 CCRx 更新事件 UE TRGO 触发输出 触发源选择 (ADCx_EXTSEL) ADC 转换器 注入组 / 规则组 转换结果寄存器 采样保持电路 关键路径说明: 1. 定时器内部事件(更新/比较)→ TRGO引脚输出 2. TRGO信号经过触发源选择器,连接到ADC的触发输入 3. ADC收到触发后启动采样保持,然后开始转换

2.5 配置实战:以STM32G4为例

纸上谈兵没意思,我们直接上手配置。以STM32G4系列为例,它内部有专门的HRTIM(高分辨率定时器),触发ADC的方式更灵活。但基本原理和普通定时器是一样的。

下面是一个完整的配置流程:

  1. 配置PWM定时器——设置周期、死区、互补输出
  2. 选择触发源——决定用更新事件还是比较事件
  3. 配置ADC触发输入——选择对应的定时器TRGO作为触发源
  4. 设置采样时间——确保ADC能在触发后完成转换
  5. 启动同步——先启动定时器,再使能ADC
// 完整配置示例:比较事件触发ADC,采样点设在PWM中间
void PWM_ADC_Sync_Init(void)
{
    // 1. 配置TIM1,PWM频率20kHz,中心对齐模式
    TIM1->ARR = 1000 - 1;          // 20kHz @ 20MHz时钟
    TIM1->CCR1 = 500;              // 50%占空比
    TIM1->CCMR1 = TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;  // PWM模式1
    TIM1->CR1 = TIM_CR1_CMS_1;     // 中心对齐模式
    
    // 2. 配置比较事件作为TRGO
    // 使用CCR2作为触发比较值,设为ARR/2 = 500
    TIM1->CCR2 = 500;
    TIM1->CR2 = (5 << 4);          // TRGO选择:OC2REF
    
    // 3. 配置ADC1,由TIM1 TRGO触发
    ADC1->CFGR2 = 1 << 4;         // 触发源选择:TIM1_TRGO
    ADC1->CFGR1 = ADC_CFGR1_EXTEN_0;  // 上升沿触发
    
    // 4. 设置采样时间,确保在PWM周期内完成
    ADC1->SMPR = 7;                // 采样周期:640.5个ADC时钟
    
    // 5. 启动
    TIM1->CR1 |= TIM_CR1_CEN;      // 启动定时器
    ADC1->CR |= ADC_CR_ADEN;       // 使能ADC
}

曾经踩过的坑:有一次我配置完触发后,ADC死活不启动转换。查了半天发现是ADC的触发极性设反了。TIM1的TRGO默认是高电平有效,但ADC的触发输入配置成了下降沿触发。信号对不上,自然采不到数据。所以配置完一定要用示波器抓一下TRGO引脚的波形,确认极性对不对。

2.6 采样时序的精确计算

配置写好了,但你真的确定采样点落在正确的位置吗?我建议你算一下时序。

假设PWM频率20kHz,中心对齐模式,ADC时钟40MHz:

参数 值 说明
PWM周期 50μs 1/20kHz
载波半周期 25μs 从0到ARR的时间
触发点设置 CCR2 = 500 对应半周期中点
ADC采样时间 16μs 640.5个ADC时钟 @ 40MHz
采样窗口余量 9μs 25μs - 16μs,足够避开开关边沿

从表格可以看出,采样点设在半周期中点,采样窗口有9μs的余量,完全避开了开关动作产生的噪声区间。这个时序是安全的。

2.7 多通道采样的同步问题

做FOC控制时,通常需要同时采集两相电流(比如U相和V相)。如果ADC只有一个转换器,就得用注入组或规则组轮流转换。

这里有个细节:注入组可以打断规则组的转换。如果你用规则组做常规采样,用注入组做同步电流采样,那么注入组的优先级更高,能保证在触发信号到来时立即响应。

我个人习惯的做法是:

  • 规则组:采集母线电压、温度等慢速信号
  • 注入组:采集两相电流,由PWM定时器触发

这样既保证了电流采样的实时性,又不耽误其他信号的采集。

2.8 常见问题与排查方法

最后,分享几个我在项目中遇到的实际问题,以及排查思路。

现象 可能原因 排查方法
ADC不启动转换 触发极性配置错误 用示波器抓TRGO引脚,检查电平变化
电流值跳动大 采样点落在开关噪声区 调整触发点位置,避开开关边沿
两相电流不同步 注入组通道配置顺序错误 检查注入组序列寄存器,确保两相连续转换
采样值偏小 采样时间不足,电容未充满 增大ADC采样周期,或降低ADC时钟

嗯,这一章的内容就到这里。PWM触发ADC这件事,说复杂也复杂,说简单也简单。核心就是记住一句话:让ADC在PWM周期里最安静的时刻干活。只要抓住这个原则,配置起来就不会跑偏。