在 STM32、GD32、NRF52 等不同架构 MCU 间移植 PWM+DMA 驱动时,常出现波形畸变。分析不同厂商定时器的计数启动逻辑、预分频器精度、DMA 请求触发时机差异,探讨如何通过动态调整 ARR 值、校准 PWM 占空比补偿时序偏差,以及利用示波器量化不同芯片的输出延迟。
一、定时器时序差异的根源分析
计数启动逻辑不同
STM32:定时器使能后立即开始计数,无需额外同步信号
GD32:部分型号需要等待一个 APB 时钟周期后才启动计数
NRF52:低功耗架构导致定时器启动存在固定的 2-3 个时钟周期延迟
预分频器精度差异
STM32:预分频器可实现 1-65536 的任意分频,且分频值立即生效
GD32:部分系列预分频器存在奇偶分频误差(奇数分频时实际值为 N+1)
NRF52:预分频器为幂次分频(2^n),无法实现连续分频值
DMA 请求触发时机差异
STM32:在计数器更新事件(UEV)发生时立即触发 DMA 请求
GD32:DMA 请求比计数器更新晚一个外设时钟周期
NRF52:DMA 请求与计数器更新存在 1-2 个 CPU 时钟周期的偏移
二、跨平台适配策略
动态调整 ARR 值补偿启动延迟
c
运行
// 根据不同芯片类型动态调整自动重装载值
#if defined(STM32F103xx)
#define ARR_COMPENSATION 0
#elif defined(GD32F130xx)
#define ARR_COMPENSATION 1 // 补偿1个APB周期
#elif defined(NRF52832_XXAA)
#define ARR_COMPENSATION 2 // 补偿2个时钟周期
#endif
// 计算实际ARR值
uint16_t get_actual_arr(uint16_t target_period) {
return target_period - ARR_COMPENSATION;
}
PWM 占空比校准机制
建立占空比补偿表,通过示波器测量不同芯片在相同配置下的实际脉宽
实现动态校准函数:
c
运行
// 占空比校准函数
uint16_t calibrate_ccr_value(uint16_t target_duty, uint32_t pclk_freq) {
#if defined(GD32F130xx)
// GD32奇数分频补偿
if((TIM_PRESCALER % 2) != 0) {
return (uint16_t)((float)target_duty * 1.002f);
}
#elif defined(NRF52832_XXAA)
// NRF52的分频特性补偿
return (uint16_t)((float)target_duty * 1.005f);
#endif
return target_duty;
}
DMA 传输时序对齐
对于 WS2812 等对时序敏感的应用,增加 DMA 传输前的同步延迟:
c
运行
// DMA传输前的同步处理
void dma_sync_before_transfer(TIM_HandleTypeDef *htim) {
#if defined(NRF52832_XXAA)
// NRF52需要额外的同步延迟
volatile uint32_t delay = 8;
while(delay--);
#elif defined(GD32F130xx)
// GD32等待预分频器稳定
while((TIMER_CTL0(htim->Instance) & TIMER_CTL0_CEN) == 0);
#endif
}
示波器量化与校准流程
生成标准测试波形(如 50% 占空比的 1MHz 方波)
测量各芯片的实际输出:
上升沿 / 下降沿延迟时间
脉冲宽度误差
周期稳定性
建立芯片特性数据库,实现自动校准
|
|