AT32F403A的通用定时器(TMR)是嵌入式开发中的瑞士军刀,尤其擅长处理与时间相关的精准控制任务。这颗芯片的定时器模块最让我惊艳的地方在于它的灵活性——通过不同的输出模式配置,可以生成从简单PWM到复杂时序的各种信号。在实际项目中,我经常用它来驱动电机、控制LED亮度,甚至实现自定义通信协议。
TMR模块的核心在于比较器和输出控制电路的配合。想象一下,比较器就像个尽职的哨兵,不断对比当前计数值(CVAL)和预设的比较值(CxDT),然后根据工作模式决定输出引脚的电平状态。这种硬件级的比较机制保证了信号输出的实时性,完全不需要CPU干预。我做过实测,即使在主频跑满的情况下,输出信号的抖动也能控制在纳秒级。
PWM模式A和B就像一对性格迥异的双胞胎。模式A在向上计数时,计数值小于比较值输出有效电平,这个特性特别适合常规的PWM应用。记得第一次调电机驱动时,我误用了模式B,结果电机启动瞬间就疯狂抖动——原来模式B在相同条件下输出的是无效电平。后来在LED调光项目中,我特意选用模式B来实现"低电平点亮"的电路设计,这种灵活性真是开发者的福音。
两种模式在向下计数时的行为也值得注意。模式A在计数值小于等于比较值时输出有效电平,而模式B正好相反。我曾经用这个特性实现了呼吸灯的正反渐变效果,代码量比用软件控制少了70%。
输出比较模式是我最喜欢的"黑科技"之一。当计数值匹配比较值时,可以强制输出指定电平或直接翻转信号。这个功能在生成精确时序时特别有用。有次做伺服控制,需要产生相位差精确的驱动信号,就是用这个模式配合两个通道实现的。配置时要注意:选择电平翻转选项后,输出会在每次匹配时跳变,这个特性可以用来测量外部信号频率。
强制输出模式相当于给定时器按了暂停键——直接锁定输出电平,但内部计数器仍在运行。我在调试紧急停止功能时就用的这招。单周期模式则像个一次性的定时炸弹,输出一个脉冲后自动关闭,非常适合触发ADC采样这类单次操作。
快速输出模式最容易被忽视,其实它在高速信号处理中很关键。这种模式下输出电平会在计数周期开始时立即更新,避免了常规模式的延迟。做500kHz以上的PWM时,如果不启用这个模式,波形边沿会出现明显抖动。
让TMR和DMA搭档工作,就像给定时器装上了自动喂料机。我在智能照明项目中就用这个方案实现了平滑的亮度过渡:DMA负责搬运预设的占空比序列,TMR专注输出PWM波,CPU完全不用参与数据传输。具体实现时,要配置DMA源地址为占空比数组,目标地址指向TMR的比较寄存器(比如TMR1->c1dt),并设置循环模式。
硬件连接很简单:以TMR1通道1为例,只需将PA8引脚配置为复用输出。但有个坑我踩过——DMA通道与定时器请求的映射关系需要查参考手册,比如TMR1的更新事件对应的是DMA1的Channel5。
c复制tmr_base_init(TMR1, 2399, 0); // 100kHz PWM (假设系统时钟240MHz)
tmr_output_struct.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
tmr_output_channel_config(TMR1, TMR_SELECT_CHANNEL_1, &tmr_output_struct);
c复制dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_inc_enable = TRUE; // 关键!使能内存地址自增
dma_init_struct.loop_mode_enable = TRUE; // 循环播放占空比序列
dma_flexible_config(DMA1, FLEX_CHANNEL5, DMA_FLEXIBLE_TMR1_OVERFLOW);
c复制tmr_dma_request_enable(TMR1, TMR_OVERFLOW_DMA_REQUEST, TRUE);
这个配置会让定时器每次溢出时触发DMA传输,自动更新下一个占空比值。
在电机控制项目中,我发现当占空比变化幅度较大时,直接切换会导致电流冲击。后来在DMA传输的数据流中插入了过渡值,就像这样:
c复制uint16_t ramp_up[] = {
100,120,150,180,200,220,240,260,280,300, // 渐变过渡
300,300,300,300,300 // 保持稳定
};
实测显示这种处理方式能让电机转速变化更加平滑,机械噪音降低约40%。
用TMR生成正交信号(相位差90°的PWM)是位置检测系统的核心。配置关键在于:
以生成500kHz正交信号为例:
c复制tmr_base_init(TMR3, 9, 23); // 500kHz @240MHz
tmr_channel_value_set(TMR3, TMR_SELECT_CHANNEL_1, 0);
tmr_channel_value_set(TMR3, TMR_SELECT_CHANNEL_2, 5); // 90°相位差
正交信号对布线非常敏感。我的血泪教训是:
用示波器测量时,要打开XY模式观察李萨如图形,这是判断相位精度的黄金标准。
刚开始用逻辑分析仪抓PWM波形时,我经常被假象迷惑。后来总结出几个要点:
最让人头疼的是寄存器间的依赖关系。比如:
有次调试花了三小时,最后发现是忘记调用tmr_counter_enable()——这个教训让我养成了写配置检查清单的习惯。
在最近的机械臂项目中,我组合使用了多种输出模式:
关键是要合理分配定时器资源:
配置多个定时器时,时钟树设置要特别注意。APB1和APB2总线上的定时器时钟可能不同,我就曾经因为这个问题导致两个定时器实际频率相差一倍。