如何从零开始在STM32F411CEU6上配置一个通用定时器
引言:生成脉宽调制(PWM)信号是嵌入式系统中最基本的需求之一,无论您是在驱动舵机、调光LED,还是控制电机驱动器。尽管高层抽象层(如HAL或Arduino)可以通过一行代码实现PWM信号,但这些层会掩盖底层的硅片逻辑。
在本项目中,我们将摒弃使用库函数,从零开始通过纯C语言和裸机寄存器操作(CMSIS)在STM32F411CEU6“黑板”上配置一个通用定时器。我们将配置TIM3同时驱动PB4和PB5上的两个独立硬件PWM输出,初始化后完全绕过CPU。
工作原理:硅层
硬件定时器本质上是一种自主运行的高速数字计数器。它利用MCU内部时钟源(在此情况下为默认的16 MHz内部高速振荡器,即HSI),在后台静默运行。
STM32F411 Blackpill
引脚映射表:
•PB4 -> TIM3_CH1 (AF2) -> 电阻(330欧姆)-> LED 1 -> GND(信号:1 kHz,25%占空比)
•PB5 -> TIM3_CH2 (AF2) -> 电阻(330欧姆)-> LED 2 -> GND(信号:1 kHz,75%占空比)
•GND -> 接地参考 -> 公共接地线
该架构依赖于三个主要的16位寄存器协同工作:
•预分频器(PSC):将输入的16 MHz时钟分频至可管理的频率。对于1 MHz的定时器计数,我们将其除以16(PSC = 15)。
•自动重载寄存器(ARR):上限值。计数器(CNT)会一直递增,直到与ARR相匹配,然后立即重置为0。这决定了PWM的频率。
•捕获/比较寄存器(CCR):阈值。硬件比较器会检查CNT是否小于CCR,这决定了PWM占空比。
机制(PWM模式1):
•当CNT小于CCR时:物理引脚被拉高。
•当CNT大于或等于CCR时:引脚变为低电平。
•当 CNT 达到 ARR 时:计数器重置为 0,引脚再次拉高。
分步配置流程:配置裸机外设需要在三个层次上遵循严格的顺序操作:RCC(时钟)、GPIO(引脚复用)和定时器外设。
第一层:为模块供电(RCC)
默认情况下,外设的时钟被关闭以节省功耗。没有时钟的外设完全无法响应——对其寄存器写入将毫无作用。我们必须在 AHB1 总线上启用 GPIOB 的时钟,在 APB1 总线上启用 TIM3 的时钟。
第二层:通过备用功能进行路由
要使一个引脚传输硬件生成的PWM信号,必须将其从标准的CPU输入/输出寄存器中分离,并交由定时器多路复用器处理。
将 MODER 中的引脚模式设置为替代功能(0b10)。
将引脚映射到替代功能低寄存器(AFRL)中的AF2,该寄存器在硅片上明确将PB4连接到TIM3_CH1,PB5连接到TIM3_CH2。
第三层:定时器设置与预加载缓冲区 我们使用标准公式计算寄存器:
频率 = 输入时钟 / ((PSC + 1) * (ARR + 1))
CCR = 职责比例 × (ARR + 1)
关键的是,我们启用了输出比较预加载位(OCxPE)。该功能起到至关重要的安全缓冲作用:当您在应用循环中更新占空比时,硬件会拦截写入操作,并等待当前计数周期完成后再执行,从而彻底消除周期中间的波形抖动。
常见陷阱与故障排除:
•引脚输出无信号?在访问任何外设寄存器前,请确保已启用 RCC->AHB1ENR 和 RCC->APB1ENR。
•使用 TIM1 而不是 TIM3?高级定时器如 TIM1 需要你显式地在 TIM1_BDTR 寄存器中设置主输出使能(MOE)位,否则引脚将完全处于关闭状态。
•覆盖通道?在CCMR1中配置通道属性时,务必使用读取-修改-写入的位操作(|=)。若直接使用赋值操作(=)来配置通道2,将静默清除通道1的设置。
本文编译自hackster.io





