4. PWM输出与呼吸灯
任务目标
使用STM32定时器的PWM模式,通过调节占空比控制LED亮度,实现呼吸灯效果(亮度从暗到亮再回到暗的周期性变化)。
硬件与依赖
- STM32F103系列开发板(以C8T6为例)
- 板载LED连接在PC13(或PB1,根据实际板卡调整)
- 使用定时器TIM3的通道2(PA7)输出PWM,或使用TIM2通道1(PA0)——本案例以TIM3_CH2(PA7)为例
- 标准外设库(SPL)或HAL库均可,本实现使用寄存器方式,无需额外库依赖
核心实现
呼吸灯的关键在于PWM频率(约1kHz以上避免闪烁)和占空比按正弦或三角波规律变化。以下代码使用TIM3_CH2输出PWM,主循环中通过软件延时改变比较寄存器值,实现约2秒周期的呼吸效果。
// PWM呼吸灯 - STM32F103寄存器版
#include "stm32f10x.h"
void TIM3_PWM_Init(void) {
// 1. 使能时钟:GPIOA和TIM3
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// 2. PA7配置为复用推挽输出,50MHz
GPIOA->CRL &= ~(0xF << 28); // 清除CNF7和MODE7
GPIOA->CRL |= (0xB << 28); // CNF=10(复用推挽), MODE=11(50MHz)
// 3. TIM3时钟 = 72MHz,分频72 → 1MHz计数频率
TIM3->PSC = 72 - 1; // 预分频
TIM3->ARR = 1000 - 1; // 自动重载,PWM频率 = 1MHz/1000 = 1kHz
// 4. 通道2配置为PWM模式1,输出比较
TIM3->CCMR1 |= TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; // PWM模式1 (110)
TIM3->CCMR1 |= TIM_CCMR1_OC2PE; // 输出比较预装载使能
TIM3->CCER |= TIM_CCER_CC2E; // 使能通道2输出
// 5. 初始占空比0%
TIM3->CCR2 = 0;
// 6. 使能自动重载预装载,启动定时器
TIM3->CR1 |= TIM_CR1_ARPE;
TIM3->CR1 |= TIM_CR1_CEN;
}
// 简易延时(约1ms @72MHz,不精确但足够呼吸灯使用)
void Delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 4000; i++) {
__NOP();
}
}
int main(void) {
TIM3_PWM_Init();
uint16_t duty = 0;
int8_t direction = 1; // 1=变亮, -1=变暗
while (1) {
TIM3->CCR2 = duty; // 更新占空比
// 三角波变化:0→1000→0,步进5,每步延时2ms → 周期约0.8s
duty += direction * 5;
if (duty >= 1000) {
duty = 1000;
direction = -1;
} else if (duty == 0) {
direction = 1;
}
Delay_ms(2);
}
}
编译与烧录
- 使用Keil MDK或STM32CubeIDE创建空工程,芯片选择STM32F103C8。
- 将上述代码复制到main.c中,替换默认生成的main函数。
- 编译工程,通过ST-Link或串口ISP烧录生成的hex文件。
验证方法
- 硬件连接:将PA7引脚通过限流电阻(约100Ω)连接LED阳极,阴极接地。若使用板载LED(如PC13),需修改代码将PWM输出到对应引脚,或直接外接LED到PA7。
- 观察现象:上电后LED亮度应从暗逐渐变亮,再逐渐变暗,循环往复,周期约0.8秒。
- 示波器验证(可选):将示波器探头接PA7,应看到频率约1kHz、占空比周期性变化的方波信号。
- 判定通过:LED呈现平滑的呼吸效果,无肉眼可见闪烁,亮度变化连续。
关键说明
- PWM频率由
72MHz / (PSC+1) / (ARR+1)决定,本配置为1kHz,高于人眼闪烁感知阈值。 - 呼吸周期由步进值和延时共同决定:
1000/5 * 2ms * 2 = 800ms。调整步进或延时即可改变呼吸速度。 - 若LED接在PA7但亮度变化不明显,检查限流电阻是否过大(建议100~330Ω),或确认TIM3_CH2复用映射是否正确(默认PA7无需重映射)。