3. PWM呼吸灯
任务目标
使用M55/U55开发板上的PWM模块驱动LED,通过调节PWM占空比实现LED亮度从暗到亮、再从亮到暗的周期性渐变,形成呼吸灯效果。本案例直接操作硬件寄存器,不依赖厂商SDK中的高级驱动封装。
硬件与原理
- 硬件连接:LED连接至开发板PWM输出通道(以M55为例,使用PWM0_CH0,对应引脚PB4)。
- 实现原理:PWM频率固定(如1kHz),通过软件定时改变占空比。占空比从0%逐步增至100%,再逐步减至0%,循环往复。每次调整间隔约10ms,单周期约2秒。
关键代码
#include "m55_reg.h" // 寄存器地址定义
#define PWM_BASE 0x40020000UL
#define PWM_CTRL (*(volatile uint32_t *)(PWM_BASE + 0x00))
#define PWM_PERIOD (*(volatile uint32_t *)(PWM_BASE + 0x04))
#define PWM_DUTY (*(volatile uint32_t *)(PWM_BASE + 0x08))
#define PWM_EN (*(volatile uint32_t *)(PWM_BASE + 0x0C))
#define PERIOD_VAL 1000 // 1kHz PWM,周期计数值
#define STEP_DELAY 10 // 占空比调整间隔(ms)
#define STEP_COUNT 100 // 占空比步进数(0~100)
static void delay_ms(uint32_t ms) {
for (volatile uint32_t i = 0; i < ms * 4000; i++);
}
void pwm_breath_init(void) {
// 配置PB4为PWM0_CH0功能(复用寄存器地址按实际芯片手册)
GPIO_MFCR &= ~(0x3 << 16);
GPIO_MFCR |= (0x2 << 16);
// PWM时钟使能(时钟控制寄存器)
CLK_APBCLK |= (1 << 3);
// 关闭PWM,配置周期与初始占空比
PWM_EN = 0;
PWM_PERIOD = PERIOD_VAL;
PWM_DUTY = 0;
PWM_CTRL = 0x01; // 边沿对齐,向上计数
PWM_EN = 1; // 使能PWM输出
}
void pwm_breath_loop(void) {
uint32_t duty = 0;
int8_t dir = 1;
while (1) {
PWM_DUTY = duty;
delay_ms(STEP_DELAY);
duty += dir;
if (duty >= STEP_COUNT) {
duty = STEP_COUNT;
dir = -1;
} else if (duty == 0) {
dir = 1;
}
}
}
int main(void) {
pwm_breath_init();
pwm_breath_loop();
return 0;
}
编译与运行
- 将上述代码保存为
pwm_breath.c,放入M55开发板的编译工程中(需包含芯片头文件路径)。 - 编译生成固件:
make(或使用厂商IDE直接编译)。 - 通过调试器(如J-Link)烧录固件至开发板。
- 复位开发板运行程序。
验证方法
- 观察现象:LED亮度应呈现平滑的渐亮→渐暗循环,单周期约2秒(100步×10ms×2)。
- 信号测量:使用示波器探头连接PB4引脚,应观察到频率1kHz、占空比周期性变化的方波信号。
- 通过标准:LED亮度变化连续无闪烁,且周期稳定在2秒左右;示波器波形占空比在0%~100%之间线性往返变化。
常见问题
- LED不亮:检查PB4引脚复用配置是否正确,以及PWM时钟是否已使能。
- 亮度跳变:确认
STEP_DELAY不小于5ms,否则人眼可察觉阶梯感。 - 周期偏差:
delay_ms为软件延时,实际周期受编译器优化影响,若需精确周期应改用定时器中断。