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;
}

编译与运行

  1. 将上述代码保存为pwm_breath.c,放入M55开发板的编译工程中(需包含芯片头文件路径)。
  2. 编译生成固件:make(或使用厂商IDE直接编译)。
  3. 通过调试器(如J-Link)烧录固件至开发板。
  4. 复位开发板运行程序。

验证方法

  • 观察现象:LED亮度应呈现平滑的渐亮→渐暗循环,单周期约2秒(100步×10ms×2)。
  • 信号测量:使用示波器探头连接PB4引脚,应观察到频率1kHz、占空比周期性变化的方波信号。
  • 通过标准:LED亮度变化连续无闪烁,且周期稳定在2秒左右;示波器波形占空比在0%~100%之间线性往返变化。

常见问题

  • LED不亮:检查PB4引脚复用配置是否正确,以及PWM时钟是否已使能。
  • 亮度跳变:确认STEP_DELAY不小于5ms,否则人眼可察觉阶梯感。
  • 周期偏差:delay_ms为软件延时,实际周期受编译器优化影响,若需精确周期应改用定时器中断。