返回首页

PWM LED 学习笔记

概述

有两个 PWM 示例:

  1. blinky_pwm - PWM 调光闪烁示例(周期变化)
  2. led/pwm - 完整的 LED PWM 控制(呼吸灯效果)

示例 1:blinky_pwm(PWM 周期变化)

1.1 核心代码

static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));

#define MIN_PERIOD PWM_SEC(1U) / 128U
#define MAX_PERIOD PWM_SEC(1U)

int main(void)
{
    uint32_t max_period;
    uint32_t period;
    uint8_t dir = 0U;

    // 校准 PWM 周期
    max_period = MAX_PERIOD;
    while (pwm_set_dt(&pwm_led0, max_period, max_period / 2U)) {
        max_period /= 2U;  // 如果失败,降低周期
    }

    period = max_period;
    while (1) {
        // 设置占空比 = 50%
        pwm_set_dt(&pwm_led0, period, period / 2U);
        
        // 周期翻倍或减半,实现频率变化
        period = dir ? (period * 2U) : (period / 2U);
        k_sleep(K_SECONDS(4U));
    }
}

1.2 关键概念

1.3 PWM 周期设置

#define PWM_SEC(1U)      // 1秒周期
#define PWM_SEC(1U) / 128U  // 1/128秒 ≈ 7.8ms

示例 2:led/pwm(呼吸灯效果)

2.1 核心 API

// 获取 LED PWM 设备
const struct device *led_pwm = DEVICE_DT_GET(LED_PWM_NODE_ID);

// 开灯
led_on(led_pwm, led);

// 关灯
led_off(led_pwm, led);

// 设置亮度 (0-100)
led_set_brightness(led_pwm, led, level);

// 闪烁
led_blink(led_pwm, led, on_time_ms, off_time_ms);

2.2 呼吸灯效果实现

// 逐渐增加亮度
for (level = 0; level <= MAX_BRIGHTNESS; level++) {
    led_set_brightness(led_pwm, led, level);
    k_sleep(K_MSEC(CONFIG_FADE_DELAY));
}

// 逐渐减少亮度
for (level = MAX_BRIGHTNESS; level >= 0; level--) {
    led_set_brightness(led_pwm, led, level);
    k_sleep(K_MSEC(CONFIG_FADE_DELAY));
}

2.3 设备树配置

/ {
    aliases {
        pwm-led0 = &pwm_led0;
    };
    
    pwmleds {
        compatible = "pwm-leds";
        pwm_led0: pwm_led_0 {
            pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
            label = "LED 0";
        };
    };
};

3. PWM 核心 API

3.1 获取 PWM 设备

// 从设备树别名获取
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_ALIAS(pwm_led0));

// 直接从节点获取
static const struct pwm_dt_spec pwm_led0 = PWM_DT_SPEC_GET(DT_NODELABEL(my_pwm));

3.2 检查设备就绪

if (!pwm_is_ready_dt(&pwm_led0)) {
    printk("PWM device not ready\n");
}

3.3 设置 PWM

// 使用 pwm_dt_spec
int pwm_set_dt(const struct pwm_dt_spec *spec, uint32_t period, uint32_t pulse);

// 直接指定通道
int pwm_set(const struct device *dev, uint32_t ch, uint32_t period, uint32_t pulse, pwm_flags_t flags);

参数说明:

参数单位说明
period纳秒PWM 周期
pulse纳秒脉冲宽度(高电平时间)
flags-极性等标志

3.4 占空比计算

// 50% 占空比
pwm_set_dt(&pwm_led0, period, period / 2);

// 25% 占空比
pwm_set_dt(&pwm_led0, period, period / 4);

// 75% 占空比
pwm_set_dt(&pwm_led0, period, period * 3 / 4);

4. PWM 配置参数

4.1 周期与频率

// 频率 = 1 / 周期
PWM_SEC(1)       // 1 Hz (1秒)
PWM_MSEC(20)     // 50 Hz (20ms)
PWM_USEC(50)     // 20 kHz (50μs)

4.2 极性

PWM_POLARITY_NORMAL   // 正极性:高电平有效
PWM_POLARITY_INVERTED // 反极性:低电平有效

4.3 nRF54L15 PWM 配置

&pwm0 {
    status = "okay";
    ch0-pin = <13>;  // LED1
    ch1-pin = <14>;  // LED2
    ch2-pin = <15>;  // LED3
};

5. LED 子系统 API

5.1 初始化

// 从设备树获取 LED 规范
static const struct led_dt_spec led0 = LED_DT_SPEC_GET_OR(DT_ALIAS(led0), {0});

5.2 控制函数

// 开/关
led_on(dev, num);
led_off(dev, num);

// 设置亮度 (0-255 或 0-100,取决于驱动)
led_set_brightness(dev, num, level);

// 闪烁 (部分驱动支持)
led_blink(dev, num, delay_on, delay_off);

// 获取颜色 (RGB LED)
led_set_color(dev, num, color);

6. 实际应用

6.1 呼吸灯效果

void breathing_led(const struct device *led, uint8_t num)
{
    int direction = 1;  // 1=变亮, -1=变暗
    uint8_t brightness = 0;
    
    while (1) {
        led_set_brightness(led, num, brightness);
        brightness += direction * 5;
        
        if (brightness >= 100) direction = -1;
        if (brightness <= 0) direction = 1;
        
        k_msleep(30);  // 约 30fps
    }
}

6.2 舵机控制 (Servo)

// 舵机通常需要 50Hz (20ms) 周期
// 脉宽 1ms = 0°, 1.5ms = 90°, 2ms = 180°
#define SERVO_PERIOD PWM_MSEC(20)   // 20ms 周期
#define SERVO_MIN   PWM_USEC(500)   // 0.5ms
#define SERVO_MAX   PWM_USEC(2500)  // 2.5ms

void servo_set_angle(const struct device *pwm, uint8_t ch, uint8_t angle)
{
    uint32_t pulse = SERVO_MIN + (angle * (SERVO_MAX - SERVO_MIN) / 180);
    pwm_set(pwm, ch, SERVO_PERIOD, pulse, 0);
}

7. 关键问题总结

问题答案
PWM 周期怎么设?pwm_set_dt(&spec, period, pulse) 第一个参数
占空比怎么算?pulse / period * 100%
呼吸灯怎么实现?循环改变 led_set_brightness() 的亮度值
舵机控制频率?通常 50Hz (20ms 周期)
nRF54L15 PWM 在哪配?设备树 pwm0 节点

8. 与其他示例对比

示例驱动层级控制方式
blinky_pwmPWM 直接驱动改变周期
led/pwmLED 子系统亮度/闪烁
Button GPIOGPIO 输入轮询/中断

9. 学习心得

  1. PWM 基础:周期 + 脉宽决定占空比
  2. LED 子系统:封装了 PWM,提供统一亮度 API
  3. 设备树配置:PWM 通道与物理引脚映射
  4. 应用场景:LED 调光、舵机控制、电机调速

小白 🤖 2026-03-14