这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » STM32 » 【Zephyr|NUCLEO-C562RE】3、PWM 驱动双 LED 呼吸效果

共1条 1/1 1 跳转至

【Zephyr|NUCLEO-C562RE】3、PWM 驱动双 LED 呼吸效果

高工
2026-06-10 16:03:50     打赏

基于 Zephyr RTOS,使用 STM32 定时器 PWM 驱动双 LED 呼吸效果


1. 概述

本项目使用 Zephyr 的 PWM LED 驱动 (pwm-leds) 控制 STM32 Nucleo-C562RE 板上的两个 LED(PA0 / PA1),产生"呼吸灯"效果 —— 两个 LED 的亮度按三角波规律交替变化,亮度的最高点彼此错开半个周期,形成亮度在两灯之间"流动"的视觉效果。

项值
目标芯片STM32C562 (Cortex-M33)
定时器TIM2 (通用定时器)
PWM 通道CH1 (PA0) / CH2 (PA1)
复用功能AF1
周期1 ms(1 kHz PWM)
占空比粒度0 ~ 100(百分比,0~10000 内部)
呼吸周期2 s(吸 + 呼)
步进20 ms / 步(每周期 100 步)

2. 硬件连接

                 ┌──────────────────────────┐
   PA0 (TIM2_CH1) ─► LED1 ──► GND
   PA1 (TIM2_CH2) ─► LED2 ──► GND
                 └──────────────────────────┘

注意:板载 LD1(PA5,对应 led0 alias)由板级 BSP 单独管理,本驱动不触碰它。PA2 在本板上为 USART2_TX,故意未使用。


3. 启用配置 (prj.conf)

CONFIG_GPIO=y
CONFIG_PWM=y
CONFIG_LED=y
CONFIG_LED_PWM=y        # pwm-leds 驱动
CONFIG_PWM_STM32=y      # STM32 的 PWM 后端
配置项作用
CONFIG_PWM启用 Zephyr PWM 通用子系统
CONFIG_PWM_STM32启用 STM32 系列 PWM 驱动后端
CONFIG_LED启用 LED 通用 API (<zephyr/drivers/led.h>)
CONFIG_LED_PWM启用基于 PWM 的 LED 驱动 pwm-leds

4. 设备树配置 (boards/nucleo_c562re.overlay)

4.1 关键节点

#include <zephyr/dt-bindings/pwm/pwm.h>
#include <zephyr/dt-bindings/pinctrl/stm32-pinctrl.h>

/ {
    aliases {
        led1 = &led_2;   /* 把应用可见的 led1/led2 指向 pwm_leds 子节点 */
        led2 = &led_3;
    };
};

&{/} {
    pwm_leds {
        compatible = "pwm-leds";

        led_2: led_2 {
            /* PA0 = TIM2_CH1, AF1 */
            pwms = <&pwm2 1 PWM_MSEC(1) 0>;
            label = "PA0_LED";
        };

        led_3: led_3 {
            /* PA1 = TIM2_CH2, AF1 */
            pwms = <&pwm2 2 PWM_MSEC(1) 0>;
            label = "PA1_LED";
        };
    };
};

&pinctrl {
    timers2_pwm_pa0: timers2_pwm_pa0 {
        pinmux = <STM32_PINMUX('A', 0, AF1)>;   /* TIM2_CH1 */
    };
    timers2_pwm_pa1: timers2_pwm_pa1 {
        pinmux = <STM32_PINMUX('A', 1, AF1)>;   /* TIM2_CH2 */
    };
};

&timers2 {
    status = "okay";
    st,prescaler = <0>;

    pwm2: pwm {
        pinctrl-0 = <&timers2_pwm_pa0 &timers2_pwm_pa1>;
        pinctrl-names = "default";
        status = "okay";
    };
};

4.2 pwms 属性详解

   pwms = <&pwm2     1     PWM_MSEC(1)       0>;
       │      │  │           │
       │      │  │           └─ 初始相位(0 = 与通道 0 同步)
       │      │  └───────────── 周期 = 1 ms
       │      └──────────────── 通道号 (1 = CH1, 2 = CH2)
       └─────────────────────── 控制器 phandle (pwm2 = TIM2)
  • PWM_MSEC(1):使用 <zephyr/dt-bindings/pwm/pwm.h> 中的宏,把 1 ms 转成对应定时器 ticks。

  • 0:初始占空比(占空比由运行时 led_set_brightness_dt() 覆盖)。

  • 周期最终由应用通过 led_set_brightness_dt() 间接决定 — 此处只是初始化默认值。

4.3 引脚配置说明

节点含义
&pinctrl注入自定义 pinctrl 子节点,把 PA0/PA1 复用到 TIM2 的 AF1
&timers2启用 TIM2 控制器;st,prescaler = <0> 表示 1:1 分频(不预分频)
pinctrl-0控制器默认状态使用上面定义的两组引脚配置

5. 应用代码解析 (src/main.c)

5.1 关键宏

#define LED_COUNT        2
#define BREATH_PERIOD_MS 2000   /* 一个完整呼吸 ≈ 2 s */
#define STEP_MS          20     /* 每步 20 ms → 一周期 100 步 */

#define LED1_NODE DT_ALIAS(led1)   /* = &led_2 (PA0) */
#define LED2_NODE DT_ALIAS(led2)   /* = &led_3 (PA1) */

编译期保护:若 led1/led2 alias 缺失,编译直接报错。

#if !DT_NODE_HAS_STATUS(LED1_NODE, okay) || \
    !DT_NODE_HAS_STATUS(LED2_NODE, okay)
#error "overlay not effective: led1/led2 aliases missing"
#endif

5.2 获取 LED spec

static const struct led_dt_spec leds[] = {
    LED_DT_SPEC_GET(LED1_NODE), /* PA0 = TIM2_CH1 */
    LED_DT_SPEC_GET(LED2_NODE), /* PA1 = TIM2_CH2 */
};

led_dt_spec 由 LED_DT_SPEC_GET() 在编译期从 devicetree 提取(控制器指针、通道、周期)。

5.3 三角波亮度

static inline uint8_t triangle_brightness(uint16_t phase, uint16_t steps)
{
    if (phase < steps / 2) {
        return (uint8_t)((100U * phase) / (steps / 2));
    } else {
        return (uint8_t)(100U - (100U * (phase - steps / 2)) / (steps / 2));
    }
}

输入 phase ∈ [0, steps),输出 0→100→0 的对称三角波。

phase输出
00
2550
4998
50100
7550
990

5.4 主循环

uint16_t phase = 0;
while (1) {
    for (size_t i = 0; i < LED_COUNT; i++) {
        uint16_t p = (phase + i * phase_offset) % steps;
        uint8_t  b = triangle_brightness(p, steps);
        led_set_brightness_dt(&leds[i], b);
    }
    phase = (phase + 1) % steps;
    k_msleep(STEP_MS);
}
  • phase_offset = (steps / 2) / LED_COUNT = 25:两灯之间错开 1/4 周期,视觉上呈"流动"。

  • 每次循环 phase++ 并 k_msleep(20),整周期 100 × 20 ms = 2000 ms。


6. 关键 API 速查

API头文件说明
LED_DT_SPEC_GET(node)<zephyr/drivers/led.h>编译期从 devicetree 构造 led_dt_spec
led_is_ready_dt(spec)同上运行时检查 LED 设备是否就绪
led_set_brightness_dt(spec, value)同上设置亮度(0 = 灭,100 = 最亮;LED_COLOR_* 通用 LED 也接受 0..255)
k_msleep(ms)<zephyr/kernel.h>非忙等延时(线程挂起)
printk(...)<zephyr/kernel.h>内核打印
DT_ALIAS(name)<zephyr/devicetree.h>解析 alias 节点引用
DT_NODE_HAS_STATUS(n, okay)同上编译期判断节点是否 status = "okay"

led_set_brightness 内部流程

应用 (0..100)
   │  led_set_brightness_dt()
   ▼
led_pwm driver
   │  duty = brightness / max_duty  × period
   ▼
pwm_set_pulse_dt(&pwm_spec, pulse)
   │  controller = stm32_pwm
   ▼
stm32_pwm: 配置 TIM2_CCRx
   │
   ▼
硬件: 比较匹配翻转 / PWM 模式 1

7. 编译与烧录

# 在工程根目录
west build -b nucleo_c562re -p auto
west flash
west espressif monitor   # 或任何 minicom, picocom, 串口工具

启动后串口应打印:

breathing-LED (PWM) startup: PA0 <-> PA1, period 2000ms, step 20ms

板子上的 PA0 / PA1 两个 LED 即可看到呼吸效果,亮度此起彼伏。


8. 自定义指南

8.1 改变呼吸速度

修改 main.c 顶部宏:

#define BREATH_PERIOD_MS 4000   /* 改成 4 s 一周期 */
#define STEP_MS     10     /* 改成 10 ms 一步 → 颗粒更细 */

8.2 增加 LED 数量

  1. 在 overlay 添加 led_4、led_5 ... 节点(pwms = <&pwm2 N PWM_MSEC(1) 0>;)。

  2. 在 overlay aliases 添加 led3 = &led_4;。

  3. LED_COUNT 改为实际数量,phase_offset 自动按 half / LED_COUNT 计算。

8.3 换用其他定时器

将 overlay 中的 &pwm2 改为 &pwm3、&timers2 改为 &timers3,对应引脚换为该定时器支持的复用引脚(参考芯片 datasheet alternate function 表)。

8.4 改占空比分辨率

led_set_brightness_dt() 的第二个参数是百分比 0~100。底层 pwm_set_pulse_dt() 才接受 ticks。若需更高精度,应直接使用 pwm API 而非 led API。

8.5 关闭呼吸,关断输出

led_off_dt(&leds[0]);
led_off_dt(&leds[1]);

9. 常见问题 (FAQ)

现象可能原因解决方案
编译报 __device_dts_ord_DT_N_ALIAS_led_P_pwms_IDX_0_PH_ORD undeclaredoverlay 没有 pwm-leds 节点或 alias 未生效确认 .overlay 在 boards 目录且 west build 实际应用了它
LED 不亮timers2 未启用 / 引脚未复用 / pinctrl-0 引用错检查 &timers2 { status = "okay"; } 与 pinctrl 节点
灯全亮(亮度不变)循环里只设了一次占空比确认主循环里 phase 在递增且 k_msleep(STEP_MS) 在执行
呼吸"卡顿"STEP_MS 过大改为 10 ms 或更小
想用 RGB 灯pwm-leds 不支持多通道组合改用 pca963x 等专用驱动,或直接使用 pwm API 自己组合

10. 文件清单

路径作用
src/main.c主程序:循环产生三角波并设置 LED 亮度
boards/nucleo_c562re.overlay设备树 overlay:定义 pwm_leds、pinctrl、timers2
prj.confKconfig 片段:开启 PWM、LED_PWM、PWM_STM32
CMakeLists.txt链接 src/main.c 到 app target
README.rst板级 / 工程说明(与本文档互补)
sample.yamlTwister 测试元数据

11. 实现效果

led_pwm_web.gif





关键词: NUCLEO-C562RE     Zephyr     TIM         

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]