跳转至

GPIO 输出

LED 闪烁 — GPIO 驱动 | sample: blinky

学习目标

  • 掌握 GPIO 输出的完整配置流程:uapi_pin_set_mode()uapi_gpio_init()uapi_gpio_set_dir()uapi_gpio_toggle()
  • 理解 GPIO 电平控制——高电平驱动 vs 低电平驱动的硬件差异
  • 能够在 WS63 上通过 GPIO 控制 LED 闪烁

基本概念

GPIO 输出做什么

GPIO(General Purpose Input/Output)输出是 MCU 控制外设的最基本方式——CPU 写一个寄存器位,引脚输出高电平或低电平,驱动 LED 亮灭、继电器通断、三极管开关。

flowchart LR
    C[CPU] -->|写寄存器| G[GPIO 控制器]
    G -->|输出电平| P[物理引脚]
    P -->|高/低电平| L[LED / 继电器 / 三极管]

三个步骤

WS63 的 GPIO 输出需要三步配置:

步骤 API 说明
1. 引脚复用 uapi_pin_set_mode(pin, HAL_PIO_FUNC_GPIO) WS63 引脚多功能——先选 GPIO 模式
2. 方向 uapi_gpio_set_dir(pin, GPIO_DIRECTION_OUTPUT) 设置为输出
3. 操作 uapi_gpio_toggle(pin) / uapi_gpio_set_val(pin, level) 翻转或写指定电平

引脚复用在 pinctrl 文档中有详细说明。本篇聚焦 GPIO 输出操作本身。

驱动能力

GPIO 的驱动电流有限(通常几 mA)。LED 必须串联限流电阻,继电器/电机必须通过三极管/MOSFET 驱动——不能直接用 GPIO 引脚驱动大电流负载。

涉及 API

API 用途 头文件
uapi_pin_set_mode(pin, HAL_PIO_FUNC_GPIO) 设置引脚为 GPIO 功能 pinctrl.h
uapi_gpio_init() 初始化 GPIO 子系统 gpio.h
uapi_gpio_set_dir(pin, GPIO_DIRECTION_OUTPUT) 设置引脚为输出方向 gpio.h
uapi_gpio_set_val(pin, GPIO_LEVEL_LOW) 写指定电平(高/低) gpio.h
uapi_gpio_toggle(pin) 翻转当前电平 gpio.h

案例说明

做什么

配置一个 GPIO 引脚为输出,以 500ms 间隔翻转电平,驱动 LED 闪烁。这是嵌入式世界的 "Hello World"。

规格与功能

规格项 说明
引脚 CONFIG_BLINKY_PIN(Kconfig 可配)
模式 GPIO 输出
初始电平 低电平(LED 灭)
闪烁周期 500ms(osal_msleep(500) + toggle
任务优先级 24
任务栈 0x1000(4096 字节)

程序运行流程:引脚复用 → GPIO 初始化 → 设方向 → 设初始电平 → 死循环翻转 + 延时。

案例流程

sequenceDiagram
    participant E as 入口
    participant G as GPIO
    participant L as LED

    E->>G: uapi_pin_set_mode — GPIO 功能
    E->>G: uapi_gpio_init
    E->>G: uapi_gpio_set_dir — 输出
    E->>G: uapi_gpio_set_val — 低电平
    Note over L: LED 灭

    loop 每 500ms
        E->>G: uapi_gpio_toggle
        Note over L: 翻转亮/灭
        E->>E: osal_msleep(500)
    end

案例操作指导

第一步:编译

fbb build ws63-liteos-app

第二步:烧录

第三步:验证

LED 以 1Hz 频率闪烁(500ms 亮 + 500ms 灭)。串口周期输出 "Blinky working."。

关键配置

参数 说明
BLINKY_DURATION_MS 500 翻转间隔,越小闪得越快
任务优先级 24 中优先级,不影响 SLE 等关键任务
任务栈 4096 简单的 GPIO 操作,4096 足够
初始电平 GPIO_LEVEL_LOW LED 灭——根据电路选择低或高

代码详解

完整代码参考 src/application/samples/peripheral/blinky/blinky_demo.c

#include "pinctrl.h"
#include "gpio.h"
#include "soc_osal.h"
#include "app_init.h"

#define BLINKY_DURATION_MS        500
#define BLINKY_TASK_PRIO          24
#define BLINKY_TASK_STACK_SIZE    0x1000

static int blinky_task(const char *arg)
{
    (void)arg;

    /* 第1步: 引脚复用为 GPIO */
    uapi_pin_set_mode(CONFIG_BLINKY_PIN, HAL_PIO_FUNC_GPIO);

    /* 第2步: 设置方向为输出 */
    uapi_gpio_set_dir(CONFIG_BLINKY_PIN, GPIO_DIRECTION_OUTPUT);

    /* 第3步: 设置初始电平——低电平,LED 灭 */
    uapi_gpio_set_val(CONFIG_BLINKY_PIN, GPIO_LEVEL_LOW);

    while (1) {
        osal_msleep(BLINKY_DURATION_MS);          // 等 500ms
        uapi_gpio_toggle(CONFIG_BLINKY_PIN);      // 翻转电平
        osal_printk("Blinky working.\r\n");       // 打印状态
    }
    return 0;
}

static void blinky_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle = osal_kthread_create(
        (osal_kthread_handler)blinky_task, 0,
        "BlinkyTask", BLINKY_TASK_STACK_SIZE);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, BLINKY_TASK_PRIO);
        osal_kfree(task_handle);  // 任务句柄可释放
    }
    osal_kthread_unlock();
}

app_run(blinky_entry);

osal_kfree(task_handle) 释放的是创建任务返回的句柄(osal_task*),不是任务本身。任务创建后独立运行,句柄不再需要即可释放。