3. 按键控制LED

按键控制LED 学习卡片

任务说明

本案例在OpenHarmony南向设备上实现按键控制LED:读取GPIO按键输入状态,每次按下按键时切换LED的亮灭状态。需要完成GPIO初始化、按键状态读取、消抖处理及LED状态翻转的完整逻辑。

硬件连接

硬件连接说明:
LED正极 → GPIO2(输出)
LED负极 → GND(串联220Ω限流电阻)
按键一端 → GPIO3(输入,启用内部上拉)
按键另一端 → GND
Hi3861 开发板 GPIO2 GPIO3 220Ω LED GND 按键 GND 内部上拉

完整代码实现

创建文件 key_led_demo.c,代码如下:

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"

#define LED_GPIO 2          // LED 控制引脚
#define KEY_GPIO 3          // 按键输入引脚
#define KEY_PRESS_DOWN 0    // 按下为低电平(内部上拉)

static int g_led_on = 0;    // 当前LED状态:0-灭,1-亮

/* 按键消抖延时 */
static void KeyDebounceDelay(void)
{
    usleep(20000);  // 20ms 消抖
}

/* 读取按键状态(带消抖) */
static int ReadKeyState(void)
{
    if (IoTGpioGetInputVal(KEY_GPIO, NULL) == 0) {
        return 0;  // 读取失败,返回未按下
    }
    
    // 第一次读取
    IotGpioValue val1 = IOT_GPIO_VALUE1;
    IoTGpioGetInputVal(KEY_GPIO, &val1);
    
    if (val1 != KEY_PRESS_DOWN) {
        return 0;  // 未按下
    }
    
    KeyDebounceDelay();  // 延时消抖
    
    // 第二次读取确认
    IotGpioValue val2 = IOT_GPIO_VALUE1;
    IoTGpioGetInputVal(KEY_GPIO, &val2);
    
    return (val2 == KEY_PRESS_DOWN) ? 1 : 0;
}

/* 切换LED状态 */
static void ToggleLed(void)
{
    g_led_on = !g_led_on;
    IoTGpioSetOutputVal(LED_GPIO, g_led_on ? IOT_GPIO_VALUE1 : IOT_GPIO_VALUE0);
    printf("LED %s\n", g_led_on ? "ON" : "OFF");
}

/* 按键扫描任务 */
static void KeyScanTask(void)
{
    while (1) {
        if (ReadKeyState()) {
            ToggleLed();
            // 等待按键释放,防止连续触发
            while (ReadKeyState()) {
                usleep(10000);
            }
        }
        usleep(10000);  // 10ms 扫描周期
    }
}

/* 初始化函数 */
static void KeyLedDemoInit(void)
{
    // 初始化 GPIO
    IoTGpioInit(LED_GPIO);
    IoTGpioInit(KEY_GPIO);
    
    // 设置LED为输出模式
    IoTGpioSetDir(LED_GPIO, IOT_GPIO_DIR_OUT);
    IoTGpioSetOutputVal(LED_GPIO, IOT_GPIO_VALUE0);  // 初始LED灭
    
    // 设置按键为输入模式,启用内部上拉
    IoTGpioSetDir(KEY_GPIO, IOT_GPIO_DIR_IN);
    IoSetPull(KEY_GPIO, IOT_GPIO_PULL_UP);
    
    printf("Key LED demo initialized.\n");
    
    // 创建按键扫描任务
    osThreadAttr_t attr = {0};
    attr.name = "KeyScanTask";
    attr.stack_size = 4096;
    attr.priority = osPriorityNormal;
    
    if (osThreadNew(KeyScanTask, NULL, &attr) == NULL) {
        printf("Failed to create task.\n");
    }
}

SYS_RUN(KeyLedDemoInit);

代码说明

  • GPIO初始化:LED引脚设为推挽输出,初始输出低电平(LED灭);按键引脚设为输入并启用内部上拉电阻,按键未按下时读到高电平,按下时读到低电平。
  • 消抖处理:检测到按键第一次为低电平后延时20ms再次确认,避免机械抖动导致的误触发。
  • 状态切换:确认按键按下后翻转LED输出状态,并等待按键释放后再继续扫描,防止长按连续切换。
  • 任务调度:创建独立任务以10ms周期循环扫描按键状态,不阻塞主流程。

编译与烧录

  1. 将 key_led_demo.c 放入设备应用的 src 目录,并在 BUILD.gn 中添加该源文件。
  2. 在 config.py 或构建配置中确认目标芯片为Hi3861,执行编译命令:
python build.py wifiiot
  1. 编译完成后,使用HiBurn工具将生成的 Hi3861_wifiiot_app_allinone.bin 烧录至开发板。

验证方法

烧录成功后,开发板上电运行,通过串口观察日志输出。验证步骤如下:

  1. 系统启动后,串口打印 Key LED demo initialized.,此时LED处于熄灭状态。
  2. 按下按键并释放,观察LED应点亮,串口打印 LED ON。
  3. 再次按下并释放按键,LED应熄灭,串口打印 LED OFF。
  4. 快速连续按键多次,每次按下释放LED状态都应翻转一次,无漏检或误触发。

判定标准:每次按键按下并释放后LED亮灭状态切换一次,串口输出与LED状态一致,且无抖动导致的多次翻转,即判定功能正常。