3. 网关外设GPIO控制

任务目标

在工业IoT网关上,通过Linux用户空间控制GPIO引脚,实现两个功能:驱动LED以1Hz频率闪烁,以及检测按键按下并打印事件。本案例采用libgpiod库(新版内核推荐方式),适用于Debian/Ubuntu嵌入式系统。

环境与依赖

  • 硬件:任意GPIO扩展芯片(如PCA9555)或CPU原生GPIO,本例以gpiochip0为例
  • 系统:Linux内核≥4.8,已加载gpio_ich/gpio_pca953x等驱动
  • 安装libgpiod开发库:sudo apt install libgpiod-dev gpiod
  • 接线:LED正极接GPIO17(经330Ω电阻),按键一端接GPIO27、另一端接GND(按下为低电平)

关键完整代码

// gpio_demo.c - 使用libgpiod控制LED闪烁与按键检测
#include <gpiod.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

static int running = 1;
void stop_handler(int sig) { running = 0; }

int main(void) {
    struct gpiod_chip *chip;
    struct gpiod_line *led_line, *btn_line;
    int ret, val;

    // 打开GPIO芯片(默认chip0,可按实际修改)
    chip = gpiod_chip_open_by_name("gpiochip0");
    if (!chip) { perror("打开gpiochip0失败"); return 1; }

    // 获取LED引脚(GPIO17)并配置为输出
    led_line = gpiod_chip_get_line(chip, 17);
    if (!led_line) { perror("获取LED引脚失败"); goto err_chip; }
    ret = gpiod_line_request_output(led_line, "led_demo", 0);
    if (ret < 0) { perror("配置LED为输出失败"); goto err_led; }

    // 获取按键引脚(GPIO27)并配置为输入(启用内部上拉)
    btn_line = gpiod_chip_get_line(chip, 27);
    if (!btn_line) { perror("获取按键引脚失败"); goto err_led; }
    ret = gpiod_line_request_input_flags(btn_line, "btn_demo",
                                         GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
    if (ret < 0) { perror("配置按键为输入失败"); goto err_btn; }

    // 注册Ctrl+C退出
    signal(SIGINT, stop_handler);

    printf("GPIO演示开始:LED闪烁,按下按键打印事件(Ctrl+C退出)\n");

    while (running) {
        // LED翻转
        val = gpiod_line_get_value(led_line);
        gpiod_line_set_value(led_line, !val);

        // 非阻塞读取按键(低电平有效)
        val = gpiod_line_get_value(btn_line);
        if (val == 0) {
            printf("[%ldms] 按键按下\n", (long)(clock() * 1000 / CLOCKS_PER_SEC));
            // 简单消抖:等待100ms后确认
            usleep(100000);
            if (gpiod_line_get_value(btn_line) == 0) {
                printf("按键事件确认\n");
                while (gpiod_line_get_value(btn_line) == 0)
                    usleep(10000);  // 等待释放
            }
        }
        usleep(500000);  // 500ms延时,实现1Hz闪烁
    }

    printf("\n退出,清理资源\n");
    gpiod_line_release(btn_line);
err_btn:
    gpiod_line_release(led_line);
err_led:
    gpiod_chip_close(chip);
err_chip:
    return 0;
}

编译与运行

# 编译(需安装libgpiod-dev)
gcc -o gpio_demo gpio_demo.c -lgpiod

# 运行(需要root权限访问GPIO)
sudo ./gpio_demo

验证方法与判定标准

  1. LED闪烁验证:观察LED以约1Hz频率(每秒亮灭各一次)闪烁。若LED不亮,检查GPIO编号是否正确(可用gpioinfo gpiochip0查看可用引脚)及接线极性。
  2. 按键检测验证:按下按键,终端应输出按键按下和按键事件确认;松开后再次按下可重复触发。若按键无响应,确认按键接在GPIO27与GND之间,且内部上拉已启用(按下为低电平)。
  3. 资源释放验证:按Ctrl+C退出时,程序应正常打印退出信息并释放GPIO资源,无错误提示。

常见问题

  • GPIO编号不匹配:不同网关的GPIO编号映射不同,先用gpiodetect和gpioinfo确认芯片名称与引脚号。
  • 权限不足:访问GPIO需要root权限,若提示Permission denied,使用sudo运行或添加udev规则。
  • 按键抖动:本代码已做简单消抖(100ms确认),若仍误触发可适当延长消抖时间。