C/C++ GPIO 开发

lgpio 简介

lgpio 是一个用于 Linux 系统的 GPIO 控制库,提供了简单易用的 C 语言接口来操作 GPIO、I2C、SPI 等外设。

主要特点

  • 高性能:C 语言实现,执行效率高
  • 跨平台兼容:支持多种 Linux 单板计算机
  • 功能丰富:支持 GPIO、I2C、SPI、PWM 等多种外设
  • 易于集成:提供标准的 C 库接口

支持的功能

功能 说明
GPIO 通用输入输出控制
I2C I²C 总线通信
SPI SPI 总线通信
PWM 脉宽调制输出
通知 GPIO 中断和事件通知

安装 lgpio

从源码安装

# 安装依赖工具
apt update
apt install -y git build-essential

# 克隆 lgpio 源码
git clone https://github.com/joan2937/lg.git
cd lg

# 编译并安装
make
make install

# 更新动态链接库缓存
ldconfig

验证安装

安装完成后,可以通过以下命令验证:

# 检查动态库
ldconfig -p | grep lgpio

应该能看到类似 liblgpio.so 的输出。

其他验证方法:

# 检查库文件是否存在
ls -l /usr/local/lib/liblgpio.*

# 检查头文件是否存在
ls -l /usr/local/include/lgpio.h

GPIO 控制

硬件准备

  • 主板
  • 杜邦线
  • 万用表(可选,用于测量电压)

GPIO 输出控制

以下示例演示如何使用 lgpio 控制 GPIO 引脚输出高低电平。

示例代码

创建 gpio_output.c 文件:

#include <stdio.h>
#include <stdlib.h>
#include <lgpio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 3) {
        printf("Usage: %s <gpio_num> <pin_level>\n", argv[0]);
        printf("Example: %s 36 1  # Set GPIO36 to HIGH\n", argv[0]);
        return 1;
    }

    int gpio_num = atoi(argv[1]);
    int pin_level = atoi(argv[2]);
    int h;

    // 打开 GPIO 芯片 4
    h = lgGpiochipOpen(4);
    if (h < 0) {
        printf("ERROR: %s (%d)\n", lguErrorText(h), h);
        return 1;
    }

    // 设置 GPIO 为输出模式并设置初始电平
    int e = lgGpioClaimOutput(h, 0, gpio_num, pin_level);
    if (e < 0) {
        printf("ERROR: %s (%d)\n", lguErrorText(e), e);
        lgGpiochipClose(h);
        return 1;
    }

    printf("GPIO %d set to %s\n", gpio_num, pin_level ? "HIGH (3.3V)" : "LOW (0V)");

    // 保持状态 5 秒
    sleep(5);

    // 释放 GPIO 资源
    lgGpioFree(h, gpio_num);
    lgGpiochipClose(h);

    return 0;
}

编译程序

gcc -o gpio_output gpio_output.c -llgpio

运行测试

测试低电平:

sudo ./gpio_output 36 0

输出:GPIO 36 set to LOW (0V)

测试高电平:

sudo ./gpio_output 36 1

输出:GPIO 36 set to HIGH (3.3V)

提示:GPIO36 对应 40Pin 的 Pin3。

GPIO 输入读取

以下示例演示如何读取 GPIO 引脚的状态。

示例代码

创建 gpio_input.c 文件:

#include <stdio.h>
#include <stdlib.h>
#include <lgpio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <gpio_num>\n", argv[0]);
        printf("Example: %s 36  # Read GPIO36 state\n", argv[0]);
        return 1;
    }

    int gpio_num = atoi(argv[1]);
    int h, e, value;

    // 打开 GPIO 芯片 4
    h = lgGpiochipOpen(4);
    if (h < 0) {
        printf("ERROR: %s (%d)\n", lguErrorText(h), h);
        return 1;
    }

    // 设置 GPIO 为输入模式
    e = lgGpioClaimInput(h, 0, gpio_num);
    if (e < 0) {
        printf("ERROR: %s (%d)\n", lguErrorText(e), e);
        lgGpiochipClose(h);
        return 1;
    }

    printf("Reading GPIO %d state (press Ctrl+C to exit)...\n", gpio_num);

    // 循环读取 GPIO 状态
    while (1) {
        value = lgGpioRead(h, gpio_num);
        if (value < 0) {
            printf("ERROR: %s (%d)\n", lguErrorText(value), value);
            break;
        }
        printf("GPIO %d: %s\n", gpio_num, value ? "HIGH" : "LOW");
        sleep(1);
    }

    // 释放资源
    lgGpioFree(h, gpio_num);
    lgGpiochipClose(h);

    return 0;
}

编译和运行

# 编译
gcc -o gpio_input gpio_input.c -llgpio

# 运行
sudo ./gpio_input 36

GPIO 输出反馈测试

以下示例演示如何通过一个引脚输出,另一个引脚读取来验证功能。

示例代码

创建 gpio_feedback.c 文件:

#include <stdio.h>
#include <lgpio.h>
#include <unistd.h>

int main()
{
    int h;
    int gpio_out = 36;  // Pin3 - GPIO36
    int gpio_in = 37;   // Pin5 - GPIO37

    // 打开 GPIO 芯片 4
    h = lgGpiochipOpen(4);
    if (h < 0) {
        printf("ERROR: %s (%d)\n", lguErrorText(h), h);
        return 1;
    }

    // 设置 GPIO36 为输出,GPIO37 为输入
    int e1 = lgGpioClaimOutput(h, 0, gpio_out, 0);
    int e2 = lgGpioClaimInput(h, 0, gpio_in);

    if (e1 < 0 || e2 < 0) {
        printf("ERROR: Failed to claim GPIO\n");
        lgGpiochipClose(h);
        return 1;
    }

    printf("=== GPIO Feedback Test ===\n");
    printf("Output: GPIO%d (Pin3)\n", gpio_out);
    printf("Input:  GPIO%d (Pin5)\n", gpio_in);
    printf("Please connect Pin3 and Pin5 together\n\n");

    for (int i = 0; i < 5; i++) {
        // 设置高电平
        lgGpioWrite(h, gpio_out, 1);
        usleep(100000);  // 100ms
        int val_high = lgGpioRead(h, gpio_in);
        printf("Output: HIGH | Input: %s\n", val_high ? "HIGH" : "LOW");

        sleep(1);

        // 设置低电平
        lgGpioWrite(h, gpio_out, 0);
        usleep(100000);
        int val_low = lgGpioRead(h, gpio_in);
        printf("Output: LOW  | Input: %s\n", val_low ? "HIGH" : "LOW");

        sleep(1);
    }

    // 释放资源
    lgGpioFree(h, gpio_out);
    lgGpioFree(h, gpio_in);
    lgGpiochipClose(h);

    printf("\nTest completed\n");
    return 0;
}

编译和运行

# 编译
gcc -o gpio_feedback gpio_feedback.c -llgpio

# 运行(需要先将 Pin3 和 Pin5 短接)
sudo ./gpio_feedback

预期输出:

=== GPIO Feedback Test ===
Output: GPIO36 (Pin3)
Input:  GPIO37 (Pin5)
Please connect Pin3 and Pin5 together

Output: HIGH | Input: HIGH
Output: LOW  | Input: LOW
Output: HIGH | Input: HIGH
Output: LOW  | Input: LOW
...

GPIO 引脚映射

Quectel Pi H1 主要使用 /dev/gpiochip4 进行 GPIO 控制。

常用引脚映射

物理引脚 引脚名称 GPIO 编号 GPIO 芯片 默认功能
Pin3 NFC_I2C_SDA 36 /dev/gpiochip4 I2C09_SDA
Pin5 NFC_I2C_SCL 37 /dev/gpiochip4 I2C09_SCL
Pin7 CAM2_RST 77 /dev/gpiochip4 GPIO
Pin11 GPIO_16 16 /dev/gpiochip4 GPIO/SPI/UART/I2C
Pin13 GPIO_17 17 /dev/gpiochip4 GPIO/SPI/UART/I2C

注意:Pin3 和 Pin5 默认配置为 I2C9 接口,如需作为 GPIO 使用,请确保没有其他设备占用。

完整的引脚映射请参考:40Pin 功能测试

lgpio API 参考

GPIO 芯片操作

// 打开 GPIO 芯片
int lgGpiochipOpen(int chip_num);

// 关闭 GPIO 芯片
int lgGpiochipClose(int handle);

GPIO 配置

// 申请 GPIO 为输出模式
int lgGpioClaimOutput(int handle, int flags, int gpio, int level);

// 申请 GPIO 为输入模式
int lgGpioClaimInput(int handle, int flags, int gpio);

// 释放 GPIO
int lgGpioFree(int handle, int gpio);

GPIO 读写

// 读取 GPIO 电平
int lgGpioRead(int handle, int gpio);

// 写入 GPIO 电平
int lgGpioWrite(int handle, int gpio, int level);

PWM 控制

// 输出 PWM 信号
int lgTxPwm(int handle, int gpio, float freq, float duty, int offset, int cycles);

常见问题

编译错误

问题:找不到 lgpio.h

fatal error: lgpio.h: No such file or directory

解决方法:

  1. 确认 lgpio 库已安装
  2. 检查头文件路径:ls /usr/local/include/lgpio.h
  3. 如果在其他路径,编译时指定:gcc -I/path/to/include ...

运行时错误

问题:Permission denied

解决方法:
使用 sudo 运行程序:

sudo ./gpio_output 36 1

问题:找不到共享库

error while loading shared libraries: liblgpio.so

解决方法:

# 更新动态链接库缓存
sudo ldconfig

# 或者设置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

引脚占用问题

如果遇到 GPIO 已被占用的错误,使用以下命令检查:

# 查看 GPIO 状态
gpioinfo /dev/gpiochip4 | grep -E "(line 36|line 37|consumer)"

如果引脚被占用,需要先释放或选择其他未占用的引脚。

参考资源