C/C++ GPIO 开发
2025-11-26
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
解决方法:
- 确认 lgpio 库已安装
- 检查头文件路径:
ls /usr/local/include/lgpio.h - 如果在其他路径,编译时指定:
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)"
如果引脚被占用,需要先释放或选择其他未占用的引脚。