前言

在嵌入式 Linux 開發中,控制 GPIO 引腳 是再常見不過的需求。
無論是點亮一個 LED,還是檢測一個按鍵,都離不開對 GPIO 的操作。

本文帶你從最傳統的 /sys/class/gpio 接口入門,再到現代的 libgpiod 接口。
兩者對比之後,你將清楚地知道:

哪種方式適合快速調試?哪種方式適合正式產品開發?


🧩 一、什麼是 GPIO?

GPIO(General Purpose Input/Output)是“通用輸入輸出口”的縮寫。
它是一種最基礎的硬件接口,用來:

  • 輸出高低電平(控制 LED、繼電器等)
  • 讀取電平狀態(檢測按鍵、傳感器信號等)

Linux 內核會把硬件 GPIO 管理起來,通過驅動暴露給用户空間使用。
而用户空間訪問 GPIO 的方式,主要有兩種:

  1. 舊接口:sysfs(通過 /sys/class/gpio
  2. 新接口:libgpiod(通過 /dev/gpiochipX

⚙️ 二、傳統方式:Sysfs GPIO 接口

在早期的 Linux 系統中,控制 GPIO 通常通過 sysfs 文件系統來完成。
所有 GPIO 節點都位於:

/sys/class/gpio/

🔹 示例操作

cd /sys/class/gpio

# 1. 導出編號為 193 的 GPIO
echo 193 > export

# 2. 設置方向為輸出
echo out > gpio193/direction

# 3. 輸出高電平
echo 1 > gpio193/value

# 4. 輸出低電平
echo 0 > gpio193/value

# 5. 讀當前狀態
cat gpio193/value

執行這些命令後,就能在硬件上看到 LED 燈亮起或熄滅。


🧮 三、GPIO 編號的計算公式(為什麼是 193?)

在 Linux 中,每個 GPIO 控制器(gpiochip) 會負責一組引腳(通常 32 個),
每組都從一個基地址(base)開始編號。

你可以通過以下命令查看:

cat /sys/class/gpio/gpiochip0/base
cat /sys/class/gpio/gpiochip0/ngpio

例如:

base = 0
ngpio = 224

這表示:

  • 第一個控制器(gpiochip0)管理 GPIO0 ~ GPIO223
  • 每個 GPIO 實際編號 = base + 引腳偏移號

🔹 計算公式

GPIO 全局編號 = GPIO 控制器基址(base) + 引腳號(offset)


🔹 舉例:以全志(Allwinner)芯片為例

在很多全志平台(如 H3/H5/F1C100s 等)中,GPIO 命名方式是:

PnX —— n 表示組號,X 表示組內編號
每組通常有 32 個引腳(編號 0~31)

因此計算公式可以寫成:

GPIO 編號 = n × 32 + X

例如:

引腳名稱

組號 (n)

組內編號 (X)

全局編號 (n×32 + X)

PA0

0

0

0

PA10

0

10

10

PB0

1

0

32

PC1

2

1

65

PD0

3

0

96

PG1

6

1

6×32 + 1 = 193

所以當你執行:

echo 193 > /sys/class/gpio/export

實際上就是導出了 PG1 這個物理引腳。


💡 小結:

  • 不同芯片的組號不同(例如 PAPBPG 等)
  • 每組佔 32 個引腳
  • 編號從 0 開始
  • 有些平台(如 Rockchip、NXP)命名方式略有不同,但計算原理一致

🧱 四、現代方式:Libgpiod 新接口

為了替代 sysfs,Linux 引入了更現代的接口:

/dev/gpiochipX

這是一種基於 字符設備 的設計,每個 GPIO 控制器會被映射為一個設備節點。
同時,官方提供了用户空間庫和命令行工具:libgpiod


🔹 命令行示例

安裝(部分發行版):

apt install gpiod

使用命令行工具操作:

# 查看 GPIO 控制器信息
gpiodetect

# 查看 gpiochip0 的所有引腳
gpioinfo gpiochip0

# 設置編號為193的GPIO為高電平
gpioset gpiochip0 193=1

# 讀取編號為193的GPIO電平
gpioget gpiochip0 193

還可以設置多個引腳:

gpioset gpiochip0 193=1 194=0 195=1

🔹 C語言調用示例

#include <gpiod.h>
#include <stdio.h>

int main(void) {
    struct gpiod_chip *chip;
    struct gpiod_line *line;

    // 打開 gpiochip0
    chip = gpiod_chip_open_by_name("gpiochip0");

    // 獲取編號為193的引腳
    line = gpiod_chip_get_line(chip, 193);

    // 設置為輸出並拉高
    gpiod_line_request_output(line, "myapp", 1);

    // 延時1秒後拉低
    sleep(1);
    gpiod_line_set_value(line, 0);

    // 釋放資源
    gpiod_line_release(line);
    gpiod_chip_close(chip);
}

⚔️ 五、Sysfs vs Libgpiod 對比

對比項

Sysfs GPIO

Libgpiod

接口位置

/sys/class/gpio

/dev/gpiochipX

狀態

已廢棄 (deprecated)

官方推薦

訪問方式

文件讀寫

函數/命令行工具

併發控制

不支持

支持

性能

較低

更高

適用場景

調試、老項目

新項目、正式產品

事件監聽

不支持

支持(可響應中斷)


🚀 六、總結:新手該怎麼選?

目的

推薦方式

想快速點亮一個燈

sysfs(操作簡單、幾條命令就夠)

在正式項目中控制 GPIO

libgpiod(性能更好、未來趨勢)

寫 C 代碼或腳本程序

libgpiod

學習內核 GPIO 原理

從 sysfs 入門更容易理解


  • sysfs 是老方法,簡單但過時;
  • libgpiod 是新標準,更強大、可維護、未來主流;
  • GPIO 編號的計算遵循:
GPIO編號 = 組號 × 32 + 組內偏移

例如 PG1 → 6×32 + 1 = 193


🧭 延伸閲讀

  • Linux Kernel Documentation – GPIO Sysfs Interface (deprecated)
  • Libgpiod GitHub 項目