Python GPIO 开发

python-periphery 简介

python-periphery 是一个用于在 Linux 系统上通过 Python 进行硬件外设(GPIO、SPI、I2C 等)开发的库。

主要特点

  • 基于 Linux 内核接口:使用标准的 Linux 字符设备接口(如 /dev/gpiochip0
  • 跨平台兼容性:适用于各种 Linux 单板计算机(Raspberry Pi、BeagleBone、Radxa 等)
  • 纯 Python 实现:不需要编译 C 扩展,易于安装和部署
  • 支持多种外设:GPIO、SPI、I2C、MMIO、Serial、PWM 等

支持的外设类型

外设类型 说明
GPIO 通用输入输出
SPI 串行外设接口
I2C I²C 总线通信
MMIO 内存映射 I/O
Serial 串口通信
PWM 脉宽调制

安装 python-periphery

安装 pip3

首先确保系统已安装 pip3:

sudo apt update
sudo apt install python3-pip -y

安装 python-periphery 库

由于 Python 3.13+ 的 PEP 668 保护机制,需要使用 --break-system-packages 参数:

pip3 install python-periphery --break-system-packages

验证安装:

python3 -c "from periphery import GPIO; print('Installation successful!')"

提示:对于嵌入式开发板,使用 --break-system-packages 参数是安全的。如果您更倾向于使用虚拟环境,可以参考 Python 虚拟环境 文档。

GPIO 读取

重要提示:Pin3 和 Pin5 默认配置为 I2C9 接口(SDA 和 SCL),如果需要作为 GPIO 使用,请确保没有其他设备占用这些引脚。

硬件准备

  • 主板
  • 杜邦线

软件准备

测试代码

以下代码使用 python-periphery 库来读取 Pin3 (GPIO36) 引脚的高低电平。

创建文件 gpio_input.py

from periphery import GPIO
import time

def read_gpio_input():
    # Configure GPIO input (modify pin number according to actual hardware)
    # Pin3 corresponds to GPIO36 (pin 36 of /dev/gpiochip4)
    try:
        # Initialize GPIO in input mode
        gpio_in = GPIO("/dev/gpiochip4", 36, "in")

        print("Starting GPIO input reading (press Ctrl+C to exit)")
        while True:
            # Read pin value
            value = gpio_in.read()
            print(f"GPIO input value: {value} (True=High, False=Low)")
            time.sleep(1)  # Read once per second

    except KeyboardInterrupt:
        print("\nProgram exited")
    except Exception as e:
        print(f"Error occurred: {e}")
    finally:
        # Ensure resources are released
        try:
            gpio_in.close()
        except:
            pass

if __name__ == "__main__":
    read_gpio_input()

测试步骤

  1. 将 Pin3 (GPIO36) 引脚接 GND 或 3.3V 引脚
  2. 将代码保存为 gpio_input.py
  3. 使用以下命令运行测试代码:
python3 gpio_input.py

实验现象

终端会输出 FalseTrue 信息:

  • False 代表低电平(引脚接 GND)
  • True 代表高电平(引脚接 3.3V)

示例输出:

Starting GPIO input reading (press Ctrl+C to exit)
GPIO input value: False (True=High, False=Low)
GPIO input value: False (True=High, False=Low)
GPIO input value: True (True=High, False=Low)

GPIO 输出

硬件准备

  • 主板
  • 杜邦线

软件准备

测试代码

以下代码使用 python-periphery 库来控制 Pin3 (GPIO36) 引脚输出高低电平,然后通过 Pin5 (GPIO37) 引脚读取 Pin3 引脚的高低电平。

创建文件 gpio_output.py

from periphery import GPIO
import time

def gpio_output_with_feedback():
    # GPIO Configuration (modify pin numbers based on your hardware)
    # Pin3/GPIO36 (output) → maps to pin 36 of /dev/gpiochip4
    # Pin5/GPIO37 (input)  → maps to pin 37 of /dev/gpiochip4
    OUTPUT_PIN_CHIP = "/dev/gpiochip4"
    OUTPUT_PIN_NUMBER = 36  # Pin3/GPIO36 (output pin, controlled by the script)
    INPUT_PIN_NUMBER = 37   # Pin5/GPIO37 (input pin, reads output state)

    # Initialize GPIO objects as None first (for safe release later)
    gpio_out = None
    gpio_in = None

    try:
        # Initialize Pin3/GPIO36 as OUTPUT mode
        gpio_out = GPIO(OUTPUT_PIN_CHIP, OUTPUT_PIN_NUMBER, "out")
        # Initialize Pin5/GPIO37 as INPUT mode
        gpio_in = GPIO(OUTPUT_PIN_CHIP, INPUT_PIN_NUMBER, "in")

        # Print test initialization info
        print("=== GPIO Output-Input Feedback Test Started ===")
        print(f"Controlled Pin (GPIO36): {OUTPUT_PIN_CHIP} - Pin {OUTPUT_PIN_NUMBER} (OUTPUT)")
        print(f"Monitoring Pin (GPIO37): {OUTPUT_PIN_CHIP} - Pin {INPUT_PIN_NUMBER} (INPUT)")
        print("Test Behavior: GPIO36 toggles HIGH/LOW every 1s; GPIO37 verifies GPIO36's state")
        print("Press Ctrl+C to stop the test\n")

        # Main loop: Toggle GPIO36 and read GPIO37 feedback
        while True:
            # 1. Set GPIO36 to HIGH level
            gpio_out.write(True)
            time.sleep(0.1)  # Short delay for signal stabilization (avoid read lag)
            gpio37_reading = gpio_in.read()
            print(f"GPIO36 Output: HIGH (True) | GPIO37 Reading: {gpio37_reading}")

            # Keep GPIO36 HIGH for 1 second
            time.sleep(1)

            # 2. Set GPIO36 to LOW level
            gpio_out.write(False)
            time.sleep(0.1)  # Short delay for signal stabilization
            gpio37_reading = gpio_in.read()
            print(f"GPIO36 Output: LOW (False) | GPIO37 Reading: {gpio37_reading}")

            # Keep GPIO36 LOW for 1 second
            time.sleep(1)

    # Handle user-initiated exit (Ctrl+C)
    except KeyboardInterrupt:
        print("\n\nTest stopped by user (Ctrl+C)")
    # Handle other unexpected errors (e.g., GPIO access failure)
    except Exception as e:
        print(f"\nError during test: {str(e)}")
    # Ensure GPIO resources are released even if an error occurs
    finally:
        print("\nReleasing GPIO resources...")
        # Safely close GPIO36 (set to LOW first to avoid residual high level)
        if gpio_out:
            try:
                gpio_out.write(False)
                gpio_out.close()
                print(f"Successfully closed GPIO36 (Pin {OUTPUT_PIN_NUMBER})")
            except Exception as close_err:
                print(f"Failed to close GPIO36 (Pin {OUTPUT_PIN_NUMBER}): {str(close_err)}")
        # Safely close GPIO37
        if gpio_in:
            try:
                gpio_in.close()
                print(f"Successfully closed GPIO37 (Pin {INPUT_PIN_NUMBER})")
            except Exception as close_err:
                print(f"Failed to close GPIO37 (Pin {INPUT_PIN_NUMBER}): {str(close_err)}")
        print("Resource release complete.")

# Run the test when the script is executed directly
if __name__ == "__main__":
    gpio_output_with_feedback()

测试步骤

  1. 将 Pin3 (GPIO36) 引脚和 Pin5 (GPIO37) 引脚进行短接
  2. 将代码保存为 gpio_output.py
  3. 使用以下命令运行测试代码:
sudo python3 gpio_output.py

注意:GPIO 输出可能需要 root 权限,因此使用 sudo 运行。

实验现象

终端会输出 FalseTrue 信息:

  • False 代表低电平
  • True 代表高电平

示例输出:

=== GPIO Output-Input Feedback Test Started ===
Controlled Pin (GPIO36): /dev/gpiochip4 - Pin 36 (OUTPUT)
Monitoring Pin (GPIO37): /dev/gpiochip4 - Pin 37 (INPUT)
Test Behavior: GPIO36 toggles HIGH/LOW every 1s; GPIO37 verifies GPIO36's state
Press Ctrl+C to stop the test

GPIO36 Output: HIGH (True) | GPIO37 Reading: True
GPIO36 Output: LOW (False) | GPIO37 Reading: False
GPIO36 Output: HIGH (True) | GPIO37 Reading: True
GPIO36 Output: LOW (False) | GPIO37 Reading: False

GPIO 引脚映射说明

在 python-periphery 中,GPIO 引脚需要指定两个参数:

  1. GPIO 芯片设备:Quectel Pi H1 主要使用 /dev/gpiochip4
  2. 引脚编号:物理引脚对应的 GPIO 编号

40Pin 引脚映射示例

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

查看可用 GPIO

使用 gpioinfo 命令查看所有可用的 GPIO 芯片和引脚:

# 安装 gpiod 工具
sudo apt install gpiod -y

# 查看所有 GPIO 信息
gpioinfo

# 查看特定芯片
gpioinfo /dev/gpiochip4

常见问题

权限问题

如果遇到权限错误,可以:

  1. 使用 sudo 运行脚本
  2. 将用户添加到 gpio 用户组:
sudo usermod -a -G gpio $USER
  1. 注销并重新登录使权限生效

引脚占用问题

如果遇到 [Errno 16] Device or resource busy[Errno 1] Operation not permitted 错误:

检查引脚状态:

# 查看所有 GPIO 引脚状态
gpioinfo | grep "consumer="

# 查看特定芯片的引脚占用情况
gpioinfo /dev/gpiochip4 | grep -E "(line|consumer)"

常见原因:

  • 引脚被其他进程占用(如 I2C、SPI 等)
  • 引脚已被系统功能使用
  • 其他 GPIO 程序正在运行

解决方法:

  • 选择未被占用的引脚(gpioinfo 中显示为 input 且无 consumer 的引脚)
  • 如果需要使用被占用的引脚,需要禁用相应的功能

引脚编号错误

如果遇到 [Errno 22] Invalid argument 错误:

原因:

  • 引脚编号不存在或不可用
  • 使用了错误的 GPIO 芯片设备

解决方法:

  1. 使用 gpioinfo 查看实际可用的引脚编号
  2. 确认使用正确的 GPIO 芯片(Quectel Pi H1 主要使用 /dev/gpiochip4

调试技巧

查看可用的 GPIO 芯片:

ls -l /dev/gpiochip*

查看 GPIO 芯片详细信息:

gpioinfo

测试特定引脚:

# 使用 gpioget 读取引脚值
sudo gpioget /dev/gpiochip4 36

# 使用 gpioset 设置引脚值
sudo gpioset /dev/gpiochip4 36=1  # 设置为高电平
sudo gpioset /dev/gpiochip4 36=0  # 设置为低电平

参考资源