#!/usr/bin/env python3

################################################################################
# Copyright (c) 2024,D-Robotics.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

import sys
import signal
import Hobot.GPIO as GPIO
import time

def signal_handler(signal, frame):
    sys.exit(0)

# 定义使用的GPIO通道:
# 31号作为输出,可以点亮一个LED
# 37号作为输入,可以接一个按钮
led_pin = 31 # BOARD 编码 31
but_pin = 37 # BOARD 编码 37

# 禁用警告信息
GPIO.setwarnings(False)

def main():
    # 设置管脚编码模式为硬件编号 BOARD
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(led_pin, GPIO.OUT)  # LED pin set as output
    GPIO.setup(but_pin, GPIO.IN)  # button pin set as input

    # Initial state for LEDs:
    GPIO.output(led_pin, GPIO.LOW)

    print("Starting demo now! Press CTRL+C to exit")
    try:
        while True:
            print("Waiting for button event")
            GPIO.wait_for_edge(but_pin, GPIO.FALLING)

            # event received when button pressed
            print("Button Pressed!")
            GPIO.output(led_pin, GPIO.HIGH)
            time.sleep(1)
            GPIO.output(led_pin, GPIO.LOW)
    finally:
        GPIO.cleanup()  # cleanup all GPIOs

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    main()

一、代码整体概述

这是地平线旭日系列开发板官方 GPIO 示例程序,实现下降沿中断检测按键 + LED 输出控制,具备Ctrl+C 优雅退出、GPIO 资源自动释放能力。

  • 运行平台:D-Robotics RDK X3 / X5
  • 依赖库:Hobot.GPIO(地平线自研用户态 GPIO 库,替代树莓派 RPi.GPIO)
  • 硬件接线:
    1. BOARD 引脚 31 → LED 正极(串限流电阻),GND 接 LED 负极
    2. BOARD 引脚 37 → 按键一端,按键另一端接 GND(默认上拉,按下电平拉低)
  • 核心逻辑:循环阻塞等待按键按下,按键触发后 LED 点亮 1 秒再熄灭。

二、逐段分层拆解

1. 文件头部声明

python

运行

#!/usr/bin/env python3
# Apache 2.0开源协议声明
  • #!/usr/bin/env python3:脚本解释器声明,终端可直接 python3 xxx.py./xxx.py 运行。
  • Apache2.0 协议:允许商用、修改、分发,保留版权声明即可。

2. 导入依赖模块

python

运行

import sys
import signal
import Hobot.GPIO as GPIO
import time
  1. sys:系统工具,用于程序退出 sys.exit(0)
  2. signal:Linux 信号捕获,处理键盘中断 Ctrl+C
  3. Hobot.GPIO as GPIO:地平线 GPIO 操作核心库,API 语法兼容 RPi.GPIO,适配旭日 SOC 硬件
  4. time:延时函数 time.sleep()

3. Ctrl+C 信号处理函数 signal_handler

python

运行

def signal_handler(signal, frame):
    sys.exit(0)
  • 作用:捕获 SIGINT(Ctrl+C 键盘中断信号),直接正常退出程序;
  • 若不注册该函数,按下 Ctrl+C 会直接抛出KeyboardInterrupt异常,跳过 GPIO 资源清理,导致引脚占用、后续程序报错。

4. 全局引脚定义

python

运行

led_pin = 31 # BOARD物理板载引脚编号31,输出
but_pin = 37 # BOARD物理板载引脚编号37,输入按键
GPIO.setwarnings(False) # 关闭GPIO重复初始化警告
  • GPIO.BOARD:按开发板丝印物理引脚编号操作(推荐新手,不用记忆芯片 BCM 内核编号);
  • setwarnings(False):屏蔽 “引脚已被占用、重复 setup” 的冗余警告日志。

5. 主业务函数 main ()

(1)GPIO 初始化配置

python

运行

GPIO.setmode(GPIO.BOARD)
GPIO.setup(led_pin, GPIO.OUT)  # 31脚设为输出模式
GPIO.setup(but_pin, GPIO.IN)    # 37脚设为输入模式
GPIO.output(led_pin, GPIO.LOW)# LED初始低电平,熄灭
  1. setmode(GPIO.BOARD):指定引脚编码规则,全局统一;
  2. setup(pin, mode):配置引脚方向
    • GPIO.OUT:输出,高低电平控制外设(LED、继电器)
    • GPIO.IN:输入,读取外部电平(按键、传感器)
  3. output(pin, level):设置输出引脚初始电平,LOW=0熄灭 LED。
(2)主循环:阻塞式等待按键中断

python

运行

try:
    while True:
        print("Waiting for button event")
        # 阻塞等待引脚下降沿(高→低)触发
        GPIO.wait_for_edge(but_pin, GPIO.FALLING)

        print("Button Pressed!")
        GPIO.output(led_pin, GPIO.HIGH) # 点亮LED
        time.sleep(1)
        GPIO.output(led_pin, GPIO.LOW)  # 1秒后熄灭
核心 API:GPIO.wait_for_edge(pin, edge_type)
  1. 阻塞特性:程序停在此处,不占用 CPU 轮询,内核硬件中断等待,低功耗;
  2. GPIO.FALLING 下降沿触发:
    • 按键默认上拉:空闲 = 高电平;按键按下接地 = 低电平;
    • 电平从高变低瞬间触发中断,进入后续 LED 逻辑;
  3. 执行流程:按键按下 → 打印提示 → LED 高电平亮 1s → 熄灭,回到循环继续等待下一次按键。也就是按键的迟滞响应时间为2s。
(3)finally 资源释放(必执行)

python

运行

finally:
    GPIO.cleanup()  # 释放所有配置过的GPIO引脚
  • 无论循环正常结束、还是 Ctrl+C 中断、代码异常,finally一定会执行
  • GPIO.cleanup():重置所有 GPIO 为默认浮空输入,释放内核设备占用,避免下次运行提示引脚被占用。

6. 程序入口与信号注册

python

运行

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    main()
  1. if __name__ == '__main__':标准 Python 程序入口,仅直接运行脚本时执行;
  2. signal.signal(signal.SIGINT, signal_handler):注册中断回调,按下 Ctrl+C 触发自定义处理函数,保证走finally清理 GPIO 资源。

三、核心 API 详解(Hobot.GPIO)

  1. GPIO.setmode(GPIO.BOARD) 物理丝印编号,RDK 开发板丝印数字直接对应,入门首选;另一种GPIO.BCM为芯片内核 GPIO 编号。
  2. GPIO.setup(pin, mode) 配置输入 / 输出,输入可额外带上拉下拉:GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  3. GPIO.output(pin, GPIO.HIGH/GPIO.LOW) 输出高低电平控制外设。
  4. GPIO.wait_for_edge(pin, edge) 硬件中断阻塞等待,三种触发模式:
    • GPIO.FALLING 下降沿(按键按下推荐)
    • GPIO.RISING 上升沿(按键松开)
    • GPIO.BOTH 双边沿(按下 + 松开都触发)
  5. GPIO.cleanup() 资源回收,嵌入式 GPIO 程序强制规范,防止硬件资源泄漏。

四、硬件接线逻辑说明

按键引脚 37 配置为输入,内部默认上拉电阻

  1. 按键松开:引脚 37 = 高电平;
  2. 按键按下:引脚 37 接地,电平变低,产生下降沿,wait_for_edge捕获事件;
  3. LED 引脚 31 输出高电平时导通点亮,低电平熄灭。

五、程序运行完整时序

  1. 脚本启动 → 注册 Ctrl+C 信号处理;
  2. 初始化 GPIO,LED 默认熄灭;
  3. 进入 while 循环,阻塞等待按键;
  4. 按下按键 → 捕获下降沿 → LED 亮 1 秒后熄灭;
  5. 重复等待按键;
  6. 按下 Ctrl+C → 触发 signal_handler,执行 sys.exit;
  7. 程序退出前进入 finally,执行GPIO.cleanup()释放所有 IO。

六、关键优势与工程规范点

  1. 硬件中断非轮询:wait_for_edge阻塞等待,CPU 不循环扫描引脚,资源占用极低;
  2. 安全退出机制:信号捕获 + finally 双重保障,GPIO 一定会释放;
  3. 分层清晰:硬件引脚定义、初始化、业务逻辑、资源回收完全分离;
  4. 适配地平线全系列 RDK 平台,Hobot.GPIO 库开箱即用,无需额外驱动编译。

七、常见踩坑补充

  1. 按键抖动:机械按键按下会产生电平毛刺,短时间多次触发;可在wait_for_edge后加time.sleep(0.02)软件消抖;
  2. 引脚编号混淆:BOARDBCM模式不可混用,否则引脚控制错乱;
  3. 忘记 cleanup:程序异常退出后 GPIO 保持输出电平,LED 常亮、引脚被锁死;
  4. 权限问题:部分 RDK 系统操作 GPIO 需要 root 权限,运行加sudo python3 xxx.py

消抖优化补充代码片段

python

运行

GPIO.wait_for_edge(but_pin, GPIO.FALLING)
time.sleep(0.02) # 20ms消抖延时
# 二次读取电平确认真实按下,过滤抖动
if GPIO.input(but_pin) == 0:
    print("Button Pressed!")
    GPIO.output(led_pin, GPIO.HIGH)
    time.sleep(1)
    GPIO.output(led_pin, GPIO.LOW)
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐