#!/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通道:
# 15号作为输出,可以点亮一个LED
# 16号作为输出,可以点亮一个LED
# 37号作为输入,可以接一个按钮
led_pin_1 = 15 # BOARD 编码 15
led_pin_2 = 16 # BOARD 编码 16
but_pin = 37   # BOARD 编码 37


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

# 按下按钮时 LED 2 快速闪烁 5 次
def blink(channel):
    print("Blink LED 2")
    for i in range(5):
        GPIO.output(led_pin_2, GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(led_pin_2, GPIO.LOW)
        time.sleep(0.5)

def main():
    # Pin Setup:
    GPIO.setmode(GPIO.BOARD)  # BOARD pin-numbering scheme
    GPIO.setup([led_pin_1, led_pin_2], GPIO.OUT)  # LED pins set as output
    GPIO.setup(but_pin, GPIO.IN)  # button pin set as input

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

    # 把blink函数注册为按钮下降沿事件的中断处理函数
    GPIO.add_event_detect(but_pin, GPIO.FALLING, callback=blink, bouncetime=10)
    # 开始测试, Led1 缓慢闪烁
    print("Starting demo now! Press CTRL+C to exit")
    try:
        while True:
            # blink LED 1 slowly
            GPIO.output(led_pin_1, GPIO.HIGH)
            time.sleep(2)
            GPIO.output(led_pin_1, GPIO.LOW)
            time.sleep(2)
    finally:
        GPIO.cleanup()  # cleanup all GPIOs

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

地平线 RDK X3/X5 Hobot.GPIO 中断回调示例完整代码解析

一、整体功能概述

该代码演示GPIO 异步硬件中断,实现两套 LED 独立逻辑并行运行

  1. 主线程循环:BOARD15 引脚 LED1,每 2 秒自动闪烁,不阻塞;
  2. 硬件中断回调:BOARD37 按键下降沿触发,自动执行blink函数,BOARD16 引脚 LED2 快速闪烁 5 次;
  3. 自带软件消抖、Ctrl+C 优雅退出、GPIO 资源自动释放,基于地平线自研Hobot.GPIO库,API 兼容 RPi.GPIO。

硬件接线说明

  • LED1:物理引脚 15 → LED 正极串限流电阻,GND 接负极
  • LED2:物理引脚 16 → LED 正极串限流电阻,GND 接负极
  • 按键:物理引脚 37 一端接 GND,松开引脚内部上拉为高,按下拉低产生下降沿

二、代码逐段分层拆解

1. 文件头与依赖导入

python

运行

#!/usr/bin/env python3
# Apache2.0开源协议声明
import sys
import signal
import Hobot.GPIO as GPIO
import time
  • #!/usr/bin/env python3:脚本可直接./xxx.py运行;
  • signal:捕获Ctrl+C中断信号;
  • Hobot.GPIO:地平线 RDK 专用 GPIO 库;
  • time:LED 闪烁延时。

2. 信号捕获回调函数 signal_handler

python

运行

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

捕获 Linux SIGINT信号(键盘 Ctrl+C),主动退出程序,保证进入finally执行 GPIO 清理,避免引脚占用。

3. 全局引脚定义

python

运行

led_pin_1 = 15 # 自动闪烁LED
led_pin_2 = 16 # 按键触发闪烁LED
but_pin = 37   # 按键输入引脚
GPIO.setwarnings(False) # 屏蔽GPIO重复初始化警告

统一使用BOARD 物理丝印编号,直观对应开发板引脚。

4. 中断回调处理函数 blink (channel)

python

运行

def blink(channel):
    print("Blink LED 2")
    for i in range(5):
        GPIO.output(led_pin_2, GPIO.HIGH)
        time.sleep(0.5)
        GPIO.output(led_pin_2, GPIO.LOW)
        time.sleep(0.5)
  • 参数channel:触发中断的引脚号(固定传入 37);
  • 功能:按键按下后 LED2 循环亮灭 5 次,单次间隔 0.5s;
  • 底层机制:中断触发时新开子线程执行该函数,不会阻塞主线程 LED1 的自动闪烁

5. 主函数 main ()

(1)GPIO 模式与引脚方向初始化

python

运行

GPIO.setmode(GPIO.BOARD)
# 批量配置两个LED引脚为输出
GPIO.setup([led_pin_1, led_pin_2], GPIO.OUT)
# 按键引脚设为输入(默认内部上拉)
GPIO.setup(but_pin, GPIO.IN)
# LED初始熄灭
GPIO.output(led_pin_1, GPIO.LOW)
GPIO.output(led_pin_2, GPIO.LOW)
  • GPIO.setup([pin1,pin2], GPIO.OUT):支持批量配置多引脚,简化代码;
  • 按键引脚默认内部上拉:松开 = 高电平,按下 = 低电平。
(2)核心:注册 GPIO 硬件中断事件

python

运行

GPIO.add_event_detect(but_pin, GPIO.FALLING, callback=blink, bouncetime=10)
参数完整释义:
  1. but_pin:绑定中断的输入引脚;
  2. GPIO.FALLING:触发边沿为下降沿(高→低,按键按下瞬间); 可选:RISING上升沿(松开)、BOTH双边沿;
  3. callback=blink:中断触发后自动执行的回调函数;
  4. bouncetime=10软件消抖时间,单位 ms 机械按键按下会产生 5~10ms 电平毛刺抖动,该参数屏蔽 10ms 内多次电平变化,避免一次按键多次触发中断。

关键特性:add_event_detect注册后,硬件中断后台持续监听,主线程可自由执行其他逻辑,无需轮询引脚,CPU 占用极低。

(3)主线程无限循环(独立自动闪烁 LED1)

python

运行

try:
    print("Starting demo now! Press CTRL+C to exit")
    while True:
        GPIO.output(led_pin_1, GPIO.HIGH)
        time.sleep(2)
        GPIO.output(led_pin_1, GPIO.LOW)
        time.sleep(2)

主线程独立循环,每 2 秒切换 LED1 电平;按键中断不会打断该循环,两个 LED 逻辑完全并行。

(4)finally 资源强制清理(嵌入式强制规范)

python

运行

finally:
    GPIO.cleanup()

无论程序正常结束、Ctrl+C 中断、代码异常崩溃,finally块一定会执行; GPIO.cleanup()重置所有配置引脚为浮空输入,释放内核设备,防止下次运行提示引脚被占用。

6. 程序入口与信号注册

python

运行

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    main()
  • signal.signal():提前注册 Ctrl+C 中断处理函数;
  • if __name__ == '__main__':标准 Python 入口,仅直接运行脚本时执行逻辑。

三、核心 API 深度解读(Hobot.GPIO)

  1. GPIO.add_event_detect () 异步中断(本代码核心)
    • 底层依赖 SOC 硬件中断控制器,非软件轮询;
    • 回调函数运行在独立子线程,和主线程互不阻塞;
    • bouncetime内置消抖,无需手动写延时消抖代码。
  2. GPIO.setup(pin_list, mode) 支持列表批量配置多路 IO,简化多外设初始化。
  3. GPIO.cleanup() 嵌入式 GPIO 程序必须调用,防止硬件资源泄漏、引脚锁定。
  4. GPIO.setmode(GPIO.BOARD) 物理引脚编号,新手友好,区别于芯片内核 BCM 编号。

四、程序完整运行时序逻辑

  1. 脚本启动 → 注册 Ctrl+C 信号处理;
  2. 初始化两路 LED 输出、按键输入,全部 LED 熄灭;
  3. 为 37 号按键注册下降沿硬件中断,开启 10ms 消抖;
  4. 主线程进入循环:LED1 亮 2s、灭 2s 持续自动闪烁;
  5. 并行监听按键中断:
    • 按下按键 → 引脚电平由高变低,触发下降沿中断;
    • 新开子线程执行blink,LED2 快速闪烁 5 次;
    • 主线程 LED1 闪烁不受任何影响,同步运行;
  6. 按下 Ctrl+C 触发 SIGINT 信号,执行sys.exit()
  7. 进入 finally 块,执行GPIO.cleanup()释放所有 GPIO,程序安全退出。

五、两大 GPIO 按键检测方案对比(本代码优势)

表格

方案 实现方式 CPU 占用 阻塞主线程 适用场景
轮询 wait_for_edge(上一版代码) 主线程阻塞等待引脚电平 是,主线程卡死无法执行其他逻辑 单任务简单 demo
add_event_detect 异步中断(本代码) 硬件中断 + 子线程回调 极低 否,主线程独立运行多业务 多外设并行、工业多 IO 控制

六、关键工程优势与踩坑点

优势

  1. 多任务并行无阻塞:自动 LED 闪烁 + 按键响应同时运行;
  2. 内置硬件中断 + 软件消抖,无需额外电容硬件消抖;
  3. 双重安全退出机制:signal 捕获 + finally 清理,杜绝 GPIO 占用;
  4. 批量引脚初始化,代码简洁易扩展多路 LED / 继电器。

常见踩坑

  1. 中断回调函数禁止长时间阻塞若 blink 内延时过长,连续快速按键会造成回调堆积;
  2. bouncetime单位为 ms,机械按键推荐 10~20ms,过小会抖动误触发;
  3. BOARD 与 BCM 编号不可混用,否则引脚控制错乱;
  4. 程序异常退出未执行 cleanup,下次运行 GPIO 报错,必须断电或手动 cleanup;
  5. 运行需要权限,报错权限不足时加sudo python3 xxx.py

七、扩展优化建议

  1. 防按键重复触发:在 blink 开头加全局标志位,闪烁期间屏蔽中断;
  2. 支持长按 / 双击:在回调内记录时间戳,区分短按、长按;
  3. 多按键扩展:调用多次add_event_detect绑定不同回调函数。

Logo

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

更多推荐