[具身智能-569]:地平线 RDK X3/X5 Hobot.GPIO 异步中断回调示例完整代码解析
·
#!/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 独立逻辑并行运行:
- 主线程循环:BOARD15 引脚 LED1,每 2 秒自动闪烁,不阻塞;
- 硬件中断回调:BOARD37 按键下降沿触发,自动执行
blink函数,BOARD16 引脚 LED2 快速闪烁 5 次; - 自带软件消抖、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)
参数完整释义:
but_pin:绑定中断的输入引脚;GPIO.FALLING:触发边沿为下降沿(高→低,按键按下瞬间); 可选:RISING上升沿(松开)、BOTH双边沿;callback=blink:中断触发后自动执行的回调函数;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)
- GPIO.add_event_detect () 异步中断(本代码核心)
- 底层依赖 SOC 硬件中断控制器,非软件轮询;
- 回调函数运行在独立子线程,和主线程互不阻塞;
bouncetime内置消抖,无需手动写延时消抖代码。
GPIO.setup(pin_list, mode)支持列表批量配置多路 IO,简化多外设初始化。GPIO.cleanup()嵌入式 GPIO 程序必须调用,防止硬件资源泄漏、引脚锁定。GPIO.setmode(GPIO.BOARD)物理引脚编号,新手友好,区别于芯片内核 BCM 编号。
四、程序完整运行时序逻辑
- 脚本启动 → 注册 Ctrl+C 信号处理;
- 初始化两路 LED 输出、按键输入,全部 LED 熄灭;
- 为 37 号按键注册下降沿硬件中断,开启 10ms 消抖;
- 主线程进入循环:LED1 亮 2s、灭 2s 持续自动闪烁;
- 并行监听按键中断:
- 按下按键 → 引脚电平由高变低,触发下降沿中断;
- 新开子线程执行
blink,LED2 快速闪烁 5 次; - 主线程 LED1 闪烁不受任何影响,同步运行;
- 按下 Ctrl+C 触发 SIGINT 信号,执行
sys.exit(); - 进入 finally 块,执行
GPIO.cleanup()释放所有 GPIO,程序安全退出。
五、两大 GPIO 按键检测方案对比(本代码优势)
表格
| 方案 | 实现方式 | CPU 占用 | 阻塞主线程 | 适用场景 |
|---|---|---|---|---|
| 轮询 wait_for_edge(上一版代码) | 主线程阻塞等待引脚电平 | 低 | 是,主线程卡死无法执行其他逻辑 | 单任务简单 demo |
| add_event_detect 异步中断(本代码) | 硬件中断 + 子线程回调 | 极低 | 否,主线程独立运行多业务 | 多外设并行、工业多 IO 控制 |
六、关键工程优势与踩坑点
优势
- 多任务并行无阻塞:自动 LED 闪烁 + 按键响应同时运行;
- 内置硬件中断 + 软件消抖,无需额外电容硬件消抖;
- 双重安全退出机制:signal 捕获 + finally 清理,杜绝 GPIO 占用;
- 批量引脚初始化,代码简洁易扩展多路 LED / 继电器。
常见踩坑
- 中断回调函数禁止长时间阻塞:若 blink 内延时过长,连续快速按键会造成回调堆积;
bouncetime单位为 ms,机械按键推荐 10~20ms,过小会抖动误触发;- BOARD 与 BCM 编号不可混用,否则引脚控制错乱;
- 程序异常退出未执行 cleanup,下次运行 GPIO 报错,必须断电或手动 cleanup;
- 运行需要权限,报错权限不足时加
sudo python3 xxx.py。
七、扩展优化建议
- 防按键重复触发:在 blink 开头加全局标志位,闪烁期间屏蔽中断;
- 支持长按 / 双击:在回调内记录时间戳,区分短按、长按;
- 多按键扩展:调用多次
add_event_detect绑定不同回调函数。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)