简介这份资源是面向 Jetson Nano 开发者的 C/C GPIO 编程资料包聚焦嵌入式 Linux 下通过 GPIO 库控制传感器、LED、按钮等外设的完整流程适合有一定 C 语法基础、想从零上手 Jetson Nano 硬件编程的爱好者与工程师。压缩包共 68 个文件以 cpp/h 源码为主体29 个 cpp、17 个 h包含可编译的库实现、PWM/中断/事件等示例程序同时附带 md 文档说明安装步骤、库 API 与项目链接方法并有 CMake 配置、Dockerfile 和 udev 规则文件便于在不同环境中构建部署。整个包仅 106KB精简无冗余。资源已有 852 人学习。通过学习可掌握 nvgpio_open、gpio_request、读写电平、事件回调等核心 API参考 simple_out、button_interrupt、simple_pwm 等用例能直观理解引脚配置、输入输出模式切换及线程同步思路有效缩短 Jetson Nano 外设项目开发的上手周期。 直接说结论Jetson Nano的GPIO库官方叫Jetson.GPIO和树莓派的RPi.GPIO在API上高度相似但底层实现、引脚映射、权限管理完全是另一套逻辑。这篇文章我把从烧录系统到点亮第一颗LED、再到踩坑排查的完整过程写清楚面向第一次接触Jetson Nano的嵌入式开发者也适合从树莓派转过来的朋友——你以为你会的其实不一定是你以为的那样。1. 为什么Jetson Nano的GPIO库值得单独写一篇1.1 它和树莓派RPi.GPIO是“表亲”但别当亲兄弟用最早拿到Jetson Nano的人大概率都有树莓派的使用背景。NVIDIA官方也的确是这么设计的——Jetson.GPIO的接口风格、命名方式、甚至示例代码都刻意往RPi.GPIO上靠目的是降低迁移门槛。但实际用起来你会发现有几个本质区别引脚编号方式完全不同。树莓派有 BOARD物理引脚号和 BCM博通芯片GPIO号两套编号Jetson Nano 虽然有 BOARD 和 BCM 两种模式但这里的“BCM”只是把Jetson的引脚重新映射了一遍跟树莓派的GPIO17、GPIO27那套编号没有半毛钱关系。硬件层面。树莓派的GPIO直连SoC的BCM2835而Jetson Nano的GPIO是通过板载的PCA9539 GPIO扩展芯片和Tegra SoC内部GPIO混合管理的这就导致部分引脚不支持中断、部分引脚有上拉/下拉的硬件限制。权限机制。Jetson.GPIO 底层依赖libgpiod和内核的gpiochip接口默认情况下需要 root 权限或者把用户加入gpio组、配置 udev 规则。新手最容易在这儿卡住。一句话总结API长得像但驱动链路完全不同。你要是直接照搬树莓派的代码大概率能跑但性能、可靠性、引脚功能选择上会有很多隐性坑。1.2 40针排针不是所有引脚都能随便用Jetson Nano 的 40-pin 扩展排针是 2x20 布局和树莓派物理上一致但引脚功能分配完全是英伟达自己的。拿到板子后第一件事应该是去NVIDIA官网下载Jetson Nano Pinmux 表格引脚复用配置表而不是凭树莓派经验猜功能。排针上有三类引脚你要心里有数供电脚3.3V、5V、GND共8个左右这部分和树莓派一致但5V引脚直接取自USB或DC电源没有稳压保护电流不大但足够烧坏传感器。数字GPIO大部分引脚默认是GPIO功能但部分引脚默认复用为I2C、UART、SPI、PWM、I2S等外设功能必须通过 Pinmux 配置切换。特殊功能脚比如PWM0、PWM2这类带硬件PWM的引脚数量比树莓派少而且默认映射的芯片通道也不同。我见过不少人在这一步翻车——拿GPIO9当普通输入用结果发现它默认是 I2C 的数据线怎么读都是高电平。这不是代码问题是引脚复用没配。2. 环境准备与Jetson.GPIO库的安装2.1 烧录系统时就要考虑GPIO库的适配性Jetson Nano 支持多个系统版本从 JetPack 4.2 到 4.6.4Nano 系列的尽头不同的 JetPack 版本对应的内核版本不同而 Jetson.GPIO 库对内核版本是有要求的。我的建议是新手直接用JetPack 4.6.4 Ubuntu 18.04这是Nano最后也是最稳定的官方镜像GCC、Python、pip 版本都不算太老能装最新版 Jetson.GPIO。不要为了尝鲜去刷 JetPack 5.x 或更高版本给 Nano那是给 Orin 系列用的驱动兼容性有风险。烧录用SDK Manager或balenaEtcher 官方镜像都行我习惯直接用 SDK Manager 刷一步到位还能顺手装 CUDA、cuDNN 这些加速库。烧录完成后先用ls /dev/gpiochip*确认内核有暴露GPIO设备节点。正常情况下应该能看到gpiochip0、gpiochip1等多个芯片其中gpiochip0对应 Tegra SoC 内部GPIOgpiochip1对应 PCA9539 扩展芯片。2.2 安装Jetson.GPIO的完整流程官方库在GitHub上名字叫NVIDIA/jetson-gpio。安装步骤我整理成下面这条链# 1. 更新系统并安装依赖 sudo apt update sudo apt install -y python3-pip git # 2. 克隆官方仓库 git clone https://github.com/NVIDIA/jetson-gpio.git # 3. 进入仓库目录直接安装Python库 cd jetson-gpio sudo python3 setup.py install # 4. 配置用户组和udev规则关键一步 sudo groupadd -f gpio sudo usermod -aG gpio $USER sudo cp lib/python/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/ sudo udevadm control --reload-rules sudo udevadm trigger # 5. 注销重新登录让用户组生效这里有个细节值得多说一句很多教程只让你pip install Jetson.GPIO但 PyPI 上的包版本可能滞后而且不会自动帮你装 udev 规则。从源码装能保证库版本和驱动匹配同时把权限文件一起搞定。建议按上面的流程走。装完后打开 Python 验证一下import Jetson.GPIO as GPIO print(GPIO.VERSION) print(GPIO.gpiochip_find)如果输出版本号且不报错说明库装好了。如果报ModuleNotFoundError检查是不是装到了 Python 2 的环境里Nano 自带 Python 2 和 Python 3pip 默认可能指向 python2。2.3 权限配置是90%新手的第一道坎JetPack 系统默认情况下普通用户访问/dev/gpiochip*是会被拒绝的。你会看到这样的报错PermissionError: [Errno 13] Permission denied: /dev/gpiochip0解决办法就是上面第4步的组和udev规则。99-gpio.rules 这个文件内容本质上就是给/dev/gpiochip*设备赋予gpio用户组的读写权限。配置完成后重新登录执行groups确认你已经在gpio组里。有个小坑SDK Manager 刷机时创建的用户名如果是nvidia那你sudo usermod -aG gpio $USER加的是当前登录用户如果用 SSH 远程登录后切换过用户$USER会变务必确认加对人了。3. 核心接口与实操编码3.1 基本流程和RPi.GPIO神似但细节不同先看一个标准程序骨架我加了些注释方便对照树莓派的使用习惯找差异import Jetson.GPIO as GPIO import time # 1. 设置引脚编号模式BOARD 或 BCM # 注意Jetson的BOARD编号 物理排针号BCM编号 英伟达自定义逻辑号 GPIO.setmode(GPIO.BOARD) # 2. 设置warnings默认开启建议关闭避免刷屏 GPIO.setwarnings(False) # 3. 设置引脚功能输出 # 物理排针第11脚对应BCM编号18 GPIO.setup(11, GPIO.OUT, initialGPIO.HIGH) # 4. 控制输出 GPIO.output(11, GPIO.LOW) time.sleep(1) GPIO.output(11, GPIO.HIGH) # 5. 清理 GPIO.cleanup()代码结构和树莓派基本一致但有几个细节GPIO.setmode(GPIO.BOARD)和GPIO.setmode(GPIO.BCM)都可以用但 BOARD 模式更直观——因为它直接对应排针上的物理位置查引脚容易不容易搞混。GPIO.setwarnings(False)在树莓派上可能只是避免“通道已被使用”的提示但在 Jetson 上经常会出现重复初始化 PWM 或其他外设的警告建议默认关掉。GPIO.cleanup()不能随意调用它会把所有GPIO通道重置为输入模式如果你在代码中间调用会导致程序后面的引脚状态全部失效。更合理的做法是在程序退出前调用。3.2 GPIO的八种工作模式到底是什么热搜词里出现过“GPIO八种工作模式”这个词在STM32圈子里最常见但在 Jetson/Linux 语境下得打个折扣理解。Linux 内核的gpiochip接口从功能上支持的模式主要分为模式对应英文说明浮空输入Input Floating默认输入模式无上下拉上拉输入Input Pull-up输入默认高电平下拉输入Input Pull-down输入默认低电平推挽输出Output Push-pull可输出高低电平开漏输出Output Open-drain只能拉低需要外接上拉电阻复用功能Alternate Function切换为I2C、SPI、UART等模拟输入Analog InputJetson不常用绝大多数引脚无ADC外部中断Interrupt Falling/Rising边沿触发事件Jetson.GPIO 库能直接操作的是前六种其中模拟输入是不支持的Jetson Nano 没有板载ADC需要外接ADS1115之类芯片。中断模式也不是所有引脚都支持具体看硬件版本PCA9539 扩展芯片的引脚对中断支持很差经常要在软件里轮询替代。3.3 实操案例按键输入与外部中断GPIO输出控制相对简单难的是输入和中断。下面这个例子展示按键检测分别用轮询和中断两种方式import Jetson.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) BUTTON_PIN 13 # 物理第13脚 LED_PIN 11 # 物理第11脚 GPIO.setup(LED_PIN, GPIO.OUT, initialGPIO.LOW) GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_downGPIO.PUD_UP) # 启用内部上拉 # 方式一轮询简单但浪费CPU try: while True: if GPIO.input(BUTTON_PIN) GPIO.LOW: # 按下为低 GPIO.output(LED_PIN, GPIO.HIGH) else: GPIO.output(LED_PIN, GPIO.LOW) time.sleep(0.05) # 消抖 except KeyboardInterrupt: pass finally: GPIO.cleanup()轮询的方式代码直观但CPU占用高而且50ms的延时会导致响应迟滞。更优雅的方式是事件回调# 定义回调函数 def button_callback(channel): print(f按键触发通道: {channel}) state GPIO.input(LED_PIN) GPIO.output(LED_PIN, not state) # 注册事件监听 GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callbackbutton_callback, bouncetime200) try: print(等待按键事件按CtrlC退出...) time.sleep(60) except KeyboardInterrupt: pass finally: GPIO.remove_event_detect(BUTTON_PIN) GPIO.cleanup()使用事件回调时bouncetime200这个参数很关键。按键在物理上抖动会造成电平在几毫秒内反复跳变如果不开消抖一次按键会被当成多次触发。200ms 是机械按键的保守值具体看你的按键质量有些轻触开关 50ms 就够有些劣质的要 300ms 才能稳住。3.4 PWM与舵机控制Jetson Nano 的 PWM 功能很让人纠结40-pin 排针上只有两个硬件PWM引脚——物理第32脚PWM0和第33脚PWM2。而且底层用的是内核的pwm子系统Jetson.GPIO 库对PWM的支持不像树莓派那么透明需要额外指定设备树配置。直接给你能跑的代码import Jetson.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) PWM_PIN 32 # 物理第32脚硬件PWM0 GPIO.setup(PWM_PIN, GPIO.OUT, initialGPIO.LOW) pwm GPIO.PWM(PWM_PIN, 50) # 50Hz标准舵机频率 pwm.start(7.5) # 7.5%占空比舵机中位 try: while True: pwm.ChangeDutyCycle(2.5) # 约0度 time.sleep(1) pwm.ChangeDutyCycle(7.5) # 约90度 time.sleep(1) pwm.ChangeDutyCycle(12.5) # 约180度 time.sleep(1) except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup()代码层面完全没有问题但有两个细节必须提醒PWM频率限制。GPIO.PWM(PIN, freq)里的 freq 不是任意值都能生效它取决于内核里pwmchip的时钟源频率一般最高能到 100kHz 左右但不保证占空比的分辨率。做电机控制用 50Hz 或 20kHz 比较安全。GPIO.setup 后立刻 start 会报错。在 Jetson.GPIO 的某些版本里PWM 引脚必须先设为GPIO.OUT然后再调用GPIO.PWM中间要加一行time.sleep(0.1)等待设备节点稳定。如果上来就GPIO.PWM(PIN, 50)报OSError: [Errno 8] Exec format error大概率就是这个时序问题。4. 常见问题与排查技巧实录4.1 权限报错与“引脚没反应”类问题我汇总了这段时间实际遇到的频率最高的问题列一个速查表现象可能原因解决办法PermissionError: [Errno 13]用户不在gpio组或udev规则没生效执行 usermod 和 udevadm 刷新重新登录OSError: [Errno 8] Exec format errorPWM引脚时序问题 / 设备树未配置在 GPIO.setup 和 GPIO.PWM 之间加延时检查 /sys/class/pwm引脚电平一直不变引脚功能被复用成I2C/SPI查Pinmux表格改用纯GPIO引脚外部中断不触发用了PCA9539扩展引脚或设置边沿错误换成SoC内置GPIO引脚确认边沿模式读取输入值跳变没有配置上下拉或硬件浮空用 pull_up_down 参数或外接10k电阻程序卡死无响应事件回调里做了阻塞操作回调函数只做标志位赋值不要在回调里延时或打印太多其中“引脚被复用”这个问题最容易排查又最容易忽略。我的经验是开发前先用sudo cat /sys/kernel/debug/gpio查看每个引脚的当前状态确认你打算用的引脚是不是已经绑定到某个外设。如果显示gpio-xxx (i2c-0 | i2c )说明它被I2C占用了要么改Pinmux要么换引脚。4.2 和热词里的RK平台报错做一个对照热词里出现了一个很有意思的报错rk gpio set mux failed, please check group info——这是Rockchip平台上的常见错误意思是GPIO的mux复用配置失败。Jetson平台虽然不会直接弹这句话但背后的逻辑是一样的芯片的引脚资源有限同一个物理引脚在不同时刻只能做一种功能。你可能在切换功能时把某个引脚从GPIO切到UART结果管脚复用表写错功能就乱套了。在Jetson上对应的情况是直接操作/sys/class/gpio或使用config-pin类似工具时如果功能和当前Pinmux配置冲突内核会静默失败或者你的程序读到不稳定的电平。解决思路固定查硬件原理图确认引脚用途 → 查Pinmux配置表 → 修改设备树或使用官方配置工具 → 重启生效。另外热词里还提到“stm32f103的GPIO作为输入能不能承受5V电压”这类耐压问题在Jetson上同样存在。Jetson Nano的GPIO是3.3V逻辑不是5V容忍的。如果你接了一个5V输出的传感器到GPIO输入引脚轻则读数异常重则烧毁SoC的GPIO控制器。一定要接5V设备时中间串一个电平转换模块如BSS138四路电平转换板或者简单的分压电阻。4.3 Jetson Orin Nano带来的兼容性差异最近不少人在问 Orin Nano 能不能复用这套代码。答案是大部分能跑但细节有差异。Orin Nano 在40-pin排针上增加了更多的I2C、SPI和UART复用选项但 Jeston.GPIO 官方库对 Orin Nano 的支持是通过libgpiod的新版驱动做的引脚编号和旧的 Nano 不一致。如果你拿着 Nano 的 BOARD 模式代码直接跑在 Orin Nano 上大概率引脚错乱。建议 Orin 系列用户直接用gpiod命令行工具来确定gpiochip的线路偏移量或者改用python3-gpiod库而不是 Jetson.GPIO。我之前测过一次Orin Nano 的gpiochip0偏移量和 Nano 完全不同BMC映射也对不上代码改起来不比重写省事。4.4 排查硬件层面的坑软件排查完了有些问题其实出在硬件层面。我遇到过的杜邦线接触不良。Jetson Nano 的排针间距是标准2.54mm但工业级的杜邦线公头尺寸有时偏小插上去松松垮垮电平读数随机跳变。后来我全部换成带锁扣的杜邦头问题消失。外部电源不够。GPIO驱动LED没问题但如果同时驱动多个继电器或舵机3.3V和5V的电流会捉襟见肘。Jetson Nano 的5V引脚从USB Type-C口取电时最大只有3A带多个电机基本不可能。正确做法是外部单独供电GPIO只做信号控制。共地问题。外接传感器和Jetson板卡必须共地否则电平参考点漂移I2C、UART全都会出奇奇怪怪的问题。这个问题很基础但真的是高发问题每次排查都排到最后才发现是GND没接。5. 一些值得坚持的工程习惯最后分享几个我实际写GPIO程序时的习惯算不上多高深但能省不少调试时间。第一每个程序开头写清楚引脚编号模式。Jetson.GPIO 的 BOARD 和 BCM 模式虽然可以随时切换但混用会让你心态爆炸。我见过有人前半段用BOARD按物理引脚控制后半段用BCM控制另一个引脚两个引脚在代码里看起来很近实际上一个在天上一个在地下。第二优先用/sys/class/gpio或gpiod工具做快速验证。写Python程序之前先用命令行确认引脚能不能正常输出、输入方向对不对。直接跑代码出了问题很难判断是硬件还是库的问题# 用gpiod快速读取引脚状态 sudo gpiodetect sudo gpioinfo gpiochip0第三错误处理里务必释放资源。Jetson运行的是Linux如果程序异常退出前没有调GPIO.cleanup()再次运行时会发现引脚状态是上一次残留的有时甚至需要重启才能恢复。我的做法是在程序开头加atexit.register(GPIO.cleanup)保证异常退出也能清理。做Jetson Nano的GPIO开发表面上是在写Python操作引脚实际上是和Linux内核的GPIO子系统、设备树、Pinmux配置打交道。遇到问题时多往底层查一层往往能找到答案。如果你也是从树莓派转过来的建议忘了树莓派的那套习惯老老实实从硬件手册开始——这板子的脾气和树莓派不太一样。本文还有配套的精品资源点击获取