新闻详情

新闻详情

首页 / 资讯中心 / 详情

Jetson Orin Nano GPIO实战:从Pinmux原理到Python/C++驱动

发布时间:2026/9/28 17:57:45来源:尧图网络
Jetson Orin Nano GPIO实战:从Pinmux原理到Python/C++驱动
如果你手头有一块 Jetson Orin Nano想用它做嵌入式控制、机器人或边缘计算项目那么“控制 GPIO”几乎是绕不开的第一步。但很多人在这一步就卡住了——不是 Python 代码不会写而是搞不清引脚为什么没反应、为什么同一个引脚有时候能当串口有时候能当 PWM更不明白“Pinmux 引脚复用”到底在复用什么。这篇文章就围绕 Jetson Orin Nano 的 GPIO 实战展开用 Python 和 C 两种语言驱动 Pinmux 配置后的引脚从原理讲到代码再从代码讲到排错把这条链路彻底打通。无论你是刚拿到板子的入门玩家还是习惯用 C 做底层控制的老手这篇内容都能给你一份可以直接照着做的参考。先说明一下文章结构。第一部分我会讲清楚 Pinmux 到底是什么、它和 GPIO 模式选择的关系这部分虽然偏理论但特别重要不懂它你会浪费大量时间在“引脚为什么不工作”上。第二部分是环境准备包括 Python 和 C 工具链的安装。第三部分是 Pinmux 修改与设备树生效的完整流程。第四、第五部分分别用 Python 和 C 写出可运行的 GPIO 示例覆盖输入输出、上拉下拉、事件回调等核心操作。第六部分聊一个很实用的场景——用 UART 转 16 路 GPIO 扩展芯片做引脚扩容。最后是问题排查把常见坑一次性列全。1. 为什么先搞懂 Pinmux 再动手控制 GPIO1.1 GPIO 和 Pinmux 的本质关系Jetson Orin Nano 的 40-pin 扩展排针看上去和树莓派很像但底层机制差别很大。树莓派的引脚大多直接对应 SoC 的固定 GPIO 控制器而 Jetson 系列几乎所有引脚都挂在 PinmuxPin Multiplexer引脚复用器后面。Pinmux 的意思就是一个物理引脚在同一时刻只能选择一种功能它可能是 GPIO可能是 UART 的 TX/RX可能是 I2C 的 SDA/SCL也可能是 SPI、PWM、I2S 等。我用一个生活化的类比解释Pinmux 就像一条多功能数据线线的物理接口只有一个但它可以传输 HDMI 视频信号也可以传输 USB 数据信号甚至还能当音频线用。你需要用“开关”去选择当前它工作在哪种模式。在 Jetson 上这个“开关”就是 Pinmux 寄存器配置。所以当你发现某个引脚用 Python 怎么都拉不高电平第一反应不应该是怀疑代码而应该检查这个引脚的 Pinmux 当前是否被复用成了其他外设功能。举个例子40-pin 排针上的 Pin 8对应 SoC 的 UART1_TX它默认就可以是 UART 发送脚。如果你没改配置直接拿它当 GPIO 输出那你会得到一个“永远无法拉高”的引脚因为 Pinmux 把它交给了 UART 控制器。1.2 GPIO 的 8 种工作模式到底怎么选热词里提到的“GPIO 的 8 种工作模式”其实是一个常见概念在 STM32 等 MCU 上被广泛讨论但在 Jetson 的 Linux 环境下这个概念需要做一次迁移理解。在 MCU 语境下GPIO 模式通常包括模式含义输入浮空引脚不接上下拉电平由外部电路决定输入上拉引脚内部接上拉电阻默认高电平输入下拉引脚内部接下拉电阻默认低电平模拟输入引脚接入 ADC 模拟前端开漏输出只能拉低拉高靠外部上拉电阻推挽输出既能拉高也能拉低驱动能力强复用推挽引脚交给片上外设如 UART、SPI使用复用开漏用于 I2C 等需要线与逻辑的总线在 Jetson Orin Nano 的 Linux 系统里这些模式不是通过 STM32 那样的寄存器位直接配出来的而是分散在Pinmux 配置、GPIO 控制器的方向寄存器、设备树中的 pinctrl 节点以及gpiod 的请求标志flags这四个层面里。Pinmux 决定“这个引脚是谁的”GPIO 控制器决定“如果是 GPIO方向是输入还是输出”设备树描述“上电后哪个外设接管”gpiod 标志则在运行时微调上下拉和默认电平。所以你在 Jetson 上选 GPIO 模式要按优先级顺序思考先看 Pinmux 是否允许这个引脚当 GPIO再看方向再看上下拉最后看驱动能力。这也是我能给你最重要的建议之一后面所有实操都会围绕这条主线展开。2. 开发环境准备Python 与 C 工具链2.1 系统环境与 Python 准备Jetson Orin Nano 出厂默认刷的是 NVIDIA JetPack SDKL4T它自带 Ubuntu 20.04JetPack 5.x或 Ubuntu 22.04JetPack 6.x定制版系统内置 Python 3。你在终端里输入python3 --version就能看到版本号。这里要说一个常见误区不要手贱卸载系统自带的 Python也不要直接往系统 Python 里乱装包。Jetson 的许多系统工具和 ROS、CUDA 组件都依赖特定版本的 Python 环境动它是给自己挖坑。我推荐的 Python 环境管理方式是使用 venv 创建独立虚拟环境sudo apt update sudo apt install -y python3-venv python3-pip mkdir -p ~/gpio_lab cd ~/gpio_lab python3 -m venv venv_gpio source venv_gpio/bin/activate激活后你会发现命令行前面多了(venv_gpio)前缀这就说明你进入了虚拟环境。所有后续通过 pip 安装的包都会装到这个环境里不影响系统全局。关于“Microsoft Visual C 14.0 is required”这类的报错这是 Windows 上安装某些 Python 包时的经典问题和 Jetson 无关。但如果你在 Windows 上只做代码编辑、然后把代码传到 Jetson 运行那需要知道报错是因为有些包比如老版本的pycuda、paddlepaddle在 Windows 上需要 MSVC 编译扩展。解决方法是安装“Microsoft C Build Tools”或者干脆把这类包放到 Jetson 上原生安装。如果你用 VS Code Remote-SSH 插件连到 Jetson 上开发本地电脑只需要装 VS Code其他都在板子上运行就不会踩这个坑。我实际用的就是 VS Code Remote-SSH 方案体验非常顺。本机装好 VS Code安装 Remote-SSH 插件通过 SSH 连上 Orin Nano打开/home/user/gpio_lab目录代码在板子上直接跑调试也方便。2.2 C 编译环境与库选择C 开发在 Jetson 上不需要做交叉编译。因为板子本身就是完整 Ubuntu 系统直接用 g 编译本地运行就好。这也是开发效率最高的方式——不用配置交叉工具链不用考虑目标平台库不匹配的问题。安装基础编译工具sudo apt update sudo apt install -y build-essential cmake git这里build-essential会帮你装好 gcc、g、make 等核心工具。cmake用于大型项目管理git用于拉取代码。至于 GPIO 的 C 控制方案我会在第五部分重点讲 libgpiod它是一个比老式 sysfs 更现代、更稳定的库。先装库和命令行工具sudo apt install -y libgpiod-dev gpiod如果你用过老旧的sysfs接口就是操作/sys/class/gpio/export那套你可能会觉得 libgpiod 不熟悉。但相信我它是从 Linux 内核 4.8 开始引入的标准 GPIO 用户空间 API字符设备/dev/gpiochip0为底层比 sysfs 更安全、更快速也是 NVIDIA 官方推荐搭配设备树用来操作 GPIO 的方式。后面我会用 sysfs 和 libgpiod 各写一遍对比让你看清差异。3. Pinmux 修改与设备树配置让引脚的“身份”符合预期3.1 用 Pins_Linux 工具导出和修改 Pinmux 配置NVIDIA 提供了一个非常实用的配置工具叫Pins_Linux全称 Pins_Linux_Tegra 或 Pins_Linux on Jetson它的核心是一个 Excel 表格 (.xls/.xlsx) 样式。你可以从 NVIDIA 开发者官网下载对应 JetPack 版本的 Pins 包。打开 Pins 表格你会看到每个模块module的每个引脚都有名称比如GPIO03、UART1_TX以及它可复用的功能列表。你要做的操作是找到你想用的引脚把它的 Pinmux 功能改成“GPIO”然后导出新的配置重新生成 DTB设备树二进制文件。具体流程是这样的下载并解压 Pins 包里面包含一个可执行脚本Pins_Linux目录里面是引脚配置的.dtsi源文件。在 Pins 表格中选中需要的引脚设定为 GPIO。使用工具从表格导出.dtsi覆盖文件称为 pinmux override。将这个.dtsi合入你的设备树源文件。编译生成.dtb并刷新到板子的 boot 分区。这个操作说起来容易但新手容易在“设备树怎么改”上卡住。好在 JetPack 5 之后的系统给大家留了一条更友好的路利用/boot/dtb目录下的设备树覆写Device Tree Overlay机制。你可以在/boot/extlinux/extlinux.conf里指定要加载的 overlay 文件然后用fdtoverlay工具动态打补丁。NVIDIA 官方提供的引脚配置工具已经可以生成 pinmux 配置头文件.dtsi你把它和你的主设备树进行编译。更简单的实战做法是直接修改/boot下的设备树源文件再重新编译。不过要注意每次 JetPack 升级都可能覆盖掉这些修改所以最好把自定义配置写成 overlay 统一管理。3.2 让配置生效从 DTB 编译到重启验证假设你已经改好了 Pinmux 配置接下来要做的就是把新的设备树刷进去。在 Jetson Orin Nano 上设备树文件在/boot/dtb/nvidia/目录下板型对应的文件名大概是tegra234-p3767-0000-p3768-0000-a0.dtb这样的格式。你需要在~/pinmux_workspace目录下准备好你的.dtsioverride 文件比如my_gpio_override.dtsi。引入基础 DTS 并用dtc工具编译。基础编译命令大致如下sudo apt install -y device-tree-compiler dtc -I dtb -O dts -o base.dts /boot/dtb/nvidia/tegra234-p3767-0000-p3768-0000-a0.dtb拿到base.dts后你可以在里面找到对应的 pinctrl 节点或者直接把你的修改合入后反向编译dtc -I dts -O dtb -o my_modified.dtb base.dts sudo cp my_modified.dtb /boot/dtb/nvidia/tegra234-p3767-0000-p3768-0000-a0.dtb然后重启。这里我要补一个非常重要的“为什么”设备树Device Tree在 ARM 嵌入式 Linux 里是硬件描述的“事实标准”。内核启动时会读取 DTB把硬件资源和驱动进行绑定。如果你想用 GPIO但 Pinmux 没放开内核 driver 不知道该引脚可以当 GPIO 用你在用户空间调任何 API 都会失败。所以 Pinmux → 设备树 → 内核驱动 → 用户空间 API 是一条完整的链路任何一环断了都不工作。另外有个很常见的“重启后黑屏”现象。前面热词里也出现“jetson orin nano 启动后黑屏”很多时候就是因为改设备树改错了导致显示驱动没起来或者 pinctrl 配置冲突。所以改动前务必备份原始 DTB至少保证能通过恢复模式刷回去。3.3 实战场景把多余的 UART 引脚改成 GPIOOrin Nano 的 40-pin header 里物理上不少引脚默认是 UART 或 I2C 功能。比如很多人做机器人项目时 UART0 用来接激光雷达UART1 想改造成普通的 GPIO 输入输出用于连接按键和 LED。我的做法是在 Pins 表格里把UART1_TX和UART1_RX两行的功能从UART1改成GPIO同时设置好默认方向和默认输出值重新生成 DTSI修改设备树后编译刷入。改完后通过cat /sys/kernel/debug/gpio就能看到对应 GPIO 已经注册成功。4. Python 实战用 Jetson.GPIO 库点亮 LED 并读取按键4.1 安装 Jetson.GPIO 并处理权限NVIDIA 官方还有一个 Python 库叫Jetson.GPIO它是树莓派 RPi.GPIO 库的移植版本API 几乎一模一样。对于写 Python 的开发者来说这套接口非常友好。安装方式pip install Jetson.GPIO不过手动 pip 安装的版本可能会比较旧JetPack 系统镜像里有些版本默认带了Jetson.GPIO但版本可能偏老。推荐到 NVIDIA 官方 GitHub 仓库NVIDIA/jetson-gpio拉取最新源码手动安装git clone https://github.com/NVIDIA/jetson-gpio.git cd jetson-gpio sudo python3 setup.py install接下来是权限配置。默认情况下普通用户没有权限操作/dev/gpiochip*你需要将当前用户加入gpio组sudo groupadd -f -r gpio sudo usermod -aG gpio $USER sudo cp lib/python/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/添加 udev 规则后重载规则并重新登录重启也行sudo udevadm control --reload-rules sudo udevadm trigger为什么要有 udev 规则这一步因为 Linux 下/dev/gpiochip*设备节点的默认属主是 root普通用户无法直接访问。Jetson.GPIO 库底层走的是libgpiod字符设备接口没有权限就会在打开设备时报Permission denied。加一条 udev 规则自动把节点权限放给 gpio 组算是嵌入式 Linux 开发的标准做法。4.2 最简示例控制 LED 亮灭在开始写代码之前先确认你接的是 40-pin 排针上的哪个引脚。这里有一个关键概念Jetson.GPIO 库用的是排针物理编号不是 SoC GPIO 编号。例如 40-pin 里的 Pin 7 对应的是GPIO216但在 Python 里你直接写board.PIN7或board.P7就行。import Jetson.GPIO as GPIO # 使用物理编号模式 GPIO.setmode(GPIO.BOARD) # 把 Pin 7 设置为输出初始为低电平 LED_PIN 7 GPIO.setup(LED_PIN, GPIO.OUT, initialGPIO.LOW) try: while True: # 拉高 GPIO.output(LED_PIN, GPIO.HIGH) time.sleep(1) # 拉低 GPIO.output(LED_PIN, GPIO.LOW) time.sleep(1) except KeyboardInterrupt: pass finally: GPIO.cleanup()这段代码是典型的 GPIO 输出测试程序。GPIO.setmode(GPIO.BOARD)是告诉库我们按排针物理编号来索引引脚这是我最推荐的模式因为不管设备树怎么改、SoC 编号怎么复杂你只要对照板子上的丝印就不会搞错。GPIO.setup(LED_PIN, GPIO.OUT, initialGPIO.LOW)这行做了两件事把引脚方向设为输出并把初始电平设为低。这样可以避免程序一启动时 LED 出现不可控闪烁。GPIO.cleanup()是不断电情况下释放引脚的语句。如果不调用它程序退出后引脚状态会保持在最后设置的电平下次运行可能会遇到“引脚被占用”或状态不对的怪问题。实测下来这个库在 Orin Nano 上跑得很稳定切换速度足够驱动普通 LED 和继电器模块。我强烈建议你第一次拿到板子时先用这个示例做“引脚连通性测试”排除硬件接线问题。4.3 输入模式与事件回调输入模式是 GPIO 的另一个半边天。比如接一个按键检测按下和释放。import Jetson.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) BTN_PIN 11 # 设置为输入启用内部上拉 GPIO.setup(BTN_PIN, GPIO.IN, pull_up_downGPIO.PUD_UP) def on_press(channel): print(fpin {channel} pressed!) # 检测下降沿按键按下时 GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callbackon_press, bouncetime200) try: while True: time.sleep(1) except KeyboardInterrupt: pass finally: GPIO.cleanup()这里面有几个细节我单独强调一下。第一为什么要用上拉。把引脚设为输入并启用内部上拉那么当按键没有按下时引脚默认读到高电平1。按键按下接地后引脚被拉低0。程序检测到电平从高变低下降沿就认为按键按下了。这个模式可以避免悬空引脚在读入时出现随机跳变。第二去抖参数 bouncetime。机械按键在物理接触的瞬间会产生几毫秒到几十毫秒的电平抖动直接读取会得到一连串的 0/1 变化。bouncetime200表示 200 毫秒内只报告一次事件这个值在实测中能过滤掉绝大多数机械抖动问题。第三事件回调是在独立线程执行的。callback函数里不要做耗时操作否则会阻塞后续事件。如果需要做复杂处理应该把数据放到队列里由主循环统一处理。4.4 用 Python 控制 UART 转 GPIO 扩展芯片热词里提到一个很实际的需求“1 路 UART 串口转 16 路的 GPIO 扩展芯片”。它解决的核心问题是板载 GPIO 数量不够用的场景。市面上这类芯片有不少比如用WK21244 路 UART 扩展、SC16IS752I2C/SPI 转双 UART、以及专门的串口转 GPIO 控制器例如NCA9555是 I2C 转 GPIOMCP23017也是 I2C如果是 UART 转 GPIO则需要使用带串口协议的专用 IO 扩展芯片等。以典型的 UART 转 16 路 IO 扩展模块为例它的工作原理是主控通过 UART 发送指令帧芯片解析后把指定 IO 拉高或拉低同时也能回读输入状态。在 Python 里操作 UART 用pyserialpip install pyserial示例代码逻辑import serial ser serial.Serial(/dev/ttyTHS1, 115200, timeout0.1) # 假设协议为 5 字节帧0xAA 0x55 命令 通道 值 def set_io(channel, value): tx bytes([0xAA, 0x55, 0x01, channel, value]) ser.write(tx) resp ser.read(5) # 根据协议校验响应 return resp # 把第3路设置为高电平 set_io(3, 1)注意/dev/ttyTHS1是 Jetson 的 UART 设备节点名称。不同 UART 控制器对应不同节点比如 UART0 通常是/dev/ttyTHS0UART1 是/dev/ttyTHS1。你可以通过ls /dev/ttyTHS*来查看。这种扩展方式特别适合需要大量开关量控制的场景比如控制多路继电器、矩阵键盘、步进电机驱动器等。单片机和 Linux 主机之间的通信只占用一根 TX 和一根 RX布线简单驱动逻辑也直观。5. C 实战用 libgpiod 和 sysfs 控制 GPIO5.1 sysfs 老方法理解 Linux 旧式 GPIO 接口在 libgpiod 出现之前Linux 用户空间操作 GPIO 的标准方式是 sysfs。你通过向/sys/class/gpio/export写入编号来“导出”引脚然后操作对应目录下的 value、direction、edge 等文件。示例#include iostream #include fstream #include string #include unistd.h int export_gpio(int gpio_num) { std::ofstream exp(/sys/class/gpio/export); exp gpio_num; return 0; } int set_direction(int gpio_num, std::string dir) { std::string base /sys/class/gpio/gpio std::to_string(gpio_num) /; std::ofstream dirFile(base direction); dirFile dir; return 0; } int write_value(int gpio_num, bool high) { std::string base /sys/class/gpio/gpio std::to_string(gpio_num) /; std::ofstream valFile(base value); valFile (high ? 1 : 0); return 0; } int main() { int pin 216; // 对应 Pin 7 的 SoC GPIO 编号 export_gpio(pin); set_direction(pin, out); while (true) { write_value(pin, true); sleep(1); write_value(pin, false); sleep(1); } return 0; }这个写法虽然能用但有几个明显的痛点GPIO 编号难查。SoC 的 GPIO 编号往往经过多级映射同一个物理引脚在不同内核版本上的编号可能不一样你得反复查文档。没有方向缓存。如果引脚已经被占用写 direction 会报错但错误反馈很模糊。性能差。每次读写都经过文件系统一次切换只有几千赫兹不适合高速接口模拟。内核已标记为废弃。sysfs GPIO 接口在较新的内核里已经标注 deprecate虽然目前还能用但未来会被移除。所以我个人的建议很明确新项目直接上 libgpiod别再学 sysfs 了。5.2 libgpiod 的正确用法完整可运行示例libgpiod 是内核 GPIO 字符设备接口的用户空间库它提供两个层级命令行工具gpioget、gpioset、gpiofind 等和 C 库 API。我们先看命令行怎么用再上 C 代码。列出所有 GPIO 控制器gpiodetect查到某个引脚的全局名称和芯片内偏移gpioinfo 0假设我们在/dev/gpiochip0上找到偏移为 8 的引脚把它设为高电平gpioset 0 81C 代码示例#include gpiod.h #include iostream #include chrono #include thread int main() { const char* chipname gpiochip0; unsigned int line_num 8; gpiod_chip* chip gpiod_chip_open_by_name(chipname); if (!chip) { std::cerr open chip failed std::endl; return -1; } gpiod_line* line gpiod_chip_get_line(chip, line_num); if (!line) { std::cerr get line failed std::endl; gpiod_chip_close(chip); return -1; } int ret gpiod_line_request_output(line, my_gpio_app, 0); if (ret 0) { std::cerr request output failed std::endl; gpiod_chip_close(chip); return -1; } for (int i 0; i 10; i) { gpiod_line_set_value(line, 1); std::this_thread::sleep_for(std::chrono::seconds(1)); gpiod_line_set_value(line, 0); std::this_thread::sleep_for(std::chrono::seconds(1)); } gpiod_line_release(line); gpiod_chip_close(chip); return 0; }这段代码中有几个需要留意的关键点gpiod_chip_open_by_name 打开的是字符设备节点/dev/gpiochip0。如果你的 40-pin header 上的一部分引脚在另一个控制器里可能需要打开/dev/gpiochip1等。用gpiodetect可以查清楚每个控制器的基地址和引脚数量。gpiod_line_request_output 的第三个参数是初始值传 0 表示请求成功后引脚先保持低电平。这样做可以避免在初始化时出现不可预期的脉冲。gpiod_line_request 是独占请求一个引脚在同一时刻只能被一个进程请求。如果你用 Python 的 Jetson.GPIO 已经占用了某个引脚再用 C 的 libgpiod 去请求同一个引脚会得到EBUSY错误。这一点在混合编程时特别注意。如果你想让代码更方便地在不同板卡之间迁移还可以用gpiod_line_find_by_name或提供GPIO 消费标签Consumer Label。在设备树里给某个 GPIO 指定别名后用户空间可以按标签获取引脚这样代码里就不需要关心具体偏移号了。不过这种方式需要设备和设备树配合对于嵌入式产品来说很实用。5.3 C 中读取输入并监听事件libgpiod 不仅支持轮询读值还支持边缘事件监听。下面的例子演示如何监听上升沿和下降沿#include gpiod.h #include iostream #include cstring int main() { gpiod_chip* chip gpiod_chip_open_by_name(gpiochip0); gpiod_line* line gpiod_chip_get_line(chip, 9); // 请求带上升沿和下降沿检测的输入 int ret gpiod_line_request_rising_falling_edges(line, button_listener, GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE | GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); if (ret 0) { std::cerr request events failed: strerror(errno) std::endl; return -1; } while (true) { gpiod_line_event ev; int wait gpiod_line_event_wait(line, 1000); // 等待1秒 if (wait 1) { gpiod_line_event_read(line, ev); if (ev.event_type GPIOD_LINE_EVENT_RISING_EDGE) { std::cout rising edge std::endl; } else if (ev.event_type GPIOD_LINE_EVENT_FALLING_EDGE) { std::cout falling edge std::endl; } } else if (wait 0) { std::cerr wait error: strerror(errno) std::endl; break; } } gpiod_line_release(line); gpiod_chip_close(chip); return 0; }这里gpiod_line_event_wait是一个带超时的等待函数单位是毫秒。如果返回 1 表示有事件到来返回 0 表示超时返回 -1 表示出错。这种非阻塞式的等待方式在实时控制中非常有用你可以把 GPIO 事件和主循环任务轻松结合起来。6. 从 GPIO 到应用场景怎么用好 Jetson Orin Nano 的 40-pin 接口6.1 引脚资源盘点与选型策略Jetson Orin Nano 的 40-pin header 可用资源其实不算很多尤其是都被其他功能占用时。你需要提前规划哪些引脚必须保留给 UART雷达、调试串口、哪些引脚给 I2C接 IMU、OLED、哪些给 SPI接高速 ADC 或显示屏、哪些留作通用 GPIO。我给一个我常用的分配建议功能引脚备注LED 输出Pin 7GPIO216默认即可复用按键输入Pin 11上拉输入配合外部按键接地I2C 总线Pin 27/28接 IMU、RTC 等UART 调修Pin 8/10保留给串口控制台备用 GPIOPin 15/16/18/22按需规划这个表不是死标准但它提醒你别把所有引脚默认都当 GPIO 用。先根据项目外设需求做一张“引脚占用表”再回推 Pinmux 配置这样能省掉大量中途改配置的时间。6.2 用 UART 转 GPIO 芯片扩展输出能力如果你做了引脚规划后发现 GPIO 还是不够用又不想为了多加几个引脚去换更大的 Jetson 模组那么用 IO 扩展芯片是性价比很高的选择。在 Jetson 上扩展 GPIO 主要有三种方案扩展方式示例芯片优点缺点I2C 转 GPIOPCA9555、MCP23017接线简单只用 2 根线I2C 速率受限不适合高频信号SPI 转 GPIOMCP23S17速率高占用 4 根线UART 转 GPIO专用串口 IO 芯片只占 TX/RX远距离更稳协议定制驱动要自己写热词里的“1 路 UART 串口转 16 路的 GPIO 扩展芯片”属于第三种。这种芯片常见的封装是一块小模块板上有一路 UART 输入、16 路数字 IO。主机通过串口发送指令控制任意 IO 输出高/低或者读回输入状态。在实际项目中我一般会把这种模块用来控制继电器阵列灯光、风扇、泵等因为继电器是慢设备几十毫秒的响应延迟完全可接受。而像编码器读取这种需要高速响应的场景我会用原生 GPIO 或 SPI 控制器。在 C 里操作这种芯片也很方便核心逻辑和 Python 版一致只是把串口读写换成 POSIX 的open、write、read。如果你项目里同时有 Python 和 C 模块我建议把 UART 驱动层封装成一个独立进程对外提供 HTTP 或 ROS 服务这样语言无关性更好。7. 常见问题与排查技巧实录7.1 启动后黑屏“Jetson Orin Nano 启动后黑屏”几乎是所有改过设备树或刷过 boot 分区的用户噩梦。我遇到过好几次原因各不相同Pinmux 配置里把 HDMI/DSI 相关引脚改成了 GPIO导致显示控制器无法正常输出信号。DTB 文件命名错误或文件损坏bootloader 在加载驱动时崩了。电源功率不够这和 GPIO 无关但黑屏现象容易被误判。我的排查顺序是拔掉所有外设包括 HDMI、USB、GPIO 外接板只保留电源和 USB 串口如果有。如果你有 USB 转串口调试线接上查看内核日志。按住板上的Force Recovery按键进入恢复模式用lsusb确认设备被识别为 NVIDIA APX。在恢复模式下将备份的原始 DTB 刷回去sudo ./tools/tegraflash.py --bl cboe.bin --applet mb1_t194_prod.bin ...如果恢复模式不生效可以刷官方 JetPack 镜像重新来一遍。最重要的习惯是改 DTB 前备份。我会把原始 DTB 放一份到/home/user/backup/并记录当时的 JetPack 版本因为不同版本之间的 DTB 格式有差异不能混用。7.2 引脚读取总是高电平或低电平很多用户问我“我设成输入了为什么读到的值一直为 1”这种情况常见原因有三个引脚悬空。没有启用内部上拉/下拉也没接外部电阻引脚状态在漂移读到随机值。注意有的引脚默认没有内部上下拉你必须外接一个 10k 电阻。Pinmux 没有设为 GPIO。前面说了如果引脚被 UART 控制器占用GPIO 输入寄存器读出来的可能是恒定的电平。接线错误。确认你用的是 40-pin 排针的定义而不是 SoC 上某个引脚。40-pin 排针丝印可能和实际 SoC 引脚不一致。排查方法用cat /sys/kernel/debug/gpio查看当前 GPIO 状态确认内核是否真的把它识别为 GPIO再用万用表量引脚电压排除接线问题最后在 Python/C 里去读如果这时还不对再查 Pinmux。7.3 Python 和 C 的权限问题Python 使用 Jetson.GPIO 时报Could not open GPIO chip或者 C 打开/dev/gpiochip0时报Permission denied通常都是 udev 规则没配好或者当前用户不在 gpio 组。处理流程见 4.1 节。还有一种是“权限看着没问题但还是打不开”这是因为你已经用 root 运行过一次程序/dev/gpiochip0上的文件描述符被某个进程占用。用sudo fuser -v /dev/gpiochip0查一下谁在占用然后 kill 掉。注意这个场景在 jetson-gpio 的 Python 库中表现得很隐蔽因为它是通过libgpiod字符设备打开引脚的如果引脚被占用报错很模糊。7.4 速查表现象可能原因排查手段Python 报Permission denied用户不在 gpio 组 / udev 规则缺失重新执行 usermod 和 udevadm引脚输出无效Pinmux 被外设占用查看 Pinmux 配置检查 DTB输入读到随机值上下拉没配设置内部上拉/下拉或外接电阻启动黑屏DTB 配置错误恢复模式刷回备份 DTBC 请求引脚返回 EBUSY引脚被其他进程占用用 fuser 或 gpioinfo 检查占用串口扩展无响应UART 设备节点选错或波特率不匹配ls /dev/ttyTHS* 确认节点检查协议7.5 避坑心得最后分享几条我自己在多次项目中沉淀下来的经验。第一永远先测引脚再写业务逻辑。拿到板子先把所有要用的引脚写一遍“全灭全亮”测试程序确认每个引脚都能正常输出再进入系统设计。这个习惯看起来多花十分钟但能帮你把硬件问题从软件问题中分离出来。第二Pinmux 改动是全局性的不是单点性的。有些引脚之间是互斥关系比如某个 Pin 组你选用了 I2S 功能旁边的几个引脚可能就不能单独做 GPIO 了。所以改动前一定要翻阅 Pins 表格的复用规则别只看目标引脚那一行。第三Python 和 C 可以混用但要明确边界。我在实际项目中会这样分工控制逻辑需要高频、实时性要求高用 C/libgpiod算法的原型验证、上层状态机、HTTP 对外服务用 Python/Jetson.GPIO。两者之间的共享状态通过简单的文本文件或 socket 传递不要让他们同时操作同一个 GPIO。第四别低估接线质量对 GPIO 的影响。40-pin 排针的杜邦线如果接触不良会表现成“时好时坏”的诡异问题。我在排查这类问题时会用万用表短接测试或直接焊线接线排查往往比软件排查更快。第五关于热词里看到的“GPIO 的 8 种工作模式”建议你在 Jetson 上重点掌握四种就够用输入上拉/下拉、输出推挽、开漏输出、复用功能。模拟输入在这个板子上不是通用能力需要使用额外 ADC 芯片开漏输出在 I2C 和逻辑电平转换里很常用其他地方一般用不到。如果你准备把 Jetson Orin Nano 用在量产产品里我还会建议把 Pinmux 配置做成一份版本化的工程文件放进 Git 仓库这样每个团队成员都能清楚知道哪些引脚被分配给了什么功能。毕竟GPIO 数量有限而踩过的坑会随时间被遗忘但表格和代码不会。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

028_封装寄生参数对高频增益的衰减 2026/9/28 19:54:31

028_封装寄生参数对高频增益的衰减

028、封装寄生参数对高频增益的衰减 一个让我连续加班两周的“幽灵衰减” 几年前做一个便携式射频收发模块,频段在几百兆赫兹。原理图仿真、板级仿真都过了,增益预算留了3dB余量,按说稳得很。结果第一版样机回来,小信号增益在目标频段高端直接掉了6dB,低端倒还正常。更诡…

阅读更多 →
小样本医学眼疾分类实战:DenseNet121迁移学习完整方案 2026/9/28 19:54:24

小样本医学眼疾分类实战:DenseNet121迁移学习完整方案

简介:这是一份面向医学图像处理学习者与毕业设计人员的DenseNet121小样本眼疾分类完整项目,使用Python及主流深度学习框架实现,聚焦数据稀缺条件下的模型训练与泛化问题。资源共11个文件,以7个Python脚本为核心,分别承…

阅读更多 →
Python基于BERT的中文情感分类实战与避坑指南 2026/9/28 19:54:17

Python基于BERT的中文情感分类实战与避坑指南

简介:一套面向毕业设计场景的基于BERT中文文本情感分类项目源码包,适合自然语言处理初学者或需要快速搭建分类模型的开发者。项目按数据准备、BERT模型加载、Tokenizer处理、分类层构建、训练与预测的完整流程组织,覆盖数据集划分、特殊token…

阅读更多 →
无线洗地机怎么选?从自清洁、贴边、续航到污水箱的实用选购指南 2026/9/28 19:54:17

无线洗地机怎么选?从自清洁、贴边、续航到污水箱的实用选购指南

家里地面清洁这件事,过去十年基本被两派割据:一派坚持“拖把扫帚”的原教旨主义,省钱但费腰;另一派投入扫地机器人的怀抱,省了手却忍不了边角漏扫、拖布发酸和被地毯卡死。最近两年,无线洗地机成了中间派的…

阅读更多 →
C与C++核心区别详解:从语法到工程实践 2026/9/28 19:54:04

C与C++核心区别详解:从语法到工程实践

1. 从一堆热搜词里看出来的真实需求先把话说在前头:C 和 C 的区别这个话题,网上的文章没有一万篇也有八千篇,但绝大多数都在干一件事——列一张对比表,然后告诉你"C 是面向过程的,C 是面向对象的"&#xff0…

阅读更多 →
AgentAI 产品形态解析:Cursor 技术路线与 TaoToken 配置实践 2026/9/28 19:53:58

AgentAI 产品形态解析:Cursor 技术路线与 TaoToken 配置实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉