系列文章目录
前言
Jetson TX1、TX2、AGX Xavier 和 Nano 开发板包含一个 40 针 GPIO 针座,类似于 Raspberry Pi 中的 40 针针座。这些 GPIO 可使用 Jetson GPIO 库软件包中提供的 Python 库进行数字输入和输出控制。该库的 API 与 Raspberry Pi 的 RPi.GPIO 库相同,目的是提供一种简单的方法,将 Raspberry Pi 上运行的应用程序转移到 Jetson 板上。
本文将介绍 Jetson GPIO 库软件包的内容、如何配置系统和运行所提供的示例应用程序,以及库的 API。
一、软件包组件
除本文外,Jetson GPIO 库软件包还包含以下内容:
lib/python/ 子目录包含实现所有库功能的 Python 模块。gpio.py 模块是将导入应用程序的主要组件,并提供所需的 API。gpio_event.py 和 gpio_pin_data.py 模块由 gpio.py 模块使用,不得直接导入应用程序。
samples/ 子目录包含示例应用程序,有助于熟悉库 API 和开始应用程序。simple_input.py 和 simple_output.py 应用程序分别展示了如何对 GPIO 引脚进行读写操作,而 button_led.py、button_event.py 和 button_interrupt.py 则分别展示了如何使用忙等待、阻塞等待和中断回调来按下按钮以闪烁 LED。
二、安装
以下是在系统中安装 Jetson.GPIO python 模块的方法。如需示例应用程序,请克隆此软件源到您的系统。
2.1 使用 pip
使用 pip 安装此库是最简单的方法:
sudo pip install Jetson.GPIO
2.2 手动下载
您可以克隆此 git 仓库,也可以下载存档文件并解压。您可以将程序库文件放置在系统中任意位置。您可以通过手动设置PYTHONPATH 直接使用此目录中的库,也可以使用 setup.py 安装:
sudo python3 setup.py install
三、设置用户权限
要使用 Jetson GPIO 库,首先必须设置正确的用户权限/组。
创建一个新的 gpio 用户组。然后将用户添加到新创建的组中。
sudo groupadd -f -r gpio sudo usermod -a -G gpio your_user_name
将 99-gpio.rules 文件复制到 rules.d 目录,安装自定义的 udev 规则。
如果您已经下载了 Jetson.GPIO.Rule 的源代码,请将其复制到 rules.d 目录中:
sudo cp lib/python/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/
如果您从软件包中安装了 Jetson.GPIO,例如使用 pip 将其安装到虚拟环境中:
sudo cp venv/lib/pythonNN/site-packages/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/
要执行新规则,需要重新启动或运行 udev 规则重新加载:
sudo udevadm control --reload-rules && sudo udevadm trigger
四、运行示例脚本
根据需要设置权限后,就可以使用 samples/ 目录中提供的示例应用程序。下面将介绍每个应用程序的操作:
simple_input.py: 该应用程序使用 BCM 引脚编号模式,读取 40 引脚接头第 12 引脚的值并将其打印到屏幕上。
simple_out.py: 此应用程序使用 Raspberry Pi 的 BCM 管脚编号模式,每隔 2 秒在 BCM 第 18 管脚(或针座上的电路板第 12 管脚)上交替输出高低值。
button_led.py: 此应用程序使用 BOARD 引脚编号。它需要一个连接 18 针和接地的按钮、一个连接 18 针和 3V3 的上拉电阻以及一个连接 12 针的 LED 和限流电阻。程序会读取按钮状态,并在每次按下按钮时使 LED 亮起 1 秒钟。
button_event.py: 该程序使用 BOARD 引脚编号。它需要一个连接到 18 针和 GND 的按钮、一个连接按钮和 3V3 的上拉电阻以及一个连接到 12 针的 LED 和限流电阻。该应用程序执行与 button_led.py 相同的功能,但对按钮按下事件执行阻塞等待,而不是持续检查引脚的值,以减少 CPU 占用。
button_interrupt.py: 该应用程序使用 BOARD 引脚编号。它需要一个连接到第 18 针和接地引脚的按钮、一个将按钮连接到 3V3 的上拉电阻、一个连接到第 12 针的 LED 和限流电阻,以及一个连接到第 13 针的第二个 LED 和限流电阻。该应用会持续缓慢闪烁第一个 LED 灯,只有在按下按钮时才会快速闪烁第二个 LED 灯五次。
若要运行这些示例应用程序,请将 Jetson.GPIO 添加到PYTHONPATH:
python3 <name_of_application_to_run>
如果 Jetson.GPIO 没有添加到PYTHONPATH,也可以使用 run_sample.sh 脚本运行这些示例程序。在 samples/ 目录下,可使用以下命令完成此操作:
./run_sample.sh <name_of_application_to_run>
还可以通过以下方式查看脚本的使用情况:
./run_sample.sh -h ./run_sample.sh --help
五、完整的库 API
Jetson GPIO 库提供 RPi.GPIO 库提供的所有公共 API。下面将讨论每个 API 的使用:
5.1 导入库
要导入 Jetson.GPIO 模块,请使用
import Jetson.GPIO as GPIO
这样,您就可以在应用程序的其余部分中将该模块称为 GPIO。对于使用 RPi 库的现有代码,也可以使用 RPi.GPIO 而不是 Jetson.GPIO 来导入模块。
5.2 引脚编号
Jetson GPIO 库提供四种 I/O 引脚编号方式。前两种与 RPi.GPIO 库提供的模式相对应,即 BOARD 和 BCM,分别指 40 引脚 GPIO 接头的引脚编号和 Broadcom SoC GPIO 编号。其余两种模式,即 CVM 和 TEGRA_SOC 使用字符串而非数字,分别对应 CVM/CVB 连接器和 Tegra SoC 上的信号名称。
要指定使用哪种模式(必选),请使用以下函数调用:
GPIO.setmode(GPIO.BOARD) # or GPIO.setmode(GPIO.BCM) # or GPIO.setmode(GPIO.CVM) # or GPIO.setmode(GPIO.TEGRA_SOC)
要查看已设置的模式,可以使用:
mode = GPIO.getmode()
模式必须为 GPIO.BOARD、GPIO.BCM、GPIO.CVM、GPIO.TEGRA_SOC 或 None。
5.3 警告
您尝试使用的 GPIO 有可能已在当前应用程序的外部使用。在这种情况下,如果正在使用的 GPIO 被配置为默认方向(输入)以外的任何其他方向,Jetson GPIO 库将向您发出警告。如果在设置模式和通道前尝试清理,也会发出警告。要禁用警告,请调用
GPIO.setwarnings(False)
此外,Jetson.GPIO 使用警告模块发出警告。因此,您可以使用 Python 标准库 - 警告来控制警告消息。
5.4 设置通道
在将 GPIO 通道用作输入或输出之前,必须对其进行设置。要将通道配置为输入,请调用
# (where channel is based on the pin numbering mode discussed above) GPIO.setup(channel, GPIO.IN)
要将通道设置为输出,请使用:
GPIO.setup(channel, GPIO.OUT)
还可以指定输出通道的初始值:
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
将通道设置为输出时,也可以同时设置多个通道:
# add as many as channels as needed. You can also use tuples: (18,12,13) channels = [18, 12, 13] GPIO.setup(channels, GPIO.OUT)
5.5 输入
要读取通道的数值,请使用
GPIO.input(channel)
这将返回 GPIO.LOW 或 GPIO.HIGH。
5.6 输出
要设置配置为输出的引脚的值,请使用
GPIO.output(channel, state)
其中状态可以是 GPIO.LOW 或 GPIO.HIGH。
您也可以输出到通道列表或通道元组:
channels = [18, 12, 13] # or use tuples GPIO.output(channels, GPIO.HIGH) # or GPIO.LOW # set first channel to LOW and rest to HIGH GPIO.output(channels, (GPIO.LOW, GPIO.HIGH, GPIO.HIGH))
5.7 清理
程序结束时,最好清理一下通道,以便将所有引脚设置为默认状态。要清理所有使用过的通道,请调用
GPIO.cleanup()
如果不想清理所有通道,也可以清理单个通道或通道列表或通道元组:
GPIO.cleanup(chan1) # cleanup only chan1 GPIO.cleanup([chan1, chan2]) # cleanup only chan1 and chan2 GPIO.cleanup((chan1, chan2)) # does the same operation as previous statement
5.8 Jetson 板信息和库版本
要获取有关 Jetson 模块的信息,请使用/阅读
GPIO.JETSON_INFO
这提供了一个 Python 字典,其中包含以下键值: P1_REVISION、RAM、REVISION、TYPE、MANUFACTURER 和 PROCESSOR。字典中的所有值都是字符串,只有 P1_REVISION 是整数。
要获取有关库版本的信息,请使用/read:
GPIO.VERSION
这将提供一个包含 X.Y.Z 版本格式的字符串。
5.9 中断
除忙轮询外,程序库还提供了三种监测输入事件的方法:
wait_for_edge() 函数
该函数会阻塞调用线程,直到检测到所提供的边沿。该函数的调用方法如下:
GPIO.wait_for_edge(channel, GPIO.RISING)
第二个参数指定要检测的边沿,可以是 GPIO.RISING、GPIO.FALLING 或 GPIO.BOTH。如果只想将等待时间限制在指定时间内,可以选择设置超时:
# timeout is in seconds GPIO.wait_for_edge(channel, GPIO.RISING, timeout=500)
函数返回检测到边沿的通道,如果发生超时则返回 "无"。
event_detected() 函数
该函数可用于定期检查上次调用后是否发生了事件。该函数的设置和调用方法如下:
# set rising edge detection on the channel GPIO.add_event_detect(channel, GPIO.RISING) run_other_code() if GPIO.event_detected(channel): do_something()
和以前一样,您可以检测 GPIO.RISING、GPIO.FALLING 或 GPIO.BOTH 的事件。
检测到边沿时运行回调函数
该功能可用于为回调函数运行第二个线程。因此,回调函数可与主程序同时运行,以响应边缘。该功能的使用方法如下:
# define callback function def callback_fn(channel): print("Callback called from channel %s" % channel) # add rising edge detection GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn)
如有需要,还可添加多个回调,具体如下:
def callback_one(channel): print("First Callback") def callback_two(channel): print("Second Callback") GPIO.add_event_detect(channel, GPIO.RISING) GPIO.add_event_callback(channel, callback_one) GPIO.add_event_callback(channel, callback_two)
在这种情况下,两个回调函数是顺序运行的,而不是并发运行的,因为只有一个线程在运行所有的回调函数。
为了防止多次调用回调函数,将多个事件合并为一个事件,可以选择设置一个延时:
# bouncetime set in milliseconds GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn, bouncetime=200)
后台运行的线程将空闲等待事件,直到超时为止,超时时间可选择设置如下。默认轮询超时为 0.2 秒。当轮询超时时,线程将被唤醒并检查线程状态。如果线程处于运行状态,它将回到空闲状态等待另一个事件,否则,线程将退出(事件检测移除)。这个过程将一直持续到线程处于退出状态。
# polltime set in seconds GPIO.add_event_detect(channel, GPIO.RISING, callback=callback_fn, polltime=1)
如果不再需要边缘检测,可按如下方法将其删除:
GPIO.remove_event_detect(channel)
可以设置超时选项来等待事件检测被移除,否则默认为 0.5 秒。建议删除超时时间至少为轮询时间的两倍。
GPIO.remove_event_detect(channel, timeout=0.5)
5.10 检查 GPIO 通道的功能
此功能可让您检查所提供 GPIO 通道的功能:
GPIO.gpio_function(channel)
函数返回 GPIO.IN 或 GPIO.OUT。
5.11 PWM
有关如何使用 PWM 通道的详细信息,请参见 samples/simple_pwm.py。
Jetson.GPIO 库仅支持带有附加硬件 PWM 控制器的引脚上的 PWM。与 RPi.GPIO 库不同,Jetson.GPIO 库不实现软件模拟 PWM。Jetson Nano 支持 2 个 PWM 通道,Jetson AGX Xavier 支持 3 个 PWM 通道。Jetson TX1 和 TX2 不支持任何 PWM 通道。
系统 pinmux 必须配置为将硬件 PWM 控制器连接到相关引脚。如果未配置 pinmux,PWM 信号将无法到达引脚!为此,Jetson.GPIO 库不会动态修改 pinmux 配置。有关如何配置 pinmux 的详细信息,请阅读 L4T 文档。
六、在 docker 容器中使用 Jetson GPIO 库
下面介绍如何在 docker 容器中使用 Jetson GPIO 库。
6.1 构建 docker 映像
samples/docker/Dockerfile 是 Jetson GPIO 库的示例 Dockerfile。下面的命令将从中构建一个名为 testimg 的 docker 镜像。
sudo docker image build -f samples/docker/Dockerfile -t testimg .
6.2 运行容器
6.2.1 基本选项
要访问 GPIO 引脚,必须将 /dev 映射到容器中。因此,你需要在 docker 容器运行命令中添加这些选项。
--device /dev/gpiochip0 \
如果要使用容器中的 GPU,还需要添加这些选项:
--runtime=nvidia --gpus all
6.2.2 以私有模式运行容器
默认情况下,程序库通过检查 /proc/device-tree/compatible 和 /proc/device-tree/chosen 来确定 jetson 模式。这些路径只能在隐私模式下映射到容器中。
下面的示例将在私有模式下从容器运行 /bin/bash。
sudo docker container run -it --rm \ --runtime=nvidia --gpus all \ --privileged \ -v /proc/device-tree/compatible:/proc/device-tree/compatible \ -v /proc/device-tree/chosen:/proc/device-tree/chosen \ --device /dev/gpiochip0 \ testimg /bin/bash
6.2.3 以非私有模式运行容器
如果不想在私有模式下运行容器,可以通过环境变量 JETSON_MODEL_NAME 直接向库提供自己的 jetson 模型名:
# ex> -e JETSON_MODEL_NAME=JETSON_NANO -e JETSON_MODEL_NAME=[PUT_YOUR_JETSON_MODEL_NAME_HERE]
您可以在主机上或预编译模式下运行 samples/jetson_model.py,获取该变量的正确值。
# run on the host or in previlleged mode sudo python3 samples/jetson_model.py
下面的示例将以非特权模式从容器运行 /bin/bash。
sudo docker container run -it --rm \ --runtime=nvidia --gpus all \ --device /dev/gpiochip0 \ -e JETSON_MODEL_NAME=[PUT_YOUR_JETSON_MODEL_NAME_HERE] \ testimg /bin/bash
七、获取 L4T 文档
L4T 文档可在以下位置获取:
- Jetson 下载中心;搜索 "L4T 文档 "包。
- docs.nvidia.com。
在文档中,可搜索相关话题,如
- 硬件设置。
- 配置 40 针扩展接头。
- Jetson-IO。
- 平台适配和调试
- Pinmux 更改