1,简介
NVIDIA Jetson Nano 是一款紧凑的板卡,尺寸约为 70x45 毫米,配有一个大型散热器。它搭载了一颗非常强大的微芯片,即 NVIDIA Tegra X1,其中包含四个 ARM 应用处理器核心,以及 128 个图形处理单元(GPU)。GPU 不仅用于图形处理,因为如今与神经网络相关的一些有趣的应用,例如模式识别,随着处理单元数量的增加,可以更轻松地创建,并且性能更好。Tegra X1 的全版本(具有两倍 GPU 核心)设计用于一些非常多样的应用,包括游戏,但也用于汽车领域,可以支持自动停车,多摄像头视频拼接以及驾驶辅助功能(如车道控制、乘客避免和驾驶员警觉性监控)。
Jetson Nano 采用了小外形双列直插式内存模块(SO-DIMM)封装形式,已经插在一个紧凑的 100x80 毫米载板上,两者共同构成了开发套件。载板上有许多典型的计算机接口,如 USB 3.0、千兆以太网和视频接口(支持 4K 60fps!)。还有一些连接器非常类似于树莓派上的连接器。有一个 40 针扩展连接器,布局几乎与树莓派的连接器相同。还支持树莓派相机。
设备如下图:
粉色标记为连接器的引脚1。连接器 J44 上有一个板载串行控制台。第三方 USB-UART 适配器用于将其连接到 PC。颜色显示了 Adafruit USB-UART 接线和 FTDI USB-UART 接线。这里有一个用于自制 USB-UART 适配器的电路:构建 USB UART 串行适配器,它使用 FTDI 接线颜色约定(从 PC 端的视角看,黄色、橙色和黑色分别用于 RXD、TXD 和 GND)。
当将东西安装在机箱内时,按钮标头将非常方便。我希望将一个按钮连接到“ON”引脚,并在 DIS 引脚上跨接短接跳线,以禁用自动启动。
请注意,当从电源插座的桶接口供电 Jetson Nano 时,需要在 J48 上放置短接跳线。否则,开发板将期望从 micro USB 获取电源。
J41 扩展头引脚定义
Linux(BCM)列包含两个数字。括号中的数字是类似于树莓派的 GPIO 编号,因此可以使用相同的 GPIO 库。
GPIO详细信息如下:
J41引脚列表汇总:
GPIO (BCM) | 名称 | 引脚 | 引脚 | 名称 | GPIO (BCM) |
---|---|---|---|---|---|
3.3V输出 | 1 | 2 | 5.0V 输出 | ||
I2C_2_SDA I2C Bus 1 | 3 | 4 | 5.0V 输出 | ||
I2C_2_SCL I2C Bus 1 | 5 | 6 | GND | ||
GPIO216 (D4) | AUDIO_MCLK | 7 | 8 | UART_TX2 /dev/ttyTHS1 | |
GND | 9 | 10 | UART_RX2 /dev/ttyTHS1 | ||
GPIO50 (D17) | UART_2_RTS | 11 | 12 | I2S_4_SCLK | GPIO79 (D18) |
GPIO232 (D23) | SPI_2_SCK | 13 | 14 | GND | |
GPIO194 (D22) | LCD_TE | 15 | 16 | SPI_2_CS1 | GPIO232 (D23) |
3.3V输出 | 17 | 18 | SPI_2_CS0 | GPIO15 (D24) | |
GPIO16 (D10) | SPI_1_MOSI | 19 | 20 | GND | |
GPIO17 (D9) | SPI_1_MOSO | 21 | 22 | SPI_2_MISO | GPIO13 (D23) |
GPIO18 (D11) | SPI_1_SCK | 23 | 24 | SPI_1_CS0 | GPIO19 (D8) |
GND | 25 | 26 | SPI_1_CS1 | GPIO20 (D7) | |
I2C_1_SDA I2C Bus 0 | 27 | 28 | I2C_1_SCL I2C Bus 0 | ||
GPIO149 (D5) | CAM_AF_EN | 29 | 30 | GND | |
GPIO200 (D6) | GPIO_PZ0 | 31 | 32 | LCD_BL_PWM | GPIO168 (D12) |
GPIO38 (D13) | GPIO_PE6 | 33 | 34 | GND | |
GPIO76 (D19) | I2S_4_LRCK | 35 | 36 | UART_2_CTS | GPIO51 (D16) |
GPIO12 (D26) | SPI_2_MOSI | 37 | 38 | I2S_4_SDIN | GPIO77 (D20) |
GND | 39 | 40 | I2S_4_SDOUT | GPIO78 (D21) |
J41 GPIO 详细信息:
Jetson Nano J41 GPIO Expansion Header Mapping (derived from Jetson.GPIO data) | ||||||
---|---|---|---|---|---|---|
Connector Pin # | RPi Connector Label | RPi Default SoC Pull | Module connector signal name | Linux GPIO Within Chip | Assumed Linux GPIO Chip Base | Linux GPIO |
1 | 3V3 | |||||
2 | 5V0 | |||||
3 | SDA1 | High | ||||
4 | 5V0 | |||||
5 | SCL1 | High | ||||
6 | GND | |||||
7 | GPIO_GCLK | High | GPIO9 | 216 | 0 | 216 |
8 | TXD0 | Low | ||||
9 | GND | |||||
10 | RXD0 | Low | ||||
11 | GPIO_GEN0 | Low | UART1_RTS | 50 | 0 | 50 |
12 | GPIO_GEN1 | Low | I2S0_SCLK | 79 | 0 | 79 |
13 | GPIO_GEN2 | Low | SPI1_SCK | 14 | 0 | 14 |
14 | GND | |||||
15 | GPIO_GEN3 | Low | GPIO12 | 194 | 0 | 194 |
16 | GPIO_GEN4 | Low | SPI1_CS1 | 232 | 0 | 232 |
17 | 3V3 | 0 | ||||
18 | GPIO_GEN5 | Low | SPI1_CS0 | 15 | 0 | 15 |
19 | SPI_MOSI | Low | SPI0_MOSI | 16 | 0 | 16 |
20 | GND | |||||
21 | SPIO_MISO | Low | SPI0_MISO | 17 | 0 | 17 |
22 | GPIO_GEN6 | Low | SPI1_MISO | 13 | 0 | 13 |
23 | SPI_SCLK | Low | SPI0_SCK | 18 | 0 | 18 |
24 | SPI_CE0_N | High | SPI0_CS0 | 19 | 0 | 19 |
25 | GND | |||||
26 | SPI_CE1_N | High | SPI0_CS1 | 20 | 0 | 20 |
27 | ID_SD | |||||
28 | ID_SC | |||||
29 | GPIO5 | High | GPIO01 | 149 | 0 | 149 |
30 | GND | |||||
31 | GPIO6 | High | GPIO11 | 200 | 0 | 200 |
32 | GPIO12 | Low | GPIO07 | 168 | 0 | 168 |
33 | GPIO13 | Low | GPIO13 | 38 | 0 | 38 |
34 | GND | |||||
35 | GPIO19 | Low | I2S0_FS | 76 | 0 | 76 |
36 | GPIO16 | Low | UART1_CTS | 51 | 0 | 51 |
37 | GPIO26 | Low | SPI1_MOSI | 12 | 0 | 12 |
38 | GPIO20 | Low | I2S0_DIN | 77 | 0 | 77 |
39 | GND | |||||
40 | GPIO21 | Low | I2S0_DOUT | 78 | 0 | 78 |
2,GPIO 控制
方法1 sysfs
79指的是Linux sysfs GPIO的gpio79。
从上列表格中我们看到Jetson Nano J41的Pinout,可以看到gpio79实际上是pin 12
# Map GPIO Pin
# gpio79 is pin 12 on the Jetson Nano
$ echo 79 > /sys/class/gpio/export
# Set Direction
$ echo out > /sys/class/gpio/gpio79/direction
# Bit Bangin'!
$ echo 1 > /sys/class/gpio/gpio79/value
$ echo 0 > /sys/class/gpio/gpio79/value
# Unmap GPIO Pin
$ echo 79 > /sys/class/gpio/unexport
# Query Status
$ cat /sys/kernel/debug/gpio
GPIO 查看:
方法2,使用NVIDIA官方提供的JetsonGPIO库
关于该函数库的具体说明,你可以在Jetson.GPIO · PyPI ()https://pypi.org/project/Jetson.GPIO/中了解
环境配置和安装库
安装PIP工具:
sudo apt-get update
sudo apt-get install python-pip
sudo apt-get install python3-pip
下载安装Jetson.GPIO库,jetson nano原版本系统自带,但是也可以直接pip安装或者官网下载源代码安装:
# pip直接安装
sudo pip install Jetson.GPIO
sudo pip3 install Jetson.GPIO
# 或者下载代码进行安装
sudo python3 setup.py install
设置用户权限,为了使用Jetson GPIO库,必须首先设置正确的用户权限/组。创建新的gpio用户组。然后将用户添加到新创建的组中。:
sudo groupadd -f -r gpio
sudo usermod -a -G gpio your_user_name
注意:这里的your_user_name需要改成你自己的账号名,不然库无法正常使用
将99-gpio.rules文件复制到rules.d目录
如果是将源代码下载到Jetson.GPIO:
sudo cp lib/python/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/
如果是使用pip安装的Jetson.GPIO,则在虚拟环境中使用pip:
sudo cp venv/lib/pythonNN/site-packages/Jetson/GPIO/99-gpio.rules /etc/udev/rules.d/
重载rules规则来让文件生效,最后需要通过运行以下命令重新启动或重新加载udev规则:
sudo udevadm control --reload-rules && sudo udevadm trigger
例子使用
针对jetson.gpio库,官方也提供了一些简单的例程,例程存放在 /opt/nvidia/jetson-gpio/samples/路径下
cd /opt/nvidia/jetson-gpio/samples/
在samples目录下,示例程序如下:
应用程序功能如下:
simple_input.py
:该应用程序使用 BCM 引脚编号模式,并读取 40 引脚头部的引脚 12 处的值,并将该值打印到屏幕上。simple_out.py
:该应用程序使用树莓派的 BCM 引脚编号模式,并在 BCM 引脚 18 处(或头部的引脚 12 处)每 2 秒输出交替的高低值。button_led.py
:该应用程序使用板上引脚编号。它需要将按钮连接到引脚 18 和 GND,通过上拉电阻将引脚 18 连接到 3V3,以及将 LED 和限流电阻连接到引脚 12。该应用程序读取按钮状态,并在每次按下按钮时将 LED 保持亮1秒。button_event.py
:该应用程序使用板上引脚编号。它需要将按钮连接到引脚 18 和 GND,通过上拉电阻将按钮连接到 3V3,以及将 LED 和限流电阻连接到引脚 12。该应用程序执行与button_led.py
相同的功能,但是为了减少 CPU 使用率,它会对按钮按下事件进行阻塞等待,而不是连续检查引脚的值。button_interrupt.py
:该应用程序使用板上引脚编号。它需要将按钮连接到引脚 18 和 GND,通过上拉电阻将按钮连接到 3V3,以及将 LED 和限流电阻连接到引脚 12,将第二个 LED 和限流电阻连接到引脚 13。该应用程序持续缓慢闪烁第一个 LED,并且仅当按下按钮时快速闪烁第二个 LED 五次。
方法3,使用C++ GPIO库
1. 下载库:
git clone https://github.com/pjueon/JetsonGPIO
2. 编译:
cd JetsonGPIO
mkdir build && cd build
cmake .. [OPTIONS]
Option | Default value | Description |
---|---|---|
-DCMAKE_INSTALL_PREFIX= | /usr/local | Installation path |
-DBUILD_EXAMPLES= | ON | Build example codes in samples |
-DJETSON_GPIO_POST_INSTALL= | ON | Run the post-install script after installation to set user permissions. If you set this OFF , you must run your application as root to use JetsonGPIO. |
3. 安装库
sudo cmake --build . --target install
4. 编译Samples
你可以在构建目录中添加 cmake 选项 -DBUILD_EXAMPLES=ON 来构建 samples 目录中的示例代码。假设你当前在 build 目录下:
cmake .. -DBUILD_EXAMPLES=ON
cmake --build . --target examples
5,写一个简单例子测试
#include <iostream>
#include <chrono>
#include <thread>
#include <signal.h>
#include <JetsonGPIO.h>
using namespace std;
static bool end_this_program = false;
inline void delay(int s)
{
this_thread::sleep_for(chrono::seconds(s));
}
void signalHandler(int s)
{
end_this_program = true;
}
int main()
{
signal(SIGINT, signalHandler);
int output_pin = 26; // BOARD pin 12, BCM pin 18
GPIO::setmode(GPIO::BCM);
GPIO::setup(output_pin, GPIO::OUT, GPIO::HIGH);
cout << "Strating demo now! Press CTRL+C to exit" << endl;
int curr_value = GPIO::HIGH;
while (!end_this_program)
{
delay(3);
cout << "Outputting " << curr_value << " to pin ";
cout << output_pin << endl;
GPIO::output(output_pin, curr_value);
curr_value ^= GPIO::HIGH;
}
GPIO::cleanup();
return 0;
}
编译方法:
# g++ simple_out.cpp -o simple_out -L./ -lJetsonGPIO -lpthread
运行结果:
方法4,使用驱动方法
1,修改设备树:
platform/t210/porg/kernel-dts/tegra210-porg-p3448-common.dtsi
gpio_try {
compatible = "gpio-try";
status = "okay";
input-gpio = <&gpio TEGRA_GPIO(Z, 0) GPIO_ACTIVE_HIGH>;
output-gpio = <&gpio TEGRA_GPIO(B, 6) GPIO_ACTIVE_LOW>;
};
2,写一个字符驱动
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define GPIO_BASE_CNT 1
#define GPIO_BASE_NAME "gpiotry"
struct gpiobase_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int gpioIdInput;
int gpioIdOutput;
};
struct gpiobase_dev gpioBaseDev;
static int gpiobase_open(struct inode *inode, struct file *filp)
{
filp->private_data = &gpioBaseDev;
return 0;
}
static ssize_t gpiobase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
struct gpiobase_dev *dev = filp->private_data;
if (cnt > 1) {
printk("read length err!\r\n");
return -EFAULT;
}
int gpioValue = gpio_get_value(dev->gpioIdInput);
if (gpioValue < 0) {
printk("get gpio value failed!\r\n");
return -EFAULT;
}
char databuf = gpioValue ? 1 : 0;
int ret = copy_to_user(buf, &databuf, cnt);
if(ret < 0) {
printk("kernel read failed!\r\n");
return -EFAULT;
}
return 0;
}
static ssize_t gpiobase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
struct gpiobase_dev *dev = filp->private_data;
if (cnt > 1) {
printk("write length err!\r\n");
return -EFAULT;
}
unsigned char databuf;
int ret = copy_from_user(&databuf, buf, cnt);
if(ret < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
int writeValue = databuf ? 1 : 0;
gpio_set_value(dev->gpioIdOutput, writeValue);
return 0;
}
static int gpiobase_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations gpiobase_fops = {
.owner = THIS_MODULE,
.open = gpiobase_open,
.read = gpiobase_read,
.write = gpiobase_write,
.release = gpiobase_release,
};
static int __init gpiobase_init(void)
{
gpioBaseDev.nd = of_find_node_by_path("/gpio_try");
if(gpioBaseDev.nd == NULL) {
printk("gpioBaseDev node cant not found!\r\n");
return -EINVAL;
}
else {
printk("gpioBaseDev node has been found!\r\n");
}
gpioBaseDev.gpioIdInput = of_get_named_gpio(gpioBaseDev.nd, "input-gpio", 0);
if(gpioBaseDev.gpioIdInput < 0) {
printk("can't get input-gpio");
return -EINVAL;
}
printk("input-gpio num = %d\r\n", gpioBaseDev.gpioIdInput);
gpioBaseDev.gpioIdOutput = of_get_named_gpio(gpioBaseDev.nd, "output-gpio", 0);
if(gpioBaseDev.gpioIdOutput < 0) {
printk("can't get output-gpio");
return -EINVAL;
}
printk("output-gpio num = %d\r\n", gpioBaseDev.gpioIdOutput);
int ret = gpio_direction_input(gpioBaseDev.gpioIdInput);
if(ret < 0) {
printk("can't set input gpio!\r\n");
}
ret = gpio_direction_output(gpioBaseDev.gpioIdOutput, 0);
if(ret < 0) {
printk("can't set output gpio!\r\n");
}
if (gpioBaseDev.major) {
gpioBaseDev.devid = MKDEV(gpioBaseDev.major, 0);
register_chrdev_region(gpioBaseDev.devid, GPIO_BASE_CNT, GPIO_BASE_NAME);
} else {
alloc_chrdev_region(&gpioBaseDev.devid, 0, GPIO_BASE_CNT, GPIO_BASE_NAME);
gpioBaseDev.major = MAJOR(gpioBaseDev.devid);
gpioBaseDev.minor = MINOR(gpioBaseDev.devid);
}
printk("gpioBaseDev major=%d,minor=%d\r\n",gpioBaseDev.major, gpioBaseDev.minor);
gpioBaseDev.cdev.owner = THIS_MODULE;
cdev_init(&gpioBaseDev.cdev, &gpiobase_fops);
cdev_add(&gpioBaseDev.cdev, gpioBaseDev.devid, GPIO_BASE_CNT);
gpioBaseDev.class = class_create(THIS_MODULE, GPIO_BASE_NAME);
if (IS_ERR(gpioBaseDev.class)) {
return PTR_ERR(gpioBaseDev.class);
}
gpioBaseDev.device = device_create(gpioBaseDev.class, NULL, gpioBaseDev.devid, NULL, GPIO_BASE_NAME);
if (IS_ERR(gpioBaseDev.device)) {
return PTR_ERR(gpioBaseDev.device);
}
return 0;
}
static void __exit gpiobase_exit(void)
{
cdev_del(&gpioBaseDev.cdev);
unregister_chrdev_region(gpioBaseDev.devid, GPIO_BASE_CNT);
device_destroy(gpioBaseDev.class, gpioBaseDev.devid);
class_destroy(gpioBaseDev.class);
}
module_init(gpiobase_init);
module_exit(gpiobase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("han");
编译:
make ARCH=arm64
测试:
打开设备,通过操控设备操控GPIO