Jetson Nano Fast GPIO C++ Example with Direct Register Access

Looking for a fast GPIO C++ example using the Direct Register Access (DRA) approach over filesys. Need very fast switching for stepper motor control and other high speed applications. GPIO via DRA has been implemented on the Raspberry Pi with the WiringPi library. Is there a similar DRA library for the Jetson Nano? Direct Register Access would be faster, simpler, and more robust over the slow filesys approach.

[url]RPi GPIO Code Samples - eLinux.org

[url]RPi GPIO Code Samples - eLinux.org

There’s this untested code…

I’d be interested in hearing how that works for you!

Also, there’s some PWM hardware in the Nano, which might be helpful if you need that level of regularity.
I don’t have a good reference for this – all of the NVIDIA documentation is spread out across documents related to each individual sub-part of the SoC, instead of being collected into “here’s all the bits that interact to make a working PWM output,” and I don’t think anyone has collected it all in one public place.

@snarky, thanks for example. I was able to compile it without errors and run it as root. The low level programming and pointer language is a bit advanced. I appreciate the succinctness. But I am not sure how the code works. It appears to be looping through all the pins beginning with “0x6000d100”, and toggles each every second?

This code sample came about become someone else tried to use the memory-based interface without properly mapping the raw memory. The intention is that it flips 8 pins with the base register 0x6000d100, which I don’t actually know which particular pins those are – we’d have to look up the GPIO hardware base address in the data sheet to figure that out. E g, the bit I know works is the “mmap()” part – the actual mapping to the Nano hardware, I took from the non-working code, and just transcribed.
One of these days, I’ll have enough spare time to go back and test these values a little more rigorously.

do you know what PRi number 4 pin will correspond to at jetson nano 40 pin interface within the context of use of
import RPi.GPIO in python addressing? GitHub - NVIDIA/jetson-gpio: A Python library that enables the use of Jetson's GPIOs

@Nvidia personnel, what are the Jetson Nano registers for the 40 GPIO interface pins? I downloaded the datasheet, but this information is not included. I want to implement very fast GPIO with direct register access as with the Raspberry Pi and the WiringPi library. I’m surprised an equivalent does not exist for the Jetson Nano. The community would benefit.

Hi xplanescientist, I believe the register information you are looking for is included in the Tegra X1 (SoC) Technical Reference Manual (TRM) you can download it from here: https://developer.nvidia.com/embedded/downloads#?search=Tegra%20X1

See Chapter 9 “Multi-Purpose I/O Pins”, section 9.13 “GPIO Registers” (table 26: GPIO Register Address Map)

Interestingly, that document talks about ACPI. I presume that should be substituted with “Uboot/Device Tree”?
(Although ACPI seems to be mostly mentioned in the unsupported VGPIO section)

Table 26 seems to be the address map for pad controls? Table 32 shows the register layout for one particular GPIO controller, and Table 1 (in the beginning of the document) shows the address of all the different functions, including GPIO-1, GPIO-2, … GPIO-8.

Table 25 describes the pull-up/down function bits of the pad control, but don’t document the actual values of those bits. Similarly, table 29 shows the pinmux control register names, but no bit values. Is there a register bit mask/map somewhere?

I updated the github code a bit to better reflect the documentation. Still un-tested :-)

https://github.com/jwatte/jetson-gpio-sample

I’m unsure of the details, but apparently these aren’t mutually exclusive: https://lwn.net/Articles/642050/

Hi,
I need to control gpio using java how to do

You can use the device API to open() the appropriate /sys/class/gpio file, and export the pins.
They look just like regular files.

I tried the program, and it partially works. It toggles pin 33 (GPIO_PE6), but only that pin. Much confusion because the program is hardcoded to toggle GPIO address 0x6000d100. But this address is not listed in table 26 of the “Tegra X1 (SoC) Technical Reference Manual”. Table 26’s address for Pin 33 (GPIO_PE6) is 0x700009c8. In fact all the register addresses start with 0x70000. So I’m surprised the program works. Or maybe the program does some shifting. The low level programming is difficult to follow.

It’s unfortunate that NVIDIA does not have a working Direct Register Access (DRA) library as an alternative to the slow filesys approach. Or perhaps NVIDIA is working on it? A jetson version of WiringPi would be huge for the community.

1 Like

This is from the address map of the X1 reference manual, version 1.3p, chapter 2:

Note that the GPIO pins are controlled by gangs – a single GPIO device controls many pins, so there’s not “the address of a pin.”

What the address 0x700009c8 controls is the drive strength of the output buffer for the pad. The way I read the documentation, that pad can be driven by one of many peripherals, or by the GPIO registered controller. There’s a bit in the GPIO control hardware that seems to mean “drive the pad by GPIO unconditionally, shunt away all the peripherals,” and the code I posted sets that bit. The code I posted does not change the drive strength from what it was configured to by default, though.

It’s possible, for example, that the default configuration for some pads is TRISTATE which would prevent the GPIO signal from actually being seen on that pin.
A robust GPIO library would have to re-configure all bits of GPIO control registers, special-purpose peripherals, pad drivers, and pinmux, all at once, to work “as the user would expect.”
It might even need some kind of kernel-level lock to ensure that no two user-level processes tried to write to the same control registers at the same time.

@snarky, thanks for the example.
I was looking for the way to access GPIOs faster than using sysfs.
I tried your example, and it works well, pin 33 (GPIO_PE6) toggles.

Then I have one question.
Where can I find the mapping of controller-port with physical GPIO pins which are on Jetson Nano?

In your example code, it seems you specify 2nd controller(GPIO_2: 0x6000d100) and 0th port(pin->OUT[0] etc), and then pin 33 (GPIO_PE6) works.
For example, assume that I want to access pin 31(GPIO_PZ0) or pin 36(UART2_CTS), which pair controller and port should I use?

I tried all the addresses in the example code. Only 3 toggle some jetson nano pins. See below for results. However, attempting to toggle other addresses caused linux to seize in seconds. I had to reboot several times. Pin 33 seemed the most robust - it toggled without trouble for hours.

define GPIO_1 0x6000d000
define GPIO_2 0x6000d100
define GPIO_3 0x6000d200 // jetson pin 33 (GPIO_PE6)
define GPIO_4 0x6000d300 // seizure
define GPIO_5 0x6000d400 // seizure
define GPIO_6 0x6000d500 // jetson serial port header, RXD pin
define GPIO_7 0x6000d600 // jetson pin 15 (LCD_TE)
define GPIO_8 0x6000d700

Clearly a robust GPIO Direct Register Access library is needed here.

@snarky, so if each address controls a gang of pins, how is a single pin toggled in your code? How do we toggle the other jetson pins?

By reading the code, it should be obvious that it’s trying to toggle 8 pins at a time. (0xff)
I have no idea where those pins map; I haven’t looked this up in the pinmux spreadsheet, because doing so is quite time consuming.
A number of other peripherals are also controlled by all the GPIO pins, everything from power control to camera blanking, so toggling random pins is likely to cause problems!

So, there are two things that need to be done to make the code a drop in “easy to use” solution, rather than just an illustration:

  1. work around the pinmux problem, where apparently many pins on the GPIO header aren’t mapped such that the GPIO hardware can reach them
  2. map the particular GPIO header pins to specific controllers-and-bit-masks so that only the GPIO you’re interested in will be toggled

Note that there is some additional hardware that allows you to “set” or “clear” a GPIO through a single write, in isolation of the other GPIOs. Thus, the read-modify-write operation of the 8-bit register isn’t needed, which avoids race conditions that could otherwise happen.

Also, for a really good library, we’d also need:

  1. some shared mechanism for “locking” access to pins so that two programs/users of the library don’t try to fight over the pin

For your information, @valentis updated @snarky’s gpio example with more relevant addresses.

https://github.com/valentis/jetson-nano-gpio-example

I tried the “switch.cpp” example, and it works. Many of the addresses are mapped out. Below is an excerpt from “gpionano.h” by @valentis.

Question: how can we access the other Jetson Nano header pins with this Direct Register Access method?

//  The only address we really need
#define GPIO_216      0x6000d60C    // Jetson Nano  7[AUDIO_MCLK]
#define GPIO_50       0x6000d108    // Jetson Nano 11[UART2_RTS]
#define GPIO_194      0x6000d600    // Jetson Nano 15[LCD_TE]
#define GPIO_14       0x6000d004    // Jetson Nano 13[SPI2_SCK]
#define GPIO_16       0x6000d008    // Jetson Nano 19[SPI1_MOSI]
#define GPIO_38       0x6000d100    // Jetson Nano 33[GPIO_PE6]
#define GPIO_77       0x6000d204    // Jetson Nano 38[I2S4_SDIN] // J

// From https://github.com/leahneukirchen/linux-jetson-tk1/blob/master/drivers/gpio/gpio-tegra.c
#define GPIO_INT_LVL_MASK		0x010101
#define GPIO_INT_LVL_EDGE_RISING	0x000101
#define GPIO_INT_LVL_EDGE_FALLING	0x000100
#define GPIO_INT_LVL_EDGE_BOTH		0x010100
#define GPIO_INT_LVL_LEVEL_HIGH		0x000001
#define GPIO_INT_LVL_LEVEL_LOW		0x000000

This open source have issue. I clone and build successfully source code. But It have issue happen.

  1. I reboot jetson nano → ./led —> Not working
  2. I reboot jetson nano → echo 1 > value (gpio38 Pin 33 on Jetson nano) —> led turn on —> After I run command ./led → working fine and bink led

I’d like to ask a question about what is issue?

Thanks you.

There are too many port # assignment errors in official documentation: 1) NVIDIA Jetson Nano Developer Kit Carrier Board P3449_B01
Specification SP-09732-001_v1.1 and 2) Jetson Nano 2GB Developer Kit User Guide (https://developer.nvidia.com/embedded/learn/jetson-nano-2gb-devkit-user-guide). In one or both of the above documents, the following pairs are mapped to the same SoC pin (e.g., PB.05): (J41 Header Pin 21, 26 → PC.01) , (Pin22, 27 → PB.05) , (Pin23, 28 -->PC.02) and (Pin24, 29 → PC.03). At least one mapping in each of the above pairs must be wrong. Since there are too many documentation errors for 40 pins of J41, confidence on correctness of the document cannot be high. May NVidia please provide correct mapping for the above pairs and also confirm if the pin to port mapping for other pins are correct?

Thanks!

1 Like