Why i receive data in /ttyTHS1 while i connect external device to /ttyTHS2 in Jetson NANO?

Hi. So the long story short:
I use Nano as a logic centre for a few microcontrollers, for testing and self-studying processes. So I’ve decided to connect them by UARTS. Luckily, Nano has enough.

The Problem:

  1. Custom application on Linux starts with sending a request via ttyTHS2 to STM32 for any data;

  2. STM32 sends whatever it has, in a loop;

  3. !HERE!
    When the data arrives, Nano checks the ttyTHS2 port, but it is stuck expecting at least something.
    I`ve checked all cords with FTDI, so I am sure the data comes as supposed. Turned out incoming data comes to the ttyTHS1 for some reason!
    The second misunderstanding occurred when I decided to reconnect STM to ttyTHS1 alone. For some reason, Nano got stuck in the reboot sequence until all cords are disconnected from UART1(ttyTHS1).

Ive been searching for a solution for a while, so Im leaving as much info as i can anticipate your requests.


  1. Nano DEV kit (jetson-io-base-A)
    L4T: 32.6.1
    JetPack: 4.6
    Ubuntu: 18.04.6 LTS

  2. Yes, i have used:
    “system stop nvgetty.services”
    “system disable nvgetty.services”
    I checked nvgetty with “system status nvgetty.services” → Inactive as the result. Moreover, i changed in /nvgetty.sh line with ttyTHS1 to ttyTHS0;

  3. “dmesg | grep THS” shows a completely usual picture:

  4. “ls -l /dev/ttyTHS*” shows:

  5. Yes, my account “dev” has access to “dialout” and “tty” groups;

  6. Application: ports init works fine. I have made a lot of error handlers with custom print() so I am sure this behaviour wasn`t my fault;

A regular user should not be in group tty.

Most of what you have described is the correct procedure. However, at the Jetson side, is it possible for you to run a serial console program on that TTY? Combine this with unplugging the cable at the remote end, and shorting the TX and RX (which puts this in loopback mode)? If this uses flow control, then you’d also short CTS/RTS. You could then type in the terminal and should see an exact copy of what you type echo back. If there is an error, then you could send a known pattern and see if you can spot a repeating error, versus random. For example, you could send a repeated 0x55 (character ‘U’, or put the terminal in hex mode and send from a file, compare hex echo).

There would be a very important bit of information if you have a predictable error versus random error.

Also, how long is the cable? Is it shielded? Are you certain both sides use a 3.3V level?

Hi, i`m glad for your answer!.

  1. I deleted dev from the tty-group. Just to be sure in the future;
  2. Shorting:
    Sending via echo -ne ‘01234’ > /dev/ttyTHS* and capturing with sudo putty -serial /dev/ttyTHS*
    UART2 TX to RX (loop):
    → FAIL (checked for both TX-s)
    UART1 TX to RX (loop):
    → OK (only from itself);
  3. I don’t use flow control. Not enough incoming data and i don’t mind to lose some of them;
  4. About wires: Classic futaba-style connectors for 40-pins module. I forgot its technical name :)
    Length is ±20cm, not shielded.
  5. STM is powered by micro-USB for Debug reasons, like Nano. So they are connected by GND in addition to TX/RX.

Did you literally use “/dev/ttyTHS*” with echo? This won’t work, I was just suggesting to use this with “ls”, and it is good for listing all serial devices with the NVIDIA “Tegra High Speed” driver (which produces files of the naming convention “/dev/ttyTHS#”, if “#” is a number). Using it with echo would cause an error. *I do not know which device is your actual UART, but I will pretend for the example below it is “/dev/ttyTHS2”. Adjust to use the correct device.

Note that when you list “ls -l /dev/ttyTHS*”, that anything group “dialout” is wrong for you to use because this is a serial console port (it could be reverted to group “tty” by disabling serial console…although you have disabled serial console I mention this because a lot of people don’t know this). Any UART with a “dialout” group is free to use (unless you’ve already added something to use that).

Rather than using “echo” could you try with a serial console program? I personally like gtkterm (“sudo apt-get install gtkterm”). Example:
gtkterm -b 8 -t 1 -s 115200 -p /dev/ttyTHS2

In the above it uses 8 bits, 1 stop bit, speed 115200, and no parity. A serial console application is capable of sending some of the IOCTL commands, so it can do more than echo. In your case what speed and other settings are you using? Pick whatever your external device is expected to use. Then try typing in text and see if it echos back.

Hi, @linuxdev.
Apologies for the long absence!
You were right. My initial check was relatively ineffective…
This time, i used your setup:
Jetson NANO:

gtkterm -b 8 -t 1 -s 115200 -p /dev/ttyTHS# , # = 1,2

PC with Termite 3.4+TTL (easier than modifying STM code):

(sending string) 12345

What i got:

  1. ttyTHS2 physical connection

gtkterm: ttyTHS2 → empty
gtkterm: ttyTHS1 → gets “12345” / and whatever i typed to ttyTHS1 i received in Termite3.4

  1. ttyTHS1 physical connection

gtkterm: ttyTHS2 → empty
gtkterm: ttyTHS1 → Termite3.4 catches interesting msgs (screenshot)

===== UPDATE =====
I’ve been playing around for some time and found where goes data intended for ttyTHS1. Turned out, data was captured in ttyS0. I do not know if it IS the correct behaviour, or not, so it would be nice to get feedback).
Correct me if I’m wrong, but i thought each UART# has its own file (ttyS# & ttyTHS# for specific UART[12]). So it isn’t obvious to me, why my data shuffles between files and how can i fix it.

Note that each UART is compatible with legacy drivers (naming “ttyS#”). However, the UARTs on the Jetson also have DMA capability if the “Tegra High Speed” driver is used (naming “ttyTHS#”). In reality there is one device file for each UART driver.

It is unusual for a single device to have multiple drivers (and it is undefined what happens if you mix them), but if you look at the device tree for the UARTs (and in fact many hardware devices using the device tree) there is a node “compatible”. That node identifies which driver(s) work with that hardware. This is a comma delimited list, and so since both drivers show up, then either driver can be used.

The reason this is important is historically due to serial console in bootloaders. These bootloaders typically always have legacy drivers, but without porting, there won’t be any DMA-capable THS driver available in early boot stages. If one considers the boot stages as a mini-operating system with the single job of killing itself via overwriting with the Linux kernel, then being able to continuously log boot requires not resetting the hardware driver state in the middle of its use. Thus a serial console is normally used purely with the legacy driver, while other UARTs are typically used with the THS driver. Both belong to the same UART. Just don’t use both…pick one and stick to it.

Note: If you were to remove one of the drivers in the device tree “compatible” node, then the related tty would go away.

I read about ttyS# and ttyTHS# origins in one of your answers, but it`s kind of unobvious.

My code uses 2 ports UART1-2 simunteneously, therefor i left 2 choices “/dev/ttyTHS1-2”. Each port(fd) initialization is almost immediate with my application. The init runs smooth but in the end, UART2 sends data to UART1 file and UART1 sends to UART0.

I believe “device-tree” refers to ‘/proc/device-tree’.
For UART0serial@7…6000 → compatibles are: nvidia, tegra114-hsuart | …, tegra210-uart | …, tegra20-uart.
For UART1serial@7…6040 → compatibles are: nvidia, tegra114-hsuart.
For UART2serial@7…6200 → compatibles are: nvidia, tegra114-hsuart.

So what am i supposed to comment out? UART0 all that is not simmilar to UART1-2&

You cannot comment out something from “/proc/device-tree”, at least not directly. This is just the kernel pretending to be a directory tree, and is in RAM, reflecting the current tree, but not capable of being used to modify the tree.

I would not recommend actually commenting out the “compatible” part of the tree for one of the drivers, but simply use only one. If you do have the “FDT” key/value pair in “/boot/extlinux/extlinux.conf”, then this will point at the particular device tree file somewhere in “/boot”. This file could be reverse compiled into source, have the driver you don’t want removed, and then placed back in the correct place of “/boot” (in reality I’d recommend that if you chose this path to leave the original and place a tree with a similar, but slightly different name, back in place and editing the extlinux.conf entry for FDT to point at the alternate name). If there is no FDT entry, then the tree is coming from a partition, and you’d need to use a flash tool to flash the modified partition.

Also, beware that in some cases the module documents are zero-based with respect to naming components (such as UARTs), but 1-based for other documentation. In some cases what you see as UART2 going to UART1 might be the same UART, but just a different name due to different documentation. The acid test as to whether or not it is the same UART is if the physical address is the same. In the “/proc/device-tree” content for a UART you’ve already seen the notation:

serial@7…6040 ...
serial@7…6200 ...

That “@7......” is the physical hexadecimal address. Regardless of whether you call it UART1 or UART2 a single address is a single UART.

Ok, so i have decompiled /boot/dtb/tegra210-p3448-0000-p3449-0000-b00.dtb and created similar file ./tegra210-p3448-0000-p3449-0000-b00-custom.dtb then changed FDT in ./extlinux.conf to refer newly created/modified file. The only modification was in serial@70006000::compatible. Specifically, i left ONLY “nvidia,tegra114-husart” driver like the other serial ports. Knowing that there are no ttyTHS0, at least on my dev kit, i was interested in further UART0<->UART1<->UART2 behaviour. But the result was the same…

PC to UART2: data is received only in tthTHS1, ttyTHS2 was empty;
PC to UART1: ttyTHS1 obviously does not react and gtkterm gets “Control signal read: Input/output error” on ttyS0, where the data was earlier

I mean, i don’t have enough experience in Linux but it looks like the problem may be in Pins usage. Is it possible to redefine the target tty-file for every UART-pins?

If the non-THS driver is removed from a UART, then the ttyS# syntax file should go away, but this won’t help you. I’ll also suggest that those are the drivers allowed (the “compatible” is a list of candidate drivers for that device at that physical address). Removing the non-THS driver would have no effect on whether or not the THS driver works (although it would eliminate the non-THS driver from running).

There is one corner case I do not know about: If the legacy ttyS# driver is run during boot stages, then the state of the UART would still remain as Linux loads unless Linux resets the state. Not sure if this would leave a ttyS# in Linux if the device is removed in device tree, but it would prevent that legacy driver from loading if the UART did not already have that driver running; parts of the device tree are in fact read during bootloader stages, and I do not know if the legacy sticks around in Linux when loaded at earlier stages and passed to Linux, but left “not compatible” during Linux device tree load.

If you have an error talking to ttyTHS1, then something else is wrong, it isn’t failing because a ttyS# exists as well (they can both happily exist at the same time, but it would be undefined what happens with data if both are used simultaneously). I think that you are correct that you have the wrong pins for ttyTHS1. That or the UART does not have power rails up.

The realm of assigning which pins work for which function is generally the device tree, and for a Jetson, this is normally generated via the “PINMUX spreadsheet” macros (you can download this for your particular Jetson’s L4T release if this is a dev kit; third party carrier boards differ, but you are using a dev kit). However, reverse compile, edit, and recompile of the existing device tree (which you did) can be easier than regenerating and installing an entire tree from scratch.

Someone from NVIDIA would have to verify your combination of pins versus which /dev/ttyTHS# is used.

Correct me if i am wrong, but as far as i can see, it’s better to activate ttyS0 back. At least for DEBUG functionality and at best i won’t meet any following troubles.

Speaking of my main situation, i highly doubt about power level, because STM and NANO are connected via 3 wires (TX/RX/GND). While both devices are powered separetly. That leaves Linux pins/tty targets for every UART.

I will try to mix pins and hope Nvidia officials will join us :)

Yes, NVIDIA would have to comment on whether the particular ttyTHS1 and the pins you used are correct, but when I mention power, it isn’t due to power draw; it is instead because device tree has a lot of influence on power rails, e.g., if a particular device is not used, then often the device tree can remove power from the rails of that device for energy savings.

Perhaps. By the way, let’s assume i was wrong in terms of the UART-file system. Although UART# pins are able to be redefined, assume there are only 2 ports to use (ttyS0 and ttyTHS1).

Is it possible to use ttyS0 as dialout port? It sounds obvious, but i can’t find any constant method to translate from tty to dialout. Because at the moment ttyS0 sends debug msgs, like 'Password: / Loggin: / and stuff'.

Well, i found one thread. Lucky for me, it’s exactly what i need!

The only things i need to clarify are:

  1. Is it possible to mix ports ttyS0 with ttyS2?
  2. Change in tegra210-…-b00.dtb(.dts) → the one that is referd in extling.conf?
  3. What troubles i can get in the observable future? :)

If ttyS0 goes to a different UART than that which ttyS2 goes to, then both are independent UARTs and both can operate simultaneously using different data wires. It won’t matter if it is the legacy driver or the THS driver which the device uses.

As for use as “dialout”, this is just the name of the group default permissions offer access to. When the group is “tty”, then it means console has taken ownership and this is a login UART for serial console (even regular consoles, non-serial, have group tty once the login software is bound to the port).

Yes, the FDT key/value pair referred to in extlinux.conf is the one with the device tree you are interested in if you need to modify. Example syntax for editing:

# Copy to source:
dtc -I dtb -O dts -o source.dts /boot/some/tree.dtb
# Then edit "source.dts".
# Then convert this to some alternate name in binary to preserve the original:
dtc -I dts -O dtb -o original_name_modified.dtb

Once you have the modified dtb you could just replace the original, but that’s a risky thing to do. You are far better off to:

  1. Keep the original dtb.
  2. Add an extra boot entry to “extlinux.conf” which is nearly a match to the original, but names a new FDT.

This is an example, and your boot entry will differ, but this will illustrate. If the original boot entry is:

DEFAULT primary

LABEL primary
      MENU LABEL primary kernel
      LINUX /boot/Image
      FDT /boot/original.dtb
      INITRD /boot/initrd
      APPEND ${cbootargs}

Then you could edit to become this:

DEFAULT update

LABEL update
      MENU LABEL updated tree
      LINUX /boot/Image
      FDT /boot/original_name_modified.dtb
      INITRD /boot/initrd
      APPEND ${cbootargs}

LABEL primary
      MENU LABEL primary kernel
      LINUX /boot/Image
      FDT /boot/original.dtb
      INITRD /boot/initrd
      APPEND ${cbootargs}

In that example I put the new entry first (not always needed, but there is a bug in some releases needing this), changed both LABEL and MENU LABEL, and edited the new device tree file name. Booting would be default use the modified entry. If something went wrong, then serial console could be used to choose boot to the original entry.

Note: If you were to modify the kernel Image file, then the same would apply; keep the original, and make a second entry pointing at a modified kernel file name.

Well, i’ve changed:
1. extlinux.conf as you said;
2. a little tegra210-..-b00.dtb. Just swapped some parameters; (as the last try)
In order to get 2 non-console (let us say that console could be in ttyS2-4), active UARTs i guess there is no choice but to rebuild the kernel with modified params?

Any UART which is not bound to the login software (getty or agetty) is a non-console. As Jetsons ship there is only one UART set up as a serial console. None of those setups are part of the kernel, although the kernel has a UART driver. What makes it change to group “tty” is not the kernel, it is outside software. Even if a terminal is also set up as a console it would not prevent data you send from reaching the other side…you’d just have a terminal seeing it as nonsense and garbage. Imagine you are sitting at a keyboard and your cat jumps up on it and lands on the keys…it is still a keyboard and data still transmits regardless of whether it is a cat on the keyboard or a human.

All of the accessible serial UARTs will show up in “/dev/ttyS*” or “/dev/ttyTHS*”. If you run this command, and the UART driver is group “tty”, then you cannot use it since it is a console, but you can use it if it is group “dialout”:
ls -l /dev/ttyS* /dev/ttyTHS*

If there is no output when you send text, then either:

  • You’re using the wrong pins for that driver.
  • The settings you are using are far enough off that the UART does not know it is data.
    NOTE: Default is 115200 8N1 (speed 115200, 8-bit, no parity, 1 stop bit.

Rebuilding a kernel won’t change the group from tty to dialout. Rebuilding a kernel won’t cause data to work when port settings are wrong. Rebuilding the kernel won’t normally fix wrong pin choices (the device tree can change lane routing in some cases if there is an option to do so, but that is limited, and a device tree is not the kernel itself…you can think of the device tree as arguments passed to drivers).

Although it seems like a decent solution, i have to clarify.
Do you think, it is the acceptable way?
Cause, it looks like the terminal will freeze a lot to analyze all incoming data)

It depends on what you mean by “freeze”. More details would help.

However, if there is activity on a serial console during boot stages, then it is essentially the user interrupting and saying to drop into a boot menu environment and wait for the user to say what to do next. If this is not a boot freeze during activity prior to Linux loading, and if this is not a serial console (meaning it is not group tty, a console, but is instead group dialout, a generic UART interface), then any kind of “freeze” is probably not right. More information on what the freezing is, and when it occurs, would help to answer.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.