How to setup UART 100000 bit/s baudrate on Jetson Nano for communication with SBUS receiver?

SBUS Protocol (https://github.com/uzh-rpg/rpg_quadrotor_control/wiki/SBUS-Protocol) uses 8E2 and 100000 bit/s baudrate. SBUS requires Signal Inverter (https://oscarliang.com/sbus-smartport-telemetry-naze32/). This works fine on Raspbery Pi using Frsky XM+ and sbusPythonDriver (https://github.com/fifteenhex/python-sbus):

setserial -a /dev/ttyS3 spd_cust
setserial -a /dev/ttyS3 divisor 15
stty -F /dev/ttyS3 parenb -parodd -cmspar cs8 cstopb -crtscts -ixon -ixoff 38400

How to get this working on Jetson Nano? I’ve tried /dev/ttyTHS1 and /dev/ttyS0. “setserial -a /dev/ttyTHS1 spd_cust” shows an error “Cannot set serial info: Invalid argument”. On /dev/ttyS0 no errors with setserial, but baudrate is definitely not 100000 bit/s because sbusPythonDriver receives some garbage from /dev/ttyS0 and can’t find beginning of SBUS frame.

systemctl stop serial-getty@ttyS0.service
setserial -a /dev/ttyS0 spd_cust
setserial -a /dev/ttyS0 divisor 255
stty -F /dev/ttyS0 parenb -parodd -cmspar cs8 cstopb -crtscts -ixon -ixoff 38400

I’ve read this article (non-standard baud rate for the uart), but it won’t help.

hello student5487,

FYI, the default UART baud-rate setting is 115200/8n1 for the serial port communications.
you may access L4T sources package and check the sources for reference,
for example,
$L4T_Sources/r32.4.3/Linux_for_Tegra/source/public/hardware/nvidia/soc/t210/kernel-dts/tegra210-soc/tegra210-soc-base.dtsi

Hello Jerry,
Thanks for tegra210-soc-base.dtsi. I’ve checked this in a different way:

export DT_NAME="tegra210-p3448-0000-p3449-0000-b00"
dtc -I dtb -O dts /boot/${DT_NAME}.dtb -o /tmp/${DT_NAME}.dts

        serial@70006000 {
                compatible = "nvidia,tegra210-uart", "nvidia,tegra114-hsuart", "nvidia,tegra20-uart";
                reg = <0x0 0x70006000 0x0 0x40>;
                reg-shift = <0x2>;
                interrupts = <0x0 0x24 0x4>;
                iommus = <0x30 0xe>;
                dmas = <0x51 0x8 0x51 0x8>;
                dma-names = "rx", "tx";
                clocks = <0x26 0x6 0x26 0xf3>;
                clock-names = "serial", "parent";
                nvidia,adjust-baud-rates = <0x1c200 0x1c200 0x64>;
                status = "okay";
                console-port;
                sqa-automation-port;
                enable-rx-poll-timer;
                linux,phandle = <0x10c>;
                phandle = <0x10c>;
        };

I don’t understand how to setup UART clocks in the JN. Usual way in Linux is to setup “divisor”. E.g. if we want to get 100000 baudrate, then we need to change “divisor” in JN to 255, so, if we connect at ‘spd_cust’, then we’ll get 100000 baudrate:

# Calculate the divisor by dividing Baud_base / Cust_Baud
#    Baud_base: 25500000
#    Cust_Baud:   100000
echo $((25500000/100000))
    255

Could you explain please - Is it possible to change UART baud-rate from 115200 to 100000 in JN at all? And how to do this?

hello student5487,

please have a try with below sample to configure your bardrate settings,
for example,

struct termios2;
ioctl: TCGETS2, TCSETS2

flags to set and clear:
c_cflag &= ~CBAUD;
c_cflag |= BOTHER;
c_cflag |=CLOCAL;
c_ispeed = NEW_SPEED;
c_ospeed = NEW_SPEED;

Hi Jerry,
Could you answer couple of questions please (probably off-topic).

setserial -a /dev/ttyS0                                  
    /dev/ttyS0, Line 0, UART: undefined, Port: 0x0000, IRQ: 63
        Baud_base: 25500000, close_delay: 50, divisor: 255
        closing_wait: 3000
        Flags: spd_cust
setserial -a /dev/ttyS1
    /dev/ttyS1, Line 1, UART: unknown, Port: 0x0000, IRQ: 0
        Baud_base: 0, close_delay: 50, divisor: 255
        closing_wait: 3000
        Flags: spd_normal
setserial -a /dev/ttyTHS1  
    /dev/ttyTHS1, Line 1, UART: undefined, Port: 0x0000, IRQ: 64
        Baud_base: 0, close_delay: 50, divisor: 255
        closing_wait: 3000
        Flags: spd_normal

Q1: Why /dev/ttyS0 shows Baud_base: 25500000 and /dev/ttyS1 shows Baud_base: 0?
Q2: To use pins 8 and 10 on 2x13 Header on JN should I use /dev/ttyS1 or /dev/ttyTHS1? And what’s the difference?

    2x13 Header  8 UART2_TX 
    2x13 Header 10 UART2_RX

BTW, what’s JN you’re mention here, did you mean Jetson-Nano ?

Hi Jerry, yes, JN is Jetson Nano. Should I use /dev/ttyTHS1 or /dev/ttyS1?
With pins 8 and 10 connected, only “screen /dev/ttyTHS1 38400” works.
“screen /dev/ttyS1 38400” disconnects immediately with “[screen is terminating]”.

sudo systemctl stop nvgetty
# sudo systemctl disable nvgetty
sudo udevadm trigger
sudo chmod 666 /dev/ttyS1 /dev/ttyTHS1
ls -l /dev/ttyS1 /dev/ttyTHS1
    crw-rw-rw- 1 root dialout   4, 65 Nov 11 10:25 /dev/ttyS1
    crw-rw-rw- 1 root tty     238,  1 Nov 11 10:28 /dev/ttyTHS1
screen /dev/ttyTHS1 38400 # works 
screen /dev/ttyS1 38400   # [screen is terminating]

hello student5487,

you should use the uart port with ttyTHS*.
please also check discussion thread, Topic 111959 for more details for your reference,
thanks

Hi Jerry,

I have followed your advice. This works fine on Orange Pi Zero, so now I can connect both using custom “spd_cust” and “divisor 15” and directly using settings you have advised (TCGETS2, TCSETS2, CBAUD, etc.). Unfortunately, on Jetson Nano this does not work.

Can’t it be that TTL levels work fine for Orange Pi Zero (or Raspberry Pi) and won’t work for Jetson Nano? For SBUS Signal Inventor I have used references https://www.youtube.com/watch?v=DtvID1YeSbE and https://oscarliang.com/sbus-smartport-telemetry-naze32/. Exactly 4.7k, 10k resistors and BC547b.

One thing I am wondering about is available IOCTLs. Whenever you make a custom setting for a driver which is not a “standard file I/O”, then an IOCTL is used for this. If the non-DMA ttyS1 driver is able to make certain changes to the hardware status via user space programs like stty, but the DMA version via ttyTHS1 is unable to make those same changes or see the same values, then it is because the drivers support different IOCTL calls.

This is more a question for NVIDIA (@JerryChang ), but are all of the non-DMA serial UART IOCTLs fully implemented in the DMA-capable driver? I ask because of @student5487’s notice of baud_base differing. The possibilities are that (A) the IOCTL is the same, but the method of using baud_base differs, and thus the change to how the IOCTL responds differently is unrelated to the IOCTL interface itself, or (B) the internal handling of baud_base is the same, but the IOCTL itself has an incomplete implementation.

Just an added emphasis for @student5487: I am illustrating some differences in IOCTL calls between two drivers using the same hardware, but I am not advocating using both drivers at the same time. When in normal operation you should not simultaneously cause an IOCTL call of any kind to both ttyS1 and ttyTHS1 while the UART operates, nor should you send or receive data to a mixture of ttyS1 and ttyTHS1. Two operations on two different drivers using the same hardware could cause problems, but the observation that the baud_base differs is interesting since it suggests differences in the implementation of the IOCTL which deals with baud_base.

Hi student5487,
Can you share the code you have used to set the flags mentioned by Jerry?

flags to set and clear:
c_cflag &= ~CBAUD;
c_cflag |= BOTHER;
c_cflag |=CLOCAL;
c_ispeed = NEW_SPEED;
c_ospeed = NEW_SPEED;

Also from which console(UART port) you are trying to run Nano’s UART (8,10)?

from pprint import pprint
import array, fcntl, termios, asyncio, serial, serial_asyncio
# sudo systemctl stop serial-getty@ttyS0.service
# sudo chmod 666 /dev/ttyS0
DEVICE = '/dev/ttyS0' # The same results for '/dev/ttyTHS1'
serial_ = serial.Serial(port=DEVICE,
                        baudrate=100000,
                        parity=serial.PARITY_EVEN,
                        stopbits=serial.STOPBITS_TWO,
                        bytesize=serial.EIGHTBITS,)
pprint(serial_.get_settings(), indent=4)
{   'baudrate': 100000,
    'bytesize': 8,
    'dsrdtr': False,
    'inter_byte_timeout': None,
    'parity': 'E',
    'rtscts': False,
    'stopbits': 2,
    'timeout': None,
    'write_timeout': None,
    'xonxoff': False}
serial_struct = array.array('i', [0] * 32)
fcntl.ioctl(serial_.fd, termios.TIOCGSERIAL, serial_struct)
port_id = serial_struct[0]
!grep {port_id} /usr/include/linux/serial_core.h
    #define PORT_TEGRA	20	/* NVIDIA Tegra internal UART */
print(f"{'termios.CBAUD':30} {termios.CBAUD:016b}")
print(f"{'serial.serialposix.BOTHER':30} {serial.serialposix.BOTHER:016b}")
print(f"{'termios.CLOCAL':30} {termios.CLOCAL:016b}")
    termios.CBAUD                  0001000000001111
    serial.serialposix.BOTHER      0001000000000000
    termios.CLOCAL                 0000100000000000
serial_buffer = array.array('I', [0] * 64) # is 44 really
fcntl.ioctl(serial_.fd, serial.serialposix.TCGETS2, serial_buffer)
c_cflag = serial_buffer[2]
print(f"{'c_cflag':30} {c_cflag:016b}")
c_cflag_ = c_cflag
c_cflag &= ~termios.CBAUD
c_cflag |= serial.serialposix.BOTHER
c_cflag |= termios.CLOCAL
print(f"{'c_cflag':30} {c_cflag:016b}")
c_cflag_ == c_cflag
    c_cflag                        0001110111110000
    c_cflag                        0001110111110000
    True # original and modified flags are the same
ispeed = serial_buffer[9]  # /* input speed */
ospeed = serial_buffer[10] # /* output speed */
print(f"ispeed {ispeed} ospeed {ospeed}")
    ispeed 100000 ospeed 100000 # inbound and outbound speed is already 100000
if serial_ is not None:
    serial_.close()
    serial_ = None
/dev/ttyS0
    J44 (Console)   Pin  3 (RXD)
/dev/ttyTHS1
    J41 2x13 Header Pin 10 (UART2_RX)
5V and GND
    J41 2x13 Header Pin  4 (5.0 VDC Power)
    J41 2x13 Header Pin  6 (GND)

So what is the error you are getting? Are you not receiving any data or not receiving with 100kbps?

Hello shgarg!
I have used ttyS0 and ttyTHS1 for experiments.

FrSky XM+ -> SBUS Invertor -> /dev/ttyS0:
  - J44 Pin  3 (RXD) /dev/ttyS0  (TX)  SBUS Invertor
  - J41 Pin  4 (5.0 VDC Power)   (5V)  SBUS Invertor
  - J41 Pin  6 (GND)             (GND) SBUS Invertor
FrSky XM+ -> SBUS Invertor -> /dev/ttyTHS1:
  - J41 Pin  4 (5.0 VDC Power)   (5V)  SBUS Invertor
  - J41 Pin  6 (GND)             (GND) SBUS Invertor
  - J41 Pin 10 (UART2_RX)        (TX)  SBUS Invertor

When ttyS0 is used, you can’t read any SBUS information at all. When ttyTHS1 is used, you receive some garbage information which is not SBUS protocol information. When the same FrSky XM+ and SBUS Invertor are connected to Orange Pi Zero everything works fine.

from sbus.rx import SBUSReceiver
sbus = await SBUSReceiver.create('/dev/ttyTHS1')
for i in range(3):
    frame = await sbus.get_frame()
    print(frame)
  16,0,0,0,0,508,896,1024,0,0,112,0,0,0,0,0,0,0
  0,768,0,0,0,448,0,4,14,3,0,0,0,0,0,0,0,0
  0,0,0,0,256,0,1031,1031,134,0,0,0,1024,16,56,0,0,0

Supposed to receive something like this (from Orange Pi Zero):

  172,994,998,994,172,172,172,172,172,172,1005,1126,992,992,992,1414,0,0
  172,994,998,994,172,172,172,172,172,172,1015,1121,992,992,992,1417,0,0
  172,994,998,994,172,172,172,172,172,172,1015,1121,992,992,992,1418,0,0

I have also connected over 100000 baudrate to ttyS0 and ttyTHS1 and tried send-receive tests. You can send receive test messages on the same ports (ttyS0 and ttyTHS1). ttyTHS1 can send test message to ttyS0, but when message is send from ttyS0 to ttyTHS1, there’s some garbage bytes received.

J44 Pin 3 (RXD) /dev/ttyS0 <-> J41 Pin 8 (UART2_TX) /dev/ttyTHS1:

serial_S1.write(b'Hi there!')
read_serial(serial_S0, timeout=1)
    b'Hi there!'

J44 Pin 2 (TXD) /dev/ttyS0 <-> J41 Pin 10 (UART2_RX) /dev/ttyTHS1:

serial_S0.write(b'Hi there!')
read_serial(serial_S1, timeout=1)
    b'\x00\x00Hi th`re \x00'
    b'\x00\x00a tHerE!' b'\x00Hi \x04\x08ep`!'
    b'\x00\x00\x00Hi\x00th\x08\x00e\t!\xa1\xe1' etc.

Dear Moderators,
Do you have any idea how to get 100000 boud rate working on Jetson Nano? Can the issue be with SBUS Invertor with the signal levels? Could you look at SBUS Invertor links please: https://oscarliang.com/ctt/uploads/2015/12/sbus-inverter-diagram-schematics-300x206.jpg https://oscarliang.com/sbus-smartport-telemetry-naze32/.