3.9.1. Use UART

3.9.1.1. Overview

This section describes how to access a UART module in user space applications. The section introduces some Linux infrastructure to expose UART runtime information, which is helpful in debugging UART applications. Some UART debugging tools and tips are also covered here.

When a UART instance is enabled in kernel devicetree and its corresponding kernel drivers are enabled in the kernel config (see details in Porting UART), then the UART module is exposed as a device node /dev/ttyS<x> in the Linux filesystem. <x> is the index defined in serial aliases in the kernel devicetree.

For example, devicetree file k3-am64.dtsi has

aliases {
        ...
        serial2 = &main_uart0;
        ...
};

which indicates serial index 2 is assigned to MAIN UART0. Thus, /dev/ttyS2 represents the MAIN UART0 module in Linux.

3.9.1.2. UART Applications

User space applications access UART via its device node /dev/ttyS<x> in a similar way as accessing regular files. The UART is accessed using open(), close(), read(), write(), and ioctl(). Some TTY specific functions, such as tcsetattr(), are available for configuring or controlling the UART port. The details are covered in Linux TTY programming topics in many resources on the internet.

The serial testing tools mentioned below in UART Test Utilities can be used as a reference to UART programming.

3.9.1.3. UART in Kernel Boot Log

UART initialization information is provided in the kernel boot log (dmesg). For example:

# dmesg | grep 'ttyS. '
[    0.296705] 2800000.serial: ttyS2 at MMIO 0x2800000 (irq = 18, base_baud = 3000000) is a 8250

shows that /dev/ttyS2 is assigned to a UART with base address 0x2800000, which is MAIN UART0 according to k3-am642-evm.dts.

3.9.1.4. UART in /proc Filesystem

Linux provides some information of UART ports in /proc filesystem, which is helpful in debugging UART application issues.

/proc/interrupts

/proc/interrupts entry provides interrupt information at Linux runtime. To only see the UART interrupts:

# grep serial /proc/interrupts
18:        712          0     GICv3 210 Level     2800000.serial

For details of procfs interface, please refer to kernel Documentation procfs.

/proc/tty

/proc/tty/driver/serial entry provides some UART runtime statistics. For example,

# cat /proc/tty/driver/serial
serinfo:1.0 driver revision:
0: uart:unknown port:00000000 irq:0
1: uart:unknown port:00000000 irq:0
2: uart:8250 mmio:0x02800000 irq:18 tx:11822 rx:88 RTS|CTS|DTR
3: uart:unknown port:00000000 irq:0
4: uart:unknown port:00000000 irq:0
5: uart:unknown port:00000000 irq:0
6: uart:unknown port:00000000 irq:0
7: uart:unknown port:00000000 irq:0
8: uart:unknown port:00000000 irq:0
9: uart:unknown port:00000000 irq:0

shows /dev/ttyS2 has transmitted 11822 characters and received 88 characters.

3.9.1.5. UART Test Utilities

There are many serial testing tools in the Linux open source community. The following are a few examples which can be compiled and run on AM64x:

serialcheck

serialcheck provides two utilities, serialcheck and serialstates.

serialcheck sends characters from a data file to a UART port at a given baud rate. When in receiving mode (using option -m r), serialcheck uses the same data file to check the received characters for data integrity.

# ./serialcheck --help
Usage: serialcheck [OPTION...]
user stress testing tool

  -b, --baud=NUM             baudrate
  -d, --device=FILE          serial node device
  -f, --file=FILE            binary file for transfers
  -h, --hflow                enable hardware flow control
  -k, --loopback             loopback mode
  -l, --loops=NUM            loops to perform (0 => wait fot CTRL-C
  -m, --mode=M               transfer mode (d = duplex, t = send r = receive)
  -n, --no-termios           No termios change (baud rate etc. remains
                             unchanged)
  -?, --help                 Give this help list
      --usage                Give a short usage message

serialstates periodically monitors the given UART port and prints its runtime status. This is useful for checking for line errors.

# ./serialstats --help
Usage: serialstats [OPTION...]
uart stats tool

  -d, --device=FILE          serial node device
  -i, --interval=NUM         interval in seconds
  -o, --once                 print stats once and exit
  -?, --help                 Give this help list
      --usage                Give a short usage message

For example:

# ./serialstats -d /dev/ttyS2 -o
cts: 0 dsr: 0 rng: 0 dcd: 0 rx: 400 tx: 21995 frame error 0 overuns 0 parity: 0 break: 0 buffer overrun: 0

linux-serial-test

linux-serial-test is another UART stress test tool. linux-serial-test is different from serialcheck because it will:

  • generate sending characters internally, instead of using an external data file.

  • support divisor option which can be used for custom baud rates, especially helpful for the 13x mode in AM64x UART.

  • support sending single byte. For example, with option -y 0x55 -z 0x0 it sends 8-bits line toggles followed by line at 0. This is helpful when using a scope to check the accuracy of the baud rate.

  • support RS-485 mode.

Usage:

# ./linux-serial-test -h
Linux serial test app
Usage: linux-serial-test [OPTION]

  -h, --help
  -b, --baud        Baud rate, 115200, etc (115200 is default)
  -p, --port        Port (/dev/ttyS0, etc) (must be specified)
  -d, --divisor     UART Baud rate divisor (can be used to set custom baud rates)
  -R, --rx_dump     Dump Rx data (ascii, raw)
  -T, --detailed_tx Detailed Tx data
  -s, --stats       Dump serial port stats every 5s
  -S, --stop-on-err Stop program if we encounter an error
  -y, --single-byte Send specified byte to the serial port
  -z, --second-byte Send another specified byte to the serial port
  -c, --rts-cts     Enable RTS/CTS flow control
  -B, --2-stop-bit  Use two stop bits per character
  -P, --parity      Use parity bit (odd, even, mark, space)
  -e, --dump-err    Display errors
  -r, --no-rx       Don't receive data (can be used to test flow control)
                    when serial driver buffer is full
  -t, --no-tx       Don't transmit data
  -l, --rx-delay    Delay between reading data (ms) (can be used to test flow control)
  -a, --tx-delay    Delay between writing data (ms)
  -w, --tx-bytes    Number of bytes for each write (default is to repeatedly write 1024 bytes
                    until no more are accepted)
  -q, --rs485       Enable RS485 direction control on port, and set delay
                    from when TX is finished and RS485 driver enable is
                    de-asserted. Delay is specified in bit times.
  -o, --tx-time     Number of seconds to transmit for (defaults to 0, meaning no limit)
  -i, --rx-time     Number of seconds to receive for (defaults to 0, meaning no limit)
  -A, --ascii       Output bytes range from 32 to 126 (default is 0 to 255)