UART
1. Introduction to UART
UART (Universal Asynchronous Receiver-Transmitter) is a widely used serial communication protocol for embedded systems and computers. UART transmits data bit by bit using asynchronous communication, eliminating the need to share a clock signal between the transmitter and receiver. This makes UART a common choice for short-distance, low-speed data transfers between devices. In Linux systems, UART is typically registered as a serial terminal device, appearing in the system under /dev/tty* or /dev/serial/*, with the specific device name depending on hardware and driver configurations.
2. Serial Port Pin Diagram
-
In the Omni3576, UART0_M0 is designated as the debug serial port.For details on how to use the debug serial port, please refer to the “Serial Login” section in the Login chapter.The location of the debug serial port interface is shown below:

-
Omni3576 Pin Diagram:

3. Serial Port Testing (Shell)
-
Taking UART2_M1 as an example, after successfully opening it, use the following command to inspect UART2_M1
root@luckfox:/home/luckfox# ls /dev/ttyS*/dev/ttyS2 /dev/ttyS4 -
Query its communication parameters with the
sttytool:root@luckfox:/home/luckfox# stty -F /dev/ttyS2speed 9600 baud; line = 0;-brkint -imaxbel -
Change the baud rate (where
ispeedis the input rate, andospeedis the output rate):stty -F /dev/ttyS2 ispeed 115200 ospeed 115200 -
Disable echo:
stty -F /dev/ttyS2 -echo
3.1 Communication with a Windows Host
-
Connect one end of a serial-to-TTL module to the PC and the other to pins 6 (GND), 8 (UART2_TX), and 10 (UART2_RX) on the Omni3576 development board.
-
Download and open MobaXterm, select the serial port, and set the baud rate (default: 9600; adjust according to your actual configuration).

-
Execute the following commands on the development board terminal to write the strings "Hello" and "world!" to the terminal device file using the
echocommand:echo Hello > /dev/ttyS2echo "world !" > /dev/ttyS2 -
The serial debugging tool on Windows will receive the content.

4. Serial Communication (Python Program)
-
Use Python's
seriallibrary to implement a complete program for sending and receiving data:#!/usr/bin/python3from periphery import Serial# Use context manager to automatically close the serial portwith Serial("/dev/ttyS3", baudrate=115200, databits=8, parity="none", stopbits=1, xonxoff=False, rtscts=False) as serial:try:# Send dataserial.write(b"Hello World!\n")# Read databuf = serial.read(128, 1)if buf:data_string = buf.decode("utf-8")print(f"Read {len(buf)} bytes:\n{data_string}")else:print("No data received.")except Exception as e:print(f"An error occurred: {e}") -
Run the program.
root@luckfox:/home/luckfox# sudo python3 uart.pyRead 13 bytes:Hello World!
5. Serial Communication (C Program)
-
Opening the Serial Port.
int open_serial(const char *device) {int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);if (fd < 0) {perror("Error opening serial port");return -1;}return fd;} -
Configuring Serial Port Parameters.
int configure_serial(int fd, speed_t baudrate) {struct termios tty;if (tcgetattr(fd, &tty) != 0) {perror("Error getting tty attributes");return -1;}cfsetospeed(&tty, baudrate); // Set output baud ratecfsetispeed(&tty, baudrate); // Set input baud ratetty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8 data bitstty.c_cflag |= (CLOCAL | CREAD); // Enable receiver, ignore modem linestty.c_cflag &= ~(PARENB | PARODD); // No paritytty.c_cflag &= ~CSTOPB; // 1 stop bittty.c_cflag &= ~CRTSCTS; // No RTS/CTS hardware flow controltty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow controltty.c_iflag &= ~(ICRNL | INLCR); // Disable CR-to-LF translationtty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Disable canonical mode and echotty.c_oflag &= ~OPOST; // Disable output processingtty.c_cc[VMIN] = 0; // Non-blocking modetty.c_cc[VTIME] = 10; // 1-second timeoutif (tcsetattr(fd, TCSANOW, &tty) != 0) {perror("Error setting tty attributes");return -1;}return 0;} -
Sending Data.
int send_data(int fd, const char *data) {int len = strlen(data);int bytes_written = write(fd, data, len);if (bytes_written < 0) {perror("Error writing to serial port");return -1;}return bytes_written;} -
Receiving Data.
int receive_data(int fd, char *buffer, int buffer_size) {int bytes_read = read(fd, buffer, buffer_size);if (bytes_read < 0) {perror("Error reading from serial port");return -1;}return bytes_read;} -
Closing the Serial Port.
void close_serial(int fd) {
if (close(fd) != 0) {
perror("Error closing serial port");
}
}
-
Complete Example Code.
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h> // open()#include <termios.h> // tcgetattr(), tcsetattr()#include <unistd.h> // read(), write(), close()#include <errno.h> // errno#define SERIAL_PORT "/dev/ttyS3"#define BAUDRATE B115200#define BUFFER_SIZE 128int open_serial(const char *device) {int fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);if (fd < 0) {perror("Error opening serial port");return -1;}return fd;}int configure_serial(int fd, speed_t baudrate) {struct termios tty;if (tcgetattr(fd, &tty) != 0) {perror("Error getting tty attributes");return -1;}cfsetospeed(&tty, baudrate);cfsetispeed(&tty, baudrate);tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; /tty.c_cflag |= (CLOCAL | CREAD);tty.c_cflag &= ~(PARENB | PARODD);tty.c_cflag &= ~CSTOPB;tty.c_cflag &= ~CRTSCTS;tty.c_iflag &= ~(IXON | IXOFF | IXANY);tty.c_iflag &= ~(ICRNL | INLCR);tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);tty.c_oflag &= ~OPOST;tty.c_cc[VMIN] = 0;tty.c_cc[VTIME] = 10;if (tcsetattr(fd, TCSANOW, &tty) != 0) {perror("Error setting tty attributes");return -1;}return 0;}int send_data(int fd, const char *data) {int len = strlen(data);int bytes_written = write(fd, data, len);if (bytes_written < 0) {perror("Error writing to serial port");return -1;}return bytes_written;}int receive_data(int fd, char *buffer, int buffer_size) {int bytes_read = read(fd, buffer, buffer_size);if (bytes_read < 0) {perror("Error reading from serial port");return -1;}return bytes_read;}void close_serial(int fd) {if (close(fd) != 0) {perror("Error closing serial port");}}int main() {int serial_fd;char recv_buffer[BUFFER_SIZE];serial_fd = open_serial(SERIAL_PORT);if (serial_fd < 0) return 1;if (configure_serial(serial_fd, BAUDRATE) != 0) {close_serial(serial_fd);return 1;}const char *message = "Hello World!\n";if (send_data(serial_fd, message) < 0) {close_serial(serial_fd);return 1;}printf("Sent: %s", message);int bytes_received = receive_data(serial_fd, recv_buffer, BUFFER_SIZE - 1);if (bytes_received > 0) {recv_buffer[bytes_received] = '\0';printf("Received: %s\n", recv_buffer);} else if (bytes_received == 0) {printf("No data received.\n");}close_serial(serial_fd);return 0;} -
Compile and Run the Program
root@luckfox:/home/luckfox# gcc uart.c -o uartroot@luckfox:/home/luckfox# ./uartSent: Hello World!Received: Hello World!
6. Device Tree Overview
-
The
uart2node is defined in thekernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsifile and can be directly referenced:uart2: serial@2ad50000 {compatible = "rockchip,rk3576-uart", "snps,dw-apb-uart";reg = <0x0 0x2ad50000 0x0 0x100>;interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;clock-names = "baudclk", "apb_pclk";reg-shift = <2>;reg-io-width = <4>;dmas = <&dmac0 10>, <&dmac0 11>;pinctrl-names = "default";pinctrl-0 = <&uart2m0_xfer>;status = "disabled";}; -
The device file path is located in
kernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dts. The following snippet enablespwm2m0_ch0:&uart2 {status = "okay";pinctrl-names = "default";pinctrl-0 = <&uart2m1_xfer>;};