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/ttyS4Query its communication parameters with the
sttytool:root@luckfox:/home/luckfox# stty -F /dev/ttyS2
speed 9600 baud; line = 0;
-brkint -imaxbelChange the baud rate (where
ispeedis the input rate, andospeedis the output rate):stty -F /dev/ttyS2 ispeed 115200 ospeed 115200Disable 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/ttyS2
echo "world !" > /dev/ttyS2The 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/python3
from periphery import Serial
# Use context manager to automatically close the serial port
with Serial("/dev/ttyS3", baudrate=115200, databits=8, parity="none", stopbits=1, xonxoff=False, rtscts=False) as serial:
try:
# Send data
serial.write(b"Hello World!\n")
# Read data
buf = 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.py
Read 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 rate
cfsetispeed(&tty, baudrate); // Set input baud rate
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8 data bits
tty.c_cflag |= (CLOCAL | CREAD); // Enable receiver, ignore modem lines
tty.c_cflag &= ~(PARENB | PARODD); // No parity
tty.c_cflag &= ~CSTOPB; // 1 stop bit
tty.c_cflag &= ~CRTSCTS; // No RTS/CTS hardware flow control
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
tty.c_iflag &= ~(ICRNL | INLCR); // Disable CR-to-LF translation
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Disable canonical mode and echo
tty.c_oflag &= ~OPOST; // Disable output processing
tty.c_cc[VMIN] = 0; // Non-blocking mode
tty.c_cc[VTIME] = 10; // 1-second timeout
if (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 128
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;
}
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 uart
root@luckfox:/home/luckfox# ./uart
Sent: 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>;
};