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 Lyra, UART0 is designated as the debug serial port, and the baud rate of the debug serial port is 1500000 by default.
- Luckfox Lyra Ultra/Ultra W Pin Diagram:

3. Serial Port Testing (Shell)
Taking UART1 as an example, after successfully opening it, use the following command to inspect UART1
root@luckfox:/home/luckfox# ls /dev/ttyS*
/dev/ttyS1Query its communication parameters with the
sttytool:root@luckfox:/home/luckfox# stty -F /dev/ttyS1
speed 9600 baud; line = 0;
-brkint -imaxbelChange the baud rate (where
ispeedis the input rate, andospeedis the output rate):iomux 0 12 16
iomux 0 13 17
stty -F /dev/ttyS1 ispeed 115200 ospeed 115200Disable echo:
stty -F /dev/ttyS1 -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 3(GND)、4(RMIO12)and 5(RMIO13)on the Lyra 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/ttyS1
echo "world !" > /dev/ttyS1The 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# 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/ttyS1"
#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;
}
Cross-compile and run the program. To build a cross-compilation environment, please refer to the "Program Compilation" or "GPIO" section.
arm-none-linux-gnueabihf-gcc uart.c -o uart
6. Device Tree Overview
The device file path is located in
kernel-6.1/arch/arm/boot/dts/rk3506g-luckfox-lyra.dts, The following snippet enablesuart:&uart1 {
pinctrl-0 = <&rm_io12_uart1_tx &rm_io13_uart1_rx>;
status = "okay";
pinctrl-names = "default";
};