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/Lyra B Pin Diagram:

Luckfox Lyra Plus 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";
};