UART
1. UART 简介
UART(Universal Asynchronous Receiver-Transmitter,通用异步收发器)是一种广泛应用于嵌入式系统和计算机之间的串行通信协议。UART 通过将数据一位一位地传输,采用异步通信方式,数据传输和接收之间无需共享时钟信号。这使得UAR T成为短距离低速设备之间数据传输的常用选择。在 Linux 系统中通常被注册为串口终端设备。这些串口设备在 Linux 系统中以/dev/tty*或/dev/serial/*的形式出现,具体设备名取决于硬件和驱动程序的设置。
2. 串口引脚图
在 Omni3576 中,UART0_M0 被指定为调试串口(有关调试串口的使用方法,请参考《登录》章节中的“串口登录”部分),调试串口的接口位置如图:

Omni3576 引脚图:

3. 串口测试(shell)
以 UART2_M1 为例,打开成功后,我们使用命令查看 UART2_M1:
root@luckfox:/home/luckfox# ls /dev/ttyS*
/dev/ttyS2 /dev/ttyS4用 stty 工具查询其通信参数:
root@luckfox:/home/luckfox# stty -F /dev/ttyS2
speed 9600 baud; line = 0;
-brkint -imaxbel修改波特率,其中i speed 为输入速率,ospeed 为输出速率:
stty -F /dev/ttyS2 ispeed 115200 ospeed 115200关闭回显:
stty -F /dev/ttyS2 -echo
3.1 与Windows主机通讯
将串口转 TTL 模块一端连接电脑,另一端连接 Omni3576 开发板的引脚6(GND)、8(UART2_TX)和10(UART2_RX)上。
下载并打开 MobaXterm,选择串口,设置波特率(默认为9600,请根据自己实际修改过的数值设置)。

在开发板上的终端执行以下指令,使用 echo 命令向终端设备文件写入字符串"Hello"和"world !":
echo Hello > /dev/ttyS2
echo "world !" > /dev/ttyS2Windows 上的串口调试助手会接收到内容:

4. 串口通信(Python程序)
使用 Python 的
serial库,实现收发数据的完整代码如下:#!/usr/bin/python3
from periphery import Serial
# 使用上下文管理器,自动关闭串口
with Serial("/dev/ttyS3", baudrate=115200, databits=8, parity="none", stopbits=1, xonxoff=False, rtscts=False) as serial:
try:
# 发送数据
serial.write(b"Hello World!\n")
# 读取数据
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}")运行程序。
root@luckfox:/home/luckfox# sudo python3 uart.py
Read 13 bytes:
Hello World!
串口通信(C程序)
打开串口。
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; // 8位数据位
tty.c_cflag |= (CLOCAL | CREAD); // 启用接收器,忽略调制解调器线状态
tty.c_cflag &= ~(PARENB | PARODD); // 无奇偶校验
tty.c_cflag &= ~CSTOPB; // 1个停止位
tty.c_cflag &= ~CRTSCTS; // 不使用RTS/CTS硬件流控
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
tty.c_iflag &= ~(ICRNL | INLCR); // 关闭CR-LF转换
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭规范模式,关闭回显
tty.c_oflag &= ~OPOST; // 关闭输出处理
tty.c_cc[VMIN] = 0; // 非阻塞模式
tty.c_cc[VTIME] = 10; // 1秒超时
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");
}
}使用 C 程序来实现收发数据完整代码如下:
#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; // 8位数据位
tty.c_cflag |= (CLOCAL | CREAD); // 启用接收器,忽略调制解调器线状态
tty.c_cflag &= ~(PARENB | PARODD); // 无奇偶校验
tty.c_cflag &= ~CSTOPB; // 1个停止位
tty.c_cflag &= ~CRTSCTS; // 不使用RTS/CTS硬件流控
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
tty.c_iflag &= ~(ICRNL | INLCR); // 关闭CR-LF转换
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭规范模式,关闭回显
tty.c_oflag &= ~OPOST; // 关闭输出处理
tty.c_cc[VMIN] = 0; // 非阻塞模式
tty.c_cc[VTIME] = 10; // 1秒超时
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;
}编译运行程序。
root@luckfox:/home/luckfox# gcc uart.c -o uart
root@luckfox:/home/luckfox# ./uart
Sent: Hello World!
Received: Hello World!
5. 设备树简介
UART2 DTS 源文件在
kernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsi已经定义,我们可以直接调用。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";
};设备文件路径位于
kernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576.dts,开启pwm2m0_ch0的代码片段如下:&uart2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart2m1_xfer>;
};