UART
UART 介绍
UART,全称为通用异步收发传输器(Universal Asynchronous Receiver and Transmitter),是一种常见的串行通信接口。在嵌入式系统中,UART 的应用非常广泛,主要用于以下几个方面:输出调试信息以便于开发和故障排查;连接外部设备模块,如 GPS 定位模块和蓝牙通信模块等。
在 UART 通信中,首先需要确定传输速率,即波特率,它表示每秒传输的数据位数。常见的波特率包括 9600、19200 和 115200 等。确定波特率后,还需设定数据传输的格式。UART 通信以帧为单位传输数据,典型的帧结构包括:1 位起始位、8 位数据位、可选的 1 位奇偶校验位以及 1 位停止位。
以下是一个示例说明:

在 UART 通信中,数据帧的波形通常由 1 位起始位、8 位数据位、1 位偶校验位和 1 位停止位组成。以字符 'A' 为例,其 ASCII 值为 41(十进制),对应的二进制为 0100 0001。在传输时,采用小端模式,即低位(LSB)在前,高位(MSB)在后,这与波形图所示一致。以下是各部分的详细说明:
- 空闲位:在没有数据传输时,数据线保持高电平(逻辑 1)。
- 起始位:当开始发送数据时,UART 会将数据线从高电平变为低电平,表示起始位(逻辑 0)。
- 数据位:数据位可以是 5、6、7 或 8 位,通常按字节(8 位)传输。发送方逐位改变数据线的状态(高电平或低电平),先传输最低位,最后传输最高位。例如,字符 'A' 的二进制表示为 01000001,首先发送最低位 bit 0(值为 1),接着发送 bit 1(值为 0),依此类推,最后发送最高位 bit 7(值为 0)。
- 奇偶校验位:如果启用了奇偶校验功能,在数据位发送完毕后,会发送 1 位校验位。奇偶校验分为奇校验和偶校验,确保数据位和校验位中”1”的数量为奇数或偶数。奇偶校验仅用于检错,无法纠错,且只能检测单比特错误,发现错误后需重新发送数据。图中使用的是偶校验,数据位和校验位中”1”的总数为偶数(2 个)。
- 停止位:停止位(逻辑 1)表示当前数据传输结束。停止位的长度可以是 1 位、1.5 位或 2 位,通常选择 1 位即可。
功能描述
MTT E300 中有 8 组 UART,其中 UART0-6 和 UART7 的硬件实现、功能以及驱动代码都不同。
UART0-6 功能特性
- 功能兼容 16550A。
- 全双工操作。
- 具有噪声滤波的稳健接收数据采样。
- 64 字节的接收 FIFO 和 64 字节的发送 FIFO。
- 可编程的 FIFO 中断触发级别。
- 接收 FIFO 上的”旧数据”定时器。
- 完全可编程的串行接口。
- 数据位:7 位或 8 位。
- 校验位:无、偶校验、奇校验或固定校验。
- 停止位:1 位或 2 位。
- 中断条件检测和生成。
- 内置波特率发生器。
- 可编程的整数和小数分频器用于波特率生成。
- 可编程的波特率计算方法,支持高达 12 Mbps 的波特率。
- 低速模式:波特率 = (功能时钟 / 16) / 分频器。
- 中速模式:波特率 = (功能时钟 / 8) / 分频器。
- 高速模式:波特率 = (功能时钟 / 4) / 分频器。
- 完整的状态报告能力。
- 内部诊断能力。
- 回环模式用于自测试。
- 中断条件、奇偶校验错误和帧错误模拟。
- 独立的 APB 读写操作,用于发送和接收数据。
- 独立的 DMA 操作,用于发送和接收数据服务。
- 自动流控制能力。
- RTS 由 UART 接收 FIFO 控制。
- 来自外部的 CTS 控制 UART 发送器。
- 软件流控制能力。
- 可编程的 XOFF 字符用于停止 UART 发送器。
- 可编程的 XON 字符用于启动 UART 发送器。
UART7 功能特性
-
AMBA APB 接口:便于集成到 AMBA SoC 实现中。
-
APB 数据总线宽度:8、16 和 32 位。
-
发送保持寄存器空(THRE)中断模式。
-
busy 功能。
-
能够在实例化时设置某些配置参数。
-
功能基于 16550 行业标准,具体如下:
- 可编程字符属性,如每个字符的数据位数(5-8)、可选的奇偶校验位(奇校验或偶校验)以及停止位数(1 或 2)。
- 线路中断生成和检测。
- 优先级中断识别。
-
可编程串行数据波特率,计算公式如下:
波特率 = (串行时钟频率) / (16 × 分频系数)。
-
调制解调器和状态线独立控制。
-
每个时钟域独立的系统复位,以防止亚稳态。
运行原理
Linux UART 驱动介绍
Linux 中 UART 驱动的层次结构如图:

-
用户层:应用程序与 UART 设备交互的接口
- 通过设备文件(如
/dev/ttyS0、/dev/ttyUSB0)读写数据。 - 使用 系统调用(如
open()、read()、write()、ioctl())控制设备。 - 配置串口参数(波特率、数据位、停止位等)通过 termios 库。
- 直接发送原始数据或接收硬件反馈。
- 通过设备文件(如
-
TTY core:内核中管理终端设备的通用框架
- 提供统一的设备抽象,支持多种终端类型(如 UART、虚拟终端、伪终端)。
- 管理用户层与底层驱动的数据流(缓冲区、流量控制)。
- 处理会话管理(如进程组、信号传递)。
- 通过
tty_driver和tty_operations接口与底层驱动交互。
-
Line Discipline:行规程是数据格式和协议转换的中间层
- 数据预处理:将原始数据转换为特定协议格式(如 PPP、SLIP)。
- 输入/输出控制:实现特殊字符处理(如 Ctrl+C 终止信号)。
- 协议封装:支持网络协议栈与串口通信的适配。
- 默认线路规程(N_TTY)提供标准终端行为(行缓冲、回显)。
-
UART driver:直接操作硬件 UART 控制器的底层驱动
- 初始化 UART 硬件(寄存器配置、中断设置)。
- 实现数据传输(发送/接收字节)。
- 处理硬件中断(如数据到达、发送完成)。
- 支持波特率生成、奇偶校验、流量控制(RTS/CTS)等配置。
- 与 TTY 核心交互,通过
uart_ops结构体注册驱动功能。
MTT E300 UART 驱动相关文件
drivers/tty/serial/8250/8250_core.c:UART7 串口驱动核心。drivers/tty/serial/8250/8250_of.c:UART7 串口驱动设备树枚举和解析。drivers/tty/serial/8250/8250_port.c:UART7 串口端口操作。drivers/tty/serial/8250/8250_early.c:UART7 串口 early console 驱动。drivers/tty/serial/mthreads_serial.c:UART0-6 串口驱动。drivers/tty/serial/serial_core.c:串口驱动核心逻辑。
配置流程
步骤 1:配置设备树
MTT E300 有 8 组 UART 控制,其设备树文件路径为:
linux-mthreads/arch/arm64/boot/dts/m1000/m1000-main.dtsi。
uart0 - uart6 设备树节点配置示例:
uart3_mth: serial_mth@71A000 {
reset-names = "mrst", "arst", "prst";
resets = <&crg RESET_PERI_UART3_MRST>,
<&crg RESET_PERI_UART3_ARST>,
<&crg RESET_PERI_UART3_PRST>;
clock-names = "aclk", "mclk", "pclk";
clocks = <&crg PERI_UART3_ACLK>,
<&crg PERI_UART3_MCLK>,
<&crg PERI_UART3_PFUNC_CLK>;
interrupts = <GIC_SPI 0x19 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x00 0x0071A000 0x00 0x00001000>;
M1000,uart-has-rtscts;
compatible = "mthreads,M1000-earlycon", "mthreads,M1000-uart";
};
注意
MTT E300,uart-has-rtscts属性,只有 uart3 - uart6 支持四线模式,需要配置该属性。
uart7 设备树节点示例:
uart0: serial@47000 {
compatible = "ns16550a";
reg = <0x00 0x00047000 0x00 0x100>;
// interrupt-parent = <&gic>;
interrupts = <GIC_SPI 0x118 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <100000000>;
reg-shift = <2>;
reg-io-width = <4>;
status = "okay";
};
为区分 8 组不同的 UART,需要在 aliases 节点中指定每个 control 在 Linux 系统中的 bus 号:
aliases {
serial0 = &uart0;
serial_mt0 = &uart0_mth;
serial_mt1 = &uart1_mth;
serial_mt2 = &uart2_mth;
serial_mt3 = &uart3_mth;
serial_mt4 = &uart4_mth;
serial_mt5 = &uart5_mth;
serial_mt6 = &uart6_mth;
};
步骤 2:配置内核
在使用前需要确保内核的 menuconfig 选中以上两个驱动。
- uart0-6 的驱动文件位于
drivers/tty/serial/mthreads_serial.c。 在内核菜单中的路径及信息如下:
│ Symbol: SERIAL_MTHREADS [=y]
│ Type : bool
│ Defined at drivers/tty/serial/Kconfig:1581
│ Prompt: Moore Threads on-chip serial port support
│ Depends on: TTY [=y] && HAS_IOMEM [=y] && ARCH_M1000_SO
C [=y]
│ Location:
│ -> Device Drivers
│ -> Character devices
│ -> Enable TTY (TTY [=y])
│ -> Serial drivers
│ (1) -> Moore Threads on-
chip serial port support (SERIAL_MTHREADS [=y])
│ Selects: SERIAL_CORE [=y]
- uart7 的驱动位于
drivers/tty/serial/8250/*。 在内核菜单中的路径及信息如下:
1. Symbol: SERIAL_8250 [=y]
2. │ Type : tristate
3. │ Defined at drivers/tty/serial/8250/Kconfig:7
4. │ Prompt: 8250/16550 and compatible serial support
5. │ Depends on: TTY [=y] && HAS_IOMEM [=y] && !S390
6. │ Location:
7. │ -> Device Drivers
8. │ -> Character devices
9. │ -> Enable TTY (TTY [=y])
10. │ -> Serial drivers
11. │ (1) -
> 8250/16550 and compatible serial support (SERIAL_8250 [=y])
12. │ Selects: SERIAL_CORE [=y] && SERIAL_MCTRL_GPIO [=y]
13. │ Selected by [n]:
14. │ - MWAVE [=n] && X86 && TTY [=y]
步骤 3:配置串口 console
在 Linux 内核中,console 用于控制台输出。可以指定 console 为 UART 设备,这样就能在内核启动过程中将日志信息输出到指定的串口设备。console 参数可以通过 U-Boot 传入或者设备树 chosen 节点中的 bootargs 来指定。
-
通过 U-Boot 传入:
setenv bootargs console=ttyS0,115200boot -
通过设备树 chosen 节点的 bootargs 指定:
chosen {bootargs = "console=ttyS0,115200";};console 的参数格式为:
console=<device>,<options>
-
<device>:指定控制台设备。常见的设备名包括:ttyS0:第一个串口设备(COM1)ttyAMA0:ARM 平台上的第一个串口设备ttyUSB0:第一个 USB 串口设备
-
<options>:指定 UART 的参数,通常是波特率。
earlycon 用于在内核启动的早期阶段(即主控制台初始化之前)启用串口输出。这对于调试内核启动过程中的问题非常有用。与 console 一样,配置 earlycon 只需要在 U-Boot 传参,或者设备树 bootargs 属性中增加
"earlycon" 字符串即可。其参数通过 chosen 节点中的 stdout-path 来指定。示例如下:
chosen {
stdout-path = "serial0:115200n8";
};
stdout-path 参数说明:
serial0:指定串口设备。serial0通常对应设备树中的一个串口节点。115200n8:指定波特率为 115200,数据位为 8,无奇偶校验,1 个停止位。
开发参考
Linux 系统中 uart 驱动注册后会在 /dev
目录下生成相应的 tty 设备,无需再注册额外的设备驱动,在应用层可以直接读写 tty 节点来发送接收串口数据。示例如下:
包含头文件
#include <stdio.h> // 标准输入输出
#include <stdlib.h> // 标准库
#include <string.h> // 字符串处理
#include <unistd.h> // POSIX 操作
#include <fcntl.h> // 文件控制
#include <termios.h> // 串口控制
#include <errno.h> // 错误代码
#include <sys/ioctl.h> // 输入输出控制
打开串口设备
打开串口设备文件一般使用 open() 系统调用。Linux 系统中的串口设备通常位于 /dev/
目录下。MTT E300 的 UART0–6 的设备名称为 ttyMT[0–6],UART7 的串口名称为 ttyS0。
int uart_fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (uart_fd == -1) {
perror("Open UART failed");
return -1;
}
O_RDWR:以读写方式打开
O_NOCTTY:不将设备分配为控制终端
O_NDELAY:打开时不阻塞,即使串口 不可用也能打开
配置串口参数
在打开串口后,需要设置串口的参数,包括波特率、数据位、校验位和停止位。可以使用 tcgetattr()
获取当前串口设置,再用 tcsetattr() 设置新配置。
struct termios options;
// 获取串口当前设置
if (tcgetattr(uart_fd, &options) != 0) {
perror("tcgetattr failed");
close(uart_fd);
return -1;
}
// 设置波特率
cfsetispeed(&options, B9600); // 输入波特率为 9600
cfsetospeed(&options, B9600); // 输出波特率为 9600
// 设置数据位为 8 位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// 设置停止位为 1 位
options.c_cflag &= ~CSTOPB;
// 设置无校验
options.c_cflag &= ~PARENB;
options.c_cflag &= ~PARODD;
// 禁用流控
options.c_cflag &= ~CRTSCTS;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// 设置字符处理模式
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 设置输入输出不阻塞
options.c_oflag &= ~OPOST;
// 设置最小读取字符数为 1
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 0;
// 将设置写入串口
if (tcsetattr(uart_fd, TCSANOW, &options) != 0) {
perror("tcsetattr failed");
close(uart_fd);
return -1;
}
读取串口数据
串口配置完成后,可使用 read() 函数从串口设备中读取数据。以下是一个读取数据的示例:
char buffer[256]; // 缓冲区
int nread;
// 从串口读取数据
nread = read(uart_fd, buffer, sizeof(buffer) - 1);
if (nread < 0) {
perror("Read failed");
close(uart_fd);
return -1;
}
buffer[nread] = '\0'; // 添加字符串结束符
printf("Received: %s\n", buffer);
写入串口数据
可以使用 write() 函数向串口设备发送数据:
const char *msg = "Hello UART!";
int nwrite = write(uart_fd, msg, strlen(msg));
if (nwrite < 0) {
perror("Write failed");
close(uart_fd);
return -1;
}
printf("Sent: %s\n", msg);
关闭串口
close(uart_fd);
调试方法说明
确认串口设备
在 Linux 中,UART 设备驱动注册成功后会在 /dev/ 目录 下生成对应的 tty 节点:
ttyS0:代表 uart7ttyMT[0-6]:代表 uart0 至 uart6
查看当前串口配置
使用 stty 命令查看串口当前参数配置:
stty -F /dev/ttyS0 -a
请将 /dev/ttyS0 替换为实际的串口设备名。输出结果将显示波特率、数据位、停止位、校验位等信息。
配置串口参数
配置串口参数可以使用 stty 命令,以下是常用参数说明:
-
波特率:
1152009600
-
数据位:
cs5:5 位cs6:6 位cs7:7 位cs8:8 位(常用)
-
停止位:
-cstopb:1 位(默认)cstopb:2 位
-
校验位:
-parenb:无校验parenb:启用校验- 默认为偶校验
- 可加
parodd启用奇校验
示例:设置波特率为 115200,8 位数据,1 位停止位,无校验:
stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb
测试串口通信
- 发送数据:
echo "Hello" > /dev/ttyS0
- 接收数据:
cat /dev/ttyS0
关闭流控
stty -F dev/ttyS0 -ixon -clocal
设置为 raw 模式
sty -F /dev/ttyS0 raw

