串口通信作为一种基础的数据传输方式,在嵌入式开发、工业控制、设备调试等领域仍被广泛应用,Linux系统提供了丰富的串口操作函数,这些函数基于POSIX标准,通过终端设备文件(如/dev/ttyS0、/dev/ttyUSB0等)实现对串口的配置、读写及控制,本文将详细介绍Linux串口相关的核心函数、配置参数、读写操作及实战应用,帮助开发者掌握串口编程的关键技术。

Linux串口设备文件与权限管理
在Linux系统中,串口设备以文件形式存在于/dev目录下,常见的有物理串口(如/dev/ttyS0对应COM1)和USB转串口设备(如/dev/ttyUSB0),应用程序通过标准文件I/O函数(如open、read、write)操作这些设备文件,但需确保进程对设备文件具有读写权限,默认情况下,串口设备属于dialout组(或特定用户组),可通过chmod 666 /dev/ttyS0修改权限,或将用户添加到dialout组(sudo usermod -aG dialout $USER)以获得访问权限。
打开串口设备时,推荐使用open()函数的O_RDWR(读写模式)、O_NOCTTY(防止终端成为控制终端)和O_NDELAY(非阻塞模式,可选)标志位组合,
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
perror("Failed to open serial port");
exit(EXIT_FAILURE);
}
核心串口配置函数:termios结构体详解
Linux串口的配置通过termios结构体实现,该结构体定义在<termios.h>中,包含控制模式、输入模式、输出模式、本地模式及控制字符等参数,核心配置函数包括tcgetattr()(获取当前配置)、tcsetattr()(设置配置)及波特率设置函数cfsetispeed()、cfsetospeed()。
获取与设置串口属性
tcgetattr()函数用于读取当前串口配置,存储到termios结构体中:
struct termios options; tcgetattr(fd, &options);
tcsetattr()函数用于修改配置,其第三个参数决定生效时机:TCSANOW(立即生效)、TCSADRAIN(输出完成后生效)、TCSAFLUSH(清空输入/输出缓冲区后生效)。

波特率配置
波特率通过c_cflag成员的Baud Rate位域设置,例如设置9600波特率:
cfsetispeed(&options, B9600); // 输入波特率 cfsetospeed(&options, B9600); // 输出波特率
常用波特率常量包括B300、B1200、B9600、B115200等,需确保两端设备波特率一致。
数据位、停止位与校验位
- 数据位:通过
c_cflag的CSIZE位域设置,如CS8(8位数据位),需先清除原有设置:options.c_cflag &= ~CSIZE; // 清除数据位设置 options.c_cflag |= CS8; // 设置8位数据位
- 停止位:通过
c_cflag的CSTOPB位控制,1位停止位时不设置该位,2位停止位时设置options.c_cflag |= CSTOPB;。 - 校验位:通过
c_cflag的PARENB、PARODD控制,如无校验位时options.c_cflag &= ~PARENB;奇校验时options.c_cflag |= PARENB | PARODD;偶校验时options.c_cflag |= PARENB并清除PARODD。
其他关键配置
- 硬件流控:禁用RTS/CTS流控时,需清除
c_cflag的CRTSCTS位(options.c_cflag &= ~CRTSCTS)。 - 软件流控:通过
c_iflag的IXON、IXOFF等位控制,通常禁用软件流控(options.c_iflag &= ~(IXON | IXOFF | IXANY))。 - 输入/输出模式:默认为规范模式(ICANON),此时数据按行处理;非规范模式适合二进制数据传输,需设置
options.c_lflag &= ~ICANON,并通过c_cc中的VMIN(最小读取字节数)和VTIME(超时时间,单位为0.1秒)控制读取行为。
串口读写操作与错误处理
串口数据传输主要通过read()和write()函数实现,需注意串口是流式设备,数据可能被拆分成多次读取。
读取数据
read()函数从串口缓冲区读取数据到指定缓冲区,返回读取的字节数(出错时返回-1),非阻塞模式下,若无数据可读则返回-1并设置errno为EAGAIN;阻塞模式下会等待数据到达,读取10字节数据:
char buffer[1024];
int n = read(fd, buffer, 10);
if (n < 0) {
perror("Failed to read from serial port");
} else {
printf("Read %d bytes: %.*s\n", n, n, buffer);
}
写入数据
write()函数将缓冲区数据写入串口,返回实际写入的字节数,需处理部分写入的情况(例如网络延迟导致写入中断),可通过循环写入确保数据完整:

const char *data = "Hello, Serial Port!";
int len = strlen(data);
int written = 0;
while (written < len) {
int n = write(fd, data + written, len - written);
if (n < 0) {
perror("Failed to write to serial port");
break;
}
written += n;
}
缓冲区控制与错误恢复
- 清空缓冲区:
tcflush()函数可清空输入/输出缓冲区,参数TCIFLUSH(输入缓冲区)、TCOFLUSH(输出缓冲区)、TCIOFLUSH(双向清空)。 - 等待输出完成:
tcdrain()函数阻塞程序,直到所有输出数据发送完毕。 - 错误处理:串口通信中常见错误包括帧错误(FE)、奇偶校验错误(PE)、溢出错误(OE),可通过
c_iflag的IGNPAR(忽略奇偶校验错误)或PARMRK(标记错误字符)处理。
实战示例:简单串口回显程序
以下是一个完整的串口回显程序,实现从串口读取数据并写回:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
int main() {
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("Failed to open serial port");
return -1;
}
struct termios options;
tcgetattr(fd, &options);
// 配置串口参数:9600波特率,8数据位,无校验,1停止位
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag &= ~PARENB; // 无校验位
options.c_cflag &= ~CSTOPB; // 1停止位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8; // 8数据位
options.c_cflag &= ~CRTSCTS; // 禁用硬件流控
// 输入/输出模式:非规范模式,不处理控制字符
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST; // 原始输出模式
// 设置超时:100ms内读取至少1字节
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 1;
tcsetattr(fd, TCSANOW, &options);
char buffer[1024];
while (1) {
int n = read(fd, buffer, sizeof(buffer) - 1);
if (n > 0) {
buffer[n] = '\0';
printf("Received: %s", buffer);
write(fd, buffer, n); // 回显
}
}
close(fd);
return 0;
}
编译并运行程序后,串口助手发送数据即可看到回显效果。
应用场景与注意事项
Linux串口函数广泛应用于嵌入式设备调试(如树莓派、Arduino)、工业传感器数据采集(如温湿度、GPS模块)、医疗设备通信等领域,开发时需注意:
- 权限管理:确保程序对串口设备具有访问权限,避免因权限不足导致操作失败。
- 参数匹配:串口两端需保持波特率、数据位、停止位、校验位一致,否则数据传输会出现乱码。
- 异常处理:对
read()、write()等函数的返回值进行检查,处理部分读写、设备断开等情况。 - 资源释放:程序退出前需调用
close()关闭串口,避免资源泄漏;若程序异常终止,可通过flock()或文件锁机制防止其他进程访问。
通过合理配置termios结构体和灵活运用串口函数,可高效实现Linux系统下的串口通信功能,满足不同场景的数据传输需求。