在Linux系统中,串口通信是一种常见且重要的设备交互方式,广泛应用于嵌入式开发、工业控制、设备调试等场景,通过串口,程序可以与外部设备进行双向数据传输,实现指令下发、状态监控等功能,本文将从串口基础概念、Linux串口设备文件、核心编程接口、代码实现示例及注意事项等方面,详细介绍Linux串口编程的相关知识。

Linux串口设备与基础配置
Linux系统将串口设备抽象为特殊的字符设备文件,通常位于/dev目录下,常见的串口设备文件包括/dev/ttyS0(COM1)、/dev/ttyS1(COM2)等,用于内置串口;而USB转串口设备通常表现为/dev/ttyUSB0、/dev/ttyACM0等形式,在编程前,需确认设备文件是否存在并具有读写权限,可通过ls -l /dev/tty*命令查看,必要时使用chmod调整权限。
串口通信的核心参数包括波特率、数据位、停止位、校验位和流控,这些参数必须与通信两端设备保持一致,否则会导致数据传输错误,常见的配置为:波特率9600、数据位8、停止位1、无校验位、无流控,在Linux中,可通过stty命令快速测试串口配置,如stty -F /dev/ttyS0 9600 cs8 -cstopb -parenb命令可将串口设置为上述常用参数。
串口编程核心API
Linux串口编程主要通过文件I/O操作实现,其核心步骤与普通文件读写类似,但需额外配置串口属性,以下是关键API及流程:

-
打开串口设备
使用open()函数打开串口设备文件,需指定O_RDWR(读写模式)和O_NOCTTY(防止终端成为控制终端)标志,
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
其中O_NDELAY表示非阻塞模式,若需阻塞模式可省略该标志。 -
配置串口属性
打开串口后,需通过termios结构体配置串口参数,主要步骤包括:- 获取当前配置:
tcgetattr(fd, &oldtio) - 修改配置:设置波特率(
cfsetispeed/cfsetospeed)、数据位(cs8)、停止位(CSTOPB)、校验位(PARENB)等 - 应用配置:
tcsetattr(fd, TCSANOW, &newtio)
示例代码片段:struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag &= ~PARENB; // 无校验位 options.c_cflag &= ~CSTOPB; // 1位停止位 options.c_cflag &= ~CSIZE; // 清除数据位设置 options.c_cflag |= CS8; // 8位数据位 tcsetattr(fd, TCSANOW, &options);
- 获取当前配置:
-
读写串口数据

- 读数据:使用
read()函数读取串口缓冲区数据,如int n = read(fd, buf, sizeof(buf)); - 写数据:使用
write()函数向串口发送数据,如write(fd, "Hello", 5);
默认情况下,read()为阻塞调用,可设置VTIME(超时 deciseconds)和VMIN(最小读取字符数)实现非阻塞读取,options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0;表示读取至少1个字符或超时。
- 读数据:使用
-
关闭串口
使用close()函数关闭串口设备,如close(fd);,若需确保数据发送完成,可在关闭前调用tcdrain(fd)等待输出缓冲区清空。
完整代码示例
以下是一个简单的串口收发程序示例,实现从串口读取数据并回显:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
int main() {
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
perror("Failed to open serial port");
return -1;
}
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= (CLOCAL | CREAD); // 忽略调制解调器控制线,启用接收
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入模式
options.c_oflag &= ~OPOST; // 原始输出模式
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 0;
tcsetattr(fd, TCSANOW, &options);
char buf[1024];
while (1) {
int n = read(fd, buf, sizeof(buf) - 1);
if (n > 0) {
buf[n] = '\0';
printf("Received: %s", buf);
write(fd, buf, n); // 回显数据
}
}
close(fd);
return 0;
}
注意事项与调试技巧
- 权限问题:确保程序对串口设备有读写权限,可临时通过
sudo运行或设置用户组权限。 - 波特率匹配:通信前务必确认两端设备波特率一致,否则数据可能乱码或丢失。
- 流控配置:若使用硬件流控(RTS/CTS),需在
c_cflag中启用CRTSCTS标志;软件流控(XON/XOFF)则通过c_iflag设置。 - 调试工具:使用
minicom或screen命令行工具测试串口是否正常工作,例如minicom -D /dev/ttyS0。 - 多线程安全:在多线程环境中,需使用互斥锁保护串口文件描述符,避免并发读写冲突。
开发者可以快速掌握Linux串口编程的核心方法,并根据实际需求调整参数和逻辑,串口通信虽然基础,但在物联网、嵌入式系统等领域仍具有不可替代的作用,深入理解其原理和实现方式对系统开发至关重要。


















