在Linux系统中,串口通信是一种重要的设备交互方式,广泛应用于嵌入式开发、工业控制、设备调试等场景,Linux将串口设备视为文件进行管理,通过标准的文件操作接口实现数据的收发,同时提供了丰富的工具和编程接口来支持串口配置与通信,本文将从串口设备概述、核心类与接口、配置方法、编程实现及常见问题五个方面,详细介绍Linux串口相关的技术要点。

串口设备概述
Linux系统中的串口设备通常以/dev/目录下的特殊文件形式存在,常见的设备名包括/dev/ttyS0、/dev/ttyS1等(表示传统串口),以及/dev/ttyUSB0、/dev/ttyACM0等(表示USB转串口设备),这些设备文件由内核驱动管理,用户程序可以通过打开、读写、关闭等文件操作函数与串口设备交互。
串口的核心参数包括波特率、数据位、停止位、校验位和流控,这些参数的匹配是确保通信正常的前提,波特率决定了数据传输的速率(如9600、115200),数据位通常为7或8位,停止位为1或2位,校验位可选无校验、奇校验或偶校验,流控则通过硬件(RTS/CTS)或软件(XON/XOFF)控制数据传输的启停。
核心类与接口
Linux串口通信的核心实现依赖于系统调用和标准C库函数,主要分为设备管理、配置控制和数据传输三类接口。
设备管理接口
设备管理是串口通信的基础,主要通过open()、close()、ioctl()等函数实现:
open():以读写方式打开串口设备文件,返回文件描述符(fd)。int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY),其中O_NOCTTY防止终端成为控制终端,O_NDELAY以非阻塞方式打开。close():关闭文件描述符,释放系统资源,如close(fd)。ioctl():用于配置串口参数或获取设备状态,是串口配置的核心函数。
配置控制接口
串口参数通过termios结构体进行配置,该结构体定义在<termios.h>中,包含多个字段控制串口行为:

c_cflag:控制波特率、数据位、停止位、校验位等硬件参数。c_iflag:处理输入字符,如奇偶校验、软件流控等。c_oflag:控制输出处理,如回车换行转换。c_lflag:控制终端模式,如是否启用回显、规范模式等。c_cc:定义字符超时控制,如VMIN和VTIME。
配置时需先通过tcgetattr(fd, &options)获取当前参数,修改termios结构体后,再通过tcsetattr(fd, TCSANOW, &options)应用配置。
串口配置方法
串口配置需根据通信需求设置termios结构体参数,以下是关键配置步骤及参数说明:
设置波特率
波特率通过c_cflag的Bxxxx常量设置,如B9600、B115200,使用cfsetispeed()和cfsetospeed()分别设置输入和输出波特率,确保两者一致:
cfsetispeed(&options, B115200); cfsetospeed(&options, B115200);
配置数据位、停止位与校验位
- 数据位:通过
c_cflag的CSIZE掩码设置,如CS8表示8位数据位。 - 停止位:通过
CSTOPB控制,设置时表示2位停止位,不设置时为1位。 - 校验位:通过
PARENB(启用校验)、PARODD(奇校验,不设置则为偶校验)控制。
示例代码:options.c_cflag &= ~PARENB; // 无校验 options.c_cflag &= ~CSTOPB; // 1位停止位 options.c_cflag &= ~CSIZE; // 清除数据位掩码 options.c_cflag |= CS8; // 8位数据位
流控设置
硬件流控通过c_cflag的CRTSCTS启用,软件流控通过c_iflag的IXON、IXOFF控制:
options.c_cflag |= CRTSCTS; // 启用硬件流控 options.c_iflag |= (IXON | IXOFF); // 启用软件流控
配置超时与阻塞模式
- 非阻塞模式:
O_NDELAY标志位,立即返回读写结果,若无数据则返回-1。 - 阻塞模式:通过
c_cc的VMIN和VTIME设置,例如VMIN=1、VTIME=0表示读取至少1个字符后返回,VMIN=0、VTIME=5表示等待5*0.1秒后返回(即使无数据)。
串口编程实现示例
以下是一个简单的串口收发程序示例,展示打开设备、配置参数、读写数据及关闭设备的过程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int init_serial(const char *device, int baudrate) {
int fd = open(device, 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, baudrate);
cfsetospeed(&options, baudrate);
// 8位数据位,无校验,1位停止位
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_iflag &= ~(IXON | IXOFF | IXANY);
// 原始输出模式
options.c_oflag &= ~OPOST;
// 设置超时:100ms内读取至少1个字符
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 1;
tcsetattr(fd, TCSANOW, &options);
return fd;
}
int main() {
const char *device = "/dev/ttyS0";
int baudrate = B115200;
int fd = init_serial(device, baudrate);
if (fd < 0) {
return -1;
}
char send_buf[] = "Hello, Serial Port!";
char recv_buf[256];
// 发送数据
write(fd, send_buf, strlen(send_buf));
printf("Sent: %s\n", send_buf);
// 接收数据
int n = read(fd, recv_buf, sizeof(recv_buf));
if (n > 0) {
recv_buf[n] = '\0';
printf("Received: %s\n", recv_buf);
}
close(fd);
return 0;
}
常见问题与解决方案
在串口通信中,常会遇到数据丢失、乱码、无法打开设备等问题,以下是典型问题及解决方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法打开设备 | 设备权限不足 | 使用chmod修改权限,如chmod 666 /dev/ttyS0,或通过udev规则永久授权 |
| 数据乱码 | 波特率、数据位等参数不匹配 | 检查两端串口配置参数是否一致 |
| 数据丢失 | 缓冲区溢出或流控未启用 | 增大缓冲区,启用硬件/软件流控 |
| 阻塞无法读写 | 未设置超时或阻塞模式 | 配置VMIN和VTIME,或使用O_NONBLOCK标志位 |
| USB转串口设备不识别 | 驱动未加载 | 安装usbutils工具,使用lsusb识别设备,加载对应驱动(如ch341、ftdi) |
Linux串口通信通过文件接口和termios结构体提供了灵活的配置与操作方式,掌握设备管理、参数配置及编程接口是实现可靠串口通信的关键,在实际应用中,需根据具体需求选择合适的波特率、数据位等参数,并结合流控机制和超时设置优化数据传输稳定性,通过本文介绍的核心类、配置方法和示例代码,开发者可快速搭建Linux串口通信系统,并有效解决常见问题。


















