Linux C++串口通信调用read()无法接收RS485设备返回数据问题
Ubuntu 18 C++ RS485串口通信接收阻塞问题原因汇总
问题复现代码
#include <iostream> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <cstring> int main(){ char *dev = "/dev/ttyUSB0"; int fd = 0; char message[3]; char reply[1024]; struct termios oldtio, newtio; memset(&oldtio, 0, sizeof(oldtio)); memset(&newtio, 0, sizeof(newtio)); //Open COM Port; fd = open(dev, O_RDWR | O_NOCTTY); if(fd < 0){ std::cout << "COM Port Error" << std::endl; return -1; } tcgetattr(fd, &oldtio); //Serial Settings cfsetispeed(&newtio, B115200); cfsetospeed(&newtio, B115200); newtio.c_iflag = IGNPAR; newtio.c_cflag |= CS8; newtio.c_cflag |= CREAD; newtio.c_cflag &= ~CSTOPB; newtio.c_lflag = ICANON; tcsetattr(fd, TCSANOW, &newtio); //Set Message(This is not real one because it will be too long) messages[0] = char(0x02); messages[1] = char(0x32); messages[2] = char(0x03); //Send Message write(fd, message, 3); //Receive Message unsigned int length = 0; length = read(fd, reply, 1024); std::cout << reply << std::endl; //Close Serial Port tcsetattr(fd, TCSANOW, &oldtio); close(fd); }
问题原因及修复方案
- 1. 错误启用规范模式(ICANON)是阻塞的核心原因
规范模式下Linux串口的read调用必须等到换行符\n(ASCII 0x0A)才会返回,但你的应答报文以ETX(0x03)结尾,不存在换行符,因此read会永久阻塞。
修复方案:关闭规范模式,改用原始模式处理二进制/自定义格式报文:
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 关闭规范模式、回显、终端信号处理 newtio.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控,避免STX/ETX被识别为流控字符丢弃 newtio.c_oflag &= ~OPOST; // 关闭输出特殊格式转换
- 2. termios参数初始化逻辑错误
代码直接对newtio执行memset清零,会丢失串口的默认配置参数,正确的做法是在原有配置基础上修改:
// 删除memset(&newtio, 0, sizeof(newtio)); 这行代码 tcgetattr(fd, &newtio); // 先读取当前串口的完整配置到newtio // 再执行波特率、数据位等自定义参数修改
- 3. 缺少接收超时配置,无容错机制
原始模式下需要配置c_cc数组的VMIN和VTIME参数控制read行为,避免异常场景下永久阻塞:
newtio.c_cc[VMIN] = 1; // 最少收到1字节就返回 newtio.c_cc[VTIME] = 5; // 接收超时时间500ms,超时后read直接返回0
- 4. 其他可优化问题
- 代码存在笔误:定义的数组名为
message,赋值时写的是messages,多了后缀s,会导致编译错误。 - 应答输出缺字符串结束符:
read仅会写入收到的字节,不会自动添加\0,直接调用cout会输出乱码甚至触发内存越界,修复方案:length = read(fd, reply, sizeof(reply)-1); // 预留1位存储结束符 if (length > 0) { reply[length] = '\0'; std::cout << reply << std::endl; } - 缺硬件流控配置:如果没有使用硬件流控,需要主动关闭该配置:
newtio.c_cflag &= ~CRTSCTS;
- 代码存在笔误:定义的数组名为
内容的提问来源于stack exchange,提问作者ajiwaiorange
相关产品推荐
相关产品推荐

