You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 19:15:02