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

Linux UART通信首字节丢失问题求助

串口通信首字节丢失问题排查与解决方案

问题背景

通过USB转TTL(3.3V)转换器搭建UART通信链路:

  • 发送端:Ubuntu 20.04系统PC
  • 接收端:运行Petalinux(analogdevicesinc/plutosdr-fw)的嵌入式设备,UART驱动为xilinx_uartps

遇到首字节丢失问题:

  • 发送{'A','B','C'}时,接收端仅收到{'B','C'}
  • 发送带空终止符的{'\0','A','B','C'}时,接收端能正常接收所有字节
  • 逻辑分析仪确认PC端发送无异常,问题出在嵌入式设备接收环节

发送端代码

int main() {
  // 打开串口。根据需要修改设备路径(当前为标准FTDI USB-UART线缆类型设备)
  int serial_port = open("/dev/ttyUSB3", O_RDWR);

  // 创建新的termios结构体,按惯例命名为'tty'
  struct termios tty;

  // 读取现有设置,并处理可能的错误
  if(tcgetattr(serial_port, &tty) != 0) {
      printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
      return 1;
  }
  
    tty.c_cflag &= ~PARENB; // 清除奇偶校验位,禁用奇偶校验(最常用)
    tty.c_cflag &= ~CSTOPB; // 清除停止位域,通信仅使用1个停止位(最常用)
    tty.c_cflag &= ~CSIZE; // 清除所有设置数据位长度的位
    tty.c_cflag |= CS8; // 每字节8位(最常用)
    tty.c_cflag &= ~CRTSCTS; // 禁用RTS/CTS硬件流控(最常用)
    tty.c_cflag |= CREAD | CLOCAL; // 开启读取功能并忽略控制线路(CLOCAL=1)

    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO; // 禁用回显
    tty.c_lflag &= ~ECHOE; // 禁用擦除功能
    tty.c_lflag &= ~ECHONL; // 禁用换行回显
    tty.c_lflag &= ~ISIG; // 禁用INTR、QUIT和SUSP信号的解析
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // 禁用对接收字节的特殊处理

    tty.c_oflag &= ~OPOST; // 禁止对输出字节进行特殊解析(如换行符)
    tty.c_oflag &= ~ONLCR; // 禁止将换行转换为回车/换行
    // tty.c_oflag &= ~OXTABS; // 禁止将制表符转换为空格(Linux中不存在)
    // tty.c_oflag &= ~ONOEOT; // 禁止在输出中移除C-d字符(0x004)(Linux中不存在)

    tty.c_cc[VTIME] = 10;    // 最多等待1秒(10个十分之一秒),收到任意数据立即返回
    tty.c_cc[VMIN] = 0;
    cfsetispeed(&tty, B115200);
    cfsetospeed(&tty, B115200);


  // 保存串口设置,并检查错误
  if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
      printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
      return 1;
  }
  while(1){
    int oneTime = 0;
    scanf("%d", &oneTime); // 每次输入"1"时发送消息
    unsigned char msg[] = { '\0','A', 'B', 'C', 'D', 'E' };
    if(oneTime == 1)
      printf("sending");
      write(serial_port, msg, sizeof(msg));
      oneTime = 0;
  }  
  close(serial_port);
  return 0; 
};

接收端代码

int main(){

    // 打开串口。根据需要修改设备路径(当前为标准FTDI USB-UART线缆类型设备)
    int serial_port = open("/dev/ttyPS0", O_RDWR);

    // 创建新的termios结构体,按惯例命名为'tty'
    struct termios tty;

    // 读取现有设置,并处理可能的错误
    if(tcgetattr(serial_port, &tty) != 0) {
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
        return 1;
    }

    tty.c_cflag &= ~PARENB; // 清除奇偶校验位,禁用奇偶校验(最常用)
    tty.c_cflag &= ~CSTOPB; // 清除停止位域,通信仅使用1个停止位(最常用)
    tty.c_cflag &= ~CSIZE; // 清除所有设置数据位长度的位
    tty.c_cflag |= CS8; // 每字节8位(最常用)
    tty.c_cflag &= ~CRTSCTS; // 禁用RTS/CTS硬件流控(最常用)
    tty.c_cflag |= CREAD | CLOCAL; // 开启读取功能并忽略控制线路(CLOCAL=1)

    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO; // 禁用回显
    tty.c_lflag &= ~ECHOE; // 禁用擦除功能
    tty.c_lflag &= ~ECHONL; // 禁用换行回显
    tty.c_lflag &= ~ISIG; // 禁用INTR、QUIT和SUSP信号的解析
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // 禁用对接收字节的特殊处理

    tty.c_oflag &= ~OPOST; // 禁止对输出字节进行特殊解析(如换行符)
    tty.c_oflag &= ~ONLCR; // 禁止将换行转换为回车/换行
    // tty.c_oflag &= ~OXTABS; // 禁止将制表符转换为空格(Linux中不存在)
    // tty.c_oflag &= ~ONOEOT; // 禁止在输出中移除C-d字符(0x004)(Linux中不存在)

    tty.c_cc[VTIME] = 10;    // 最多等待1秒(10个十分之一秒),收到任意数据立即返回
    tty.c_cc[VMIN] = 0;
    cfsetispeed(&tty, B115200);
    cfsetospeed(&tty, B115200);

    // 保存串口设置,并检查错误
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
        return 1;
    }


    // 为读取缓冲区分配内存,根据需要设置大小
    char read_buf [256];

    memset(&read_buf, '\0', sizeof(read_buf));
    int num_bytes;

    while(1){

        num_bytes = read(serial_port, &read_buf, sizeof(read_buf));

        if (num_bytes <= 0) {
            printf("Error reading: %s", strerror(errno));
            break;
        }
        printf("Read %i bytes. Received message: %s", num_bytes, read_buf);    
        
    }

  close(serial_port);

return 0;
}

解决方案建议

  • 清空接收缓冲区
    在接收端完成串口属性配置后,立即调用tcflush(serial_port, TCIFLUSH)清空输入缓冲区,消除串口初始化过程中可能残留的无效数据,避免干扰首次接收。

  • 调整接收超时与最小字节参数
    当前VMIN=0的非阻塞模式下,驱动可能因初始化延迟漏掉首字节。可尝试:

    • 将VMIN设置为1,确保read()至少收到1字节才返回;
    • 保持VMIN=0不变,在进入接收循环前执行一次空读操作,消耗掉缓冲区中的残留数据。
  • 修正接收数据打印逻辑
    原代码用%s打印缓冲区,若首字节为0x00或非ASCII字符,printf会提前截断输出,导致误判。改为逐字节打印十六进制值,准确验证接收数据:

    printf("Read %i bytes. Received message: ", num_bytes);
    for(int i=0; i<num_bytes; i++){
        printf("0x%02X ", read_buf[i]);
    }
    printf("\n");
    
  • 检查驱动与设备树配置
    确认xilinx_uartps驱动的中断触发方式、流控配置是否正确,Petalinux设备树中UART节点的参数(如status、clock-frequency)是否匹配硬件实际情况,必要时添加唤醒相关配置。

内容的提问来源于stack exchange,提问作者tripleclick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:27:26