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
相关产品推荐
相关产品推荐

