Ubuntu 22.04下串口持续读取异常及PyQt显示问题求助
问题排查:Ubuntu 22.04下C串口读取程序偶尔失效的问题
在Ubuntu 22.04系统中运行以下C语言串口读取程序和PyQt显示程序,已将串口的Rx与Tx短接。多数情况下程序能正常读取串口数据(通过echo "hello" >> /dev/ttyUSB0输入)、写入output.txt并在PyQt界面显示,但偶尔会出现C程序无法读取串口数据的情况,此时数据可在minicom中正常显示,请求协助排查该问题。
C语言串口读取程序
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> int main() { const char *device = "/dev/ttyUSB0"; // 替换为你的USB TTL设备名 int serialport_fd,output_fd,serial_rbytes=0,serial_wbytes=0, python_status; char serial_buffer[100]; output_fd = open("/home/inbe1e-dl3882av/navin/systecall/output.txt", O_RDWR | O_CREAT, S_IRUSR |S_IWUSR); if(output_fd< 0) { printf("Output File is not opened\n"); return 1; } serialport_fd = open(device, O_RDWR | O_NOCTTY); if(serialport_fd < 0) { printf("Serial port not opened\n"); return 1; } struct termios options; tcgetattr(serialport_fd, &options); cfsetispeed(&options, B115200); // 设置波特率 cfsetospeed(&options, B115200); options.c_cflag |= (CLOCAL | CREAD); // 启用接收器并设置本地模式 options.c_cflag &= ~PARENB; // 无校验位 options.c_cflag &= ~CSTOPB; // 1位停止位 options.c_cflag &= ~CSIZE; // 屏蔽字符大小位 options.c_cflag |= CS8; // 设置8位数据位 tcsetattr(serialport_fd, TCSANOW, &options); while(1) { read_again: serial_rbytes = read(serialport_fd,serial_buffer,sizeof(serial_buffer)); if(serial_rbytes > 0) { write_again: printf("serial Read Bytes %d and serial buffer is %s\n",serial_rbytes,serial_buffer); serial_wbytes = write(output_fd,serial_buffer,serial_rbytes); if(serial_wbytes > 0) { printf("successfully Written into the output.txt file\n"); python_status = system("python3 read.py"); } } else if(serial_rbytes <= 0) { printf("Failed to Read\n\n"); goto read_again; } } close(serialport_fd); close(output_fd); }
PyQt显示程序
import sys from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit class ConsoleWindow(QMainWindow): def __init__(self, file_path, display_duration_seconds): super().__init__() self.file_path = file_path self.display_duration_seconds = display_duration_seconds self.initUI() def initUI(self): self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) self.setWindowTitle('Console Window') self.setGeometry(100, 100, 800, 600) self.timer = QTimer(self) self.timer.timeout.connect(self.closeWindow) self.timer.start(self.display_duration_seconds * 1000) self.readFile() self.show() def readFile(self): try: with open(self.file_path, 'r') as file: data = file.read() self.text_edit.setPlainText(data) except FileNotFoundError: self.text_edit.setPlainText("File not found: " + self.file_path) def closeWindow(self): self.timer.stop() self.close() def main(file_path, display_duration_seconds): app = QApplication(sys.argv) window = ConsoleWindow(file_path, display_duration_seconds) sys.exit(app.exec_()) if __name__ == '__main__': file_path = '/home/inbe1e-dl3882av/navin/systecall/output.txt' # 替换为你的文件路径 display_duration_seconds = 5 # 调整显示时长 main(file_path, display_duration_seconds)
可能的问题点及修复建议
1. 串口配置不完整
当前程序仅配置了波特率、校验位等基础参数,未设置输入输出模式和读取超时规则,默认的规范模式(Canonical Mode)会等待换行符才返回读取结果,或因缓冲区阈值设置不合理导致数据无法被及时读取。
修复:在tcsetattr前添加以下配置:
#include <errno.h> // 需要新增头文件 // 禁用规范模式、回显及信号字符 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 禁用软件流控 options.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用输出处理,使用原始模式 options.c_oflag &= ~OPOST; // 设置读取规则:最少读1个字符,超时1秒(VTIME单位为0.1秒) options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 10;
2. read返回值处理不严谨
当read返回0表示串口连接关闭,返回负数可能是被信号中断(如EINTR)或其他错误,当前代码直接重试未做区分,可能导致无限循环无法恢复正常读取。
修复:优化read后的判断逻辑:
serial_rbytes = read(serialport_fd, serial_buffer, sizeof(serial_buffer)); if (serial_rbytes > 0) { // 正常读取逻辑 } else if (serial_rbytes == 0) { printf("Serial port connection closed\n"); break; // 或添加重连逻辑 } else { if (errno != EINTR) { perror("Serial read error"); usleep(10000); // 短暂延迟避免忙等 } goto read_again; }
3. system("python3 read.py")阻塞串口读取
每次读取数据后调用system会阻塞C程序的读取循环,Python程序运行的5秒内,串口新数据无法被及时读取,甚至因缓冲区溢出丢失。
修复:改用非阻塞方式启动Python程序,避免阻塞读取循环:
python_status = fork(); if (python_status == 0) { // 子进程执行Python程序 execlp("python3", "python3", "read.py", NULL); perror("Failed to start Python program"); exit(1); } else if (python_status < 0) { perror("Fork failed"); } // 父进程继续读取串口,不阻塞
4. 文件写入未强制刷新
write调用后数据可能留在内核缓冲区,未同步到磁盘,可能导致Python读取旧数据,同时也可能影响后续写入逻辑。
修复:写入后添加磁盘同步:
serial_wbytes = write(output_fd, serial_buffer, serial_rbytes); if (serial_wbytes == serial_rbytes) { // 确保全部写入成功 printf("successfully Written into the output.txt file\n"); fsync(output_fd); // 强制刷新到磁盘 // 启动Python程序的逻辑 } else { perror("Write to file failed"); goto write_again; }
内容的提问来源于stack exchange,提问作者Naveen Krishna Muvva
相关产品推荐
相关产品推荐

