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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:17:19