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

为何每次运行C++程序时termios串口设置无法重置?

问题分析与修复方案

核心问题排查

你的程序出现异常的主要原因有两点:

  1. termios配置顺序完全错误:你先手动修改了tty结构体参数,再调用tcgetattr(sp, &tty),这会用串口当前属性覆盖你之前的修改,导致配置根本没生效。
  2. 读取逻辑未处理行拼接:即使串口配置正确,read()调用也不一定能一次返回完整的GPS日志行,直接把单次读取内容当作完整行处理,必然引发解析错误。

另外,pygpsclient退出后可能未恢复串口规范模式,而你的配置代码因顺序错误无法修正该状态,所以必须插拔设备重置串口。

具体修复步骤

1. 修正termios配置顺序

必须先调用tcgetattr获取当前串口属性,再基于该结构体修改参数,最后调用tcsetattr生效。同时关闭GPS串口不需要的回显、流控等功能。

修改后的configPort函数:

void configPort(int sp)
{
    struct termios tty;

    // 先获取当前串口属性,这一步必须放在最前面
    if (tcgetattr(sp, &tty) != 0)
    {
        std::cerr << "Error getting serial port attributes" << std::endl;
        return;
    }

    // 重置所有标志位,清除残留配置
    cfmakeraw(&tty);
    // 恢复规范模式(按行读取)
    tty.c_lflag |= ICANON;

    // 配置波特率
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    // 配置行结束符(GPS日志以换行符结尾)
    tty.c_cc[VEOL] = '\n';
    tty.c_cc[VMIN] = 1; // 至少读取1字节
    tty.c_cc[VTIME] = 0; // 无超时

    // 禁用不需要的功能:回显、流控等
    tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ISIG);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
    tty.c_oflag &= ~OPOST;

    // 应用配置,TCSANOW立即生效,TCSAFLUSH清空缓冲区
    if (tcsetattr(sp, TCSANOW | TCSAFLUSH, &tty) != 0)
    {
        std::cerr << "Error setting serial port attributes." << std::endl;
        return;
    }
}

2. 修复读取逻辑,实现行缓存拼接

添加类成员缓存字符串,每次读取内容先追加到缓存,分割出完整行后再调用回调处理。

修改后的Read_Class完整代码:

class Read_Class
{
private:
    std::string line_buffer; // 缓存不完整的行内容
public:
    using LineCallback = std::function<void(std::string &)>;

    int openSerial()
    {
        // 添加O_NOCTTY(不将串口设为控制终端)和O_NONBLOCK(非阻塞模式)
        const int serial_port = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK);

        if (serial_port < 0)
        {
            std::cout << "serial port: " << serial_port << std::endl;
            std::cerr << "Error opening serial port." << std::endl;
        }
        else
        {
            fcntl(serial_port, F_SETFL, 0); // 恢复阻塞模式,配合poll使用
        }
        return serial_port;
    }

    // 上面修改后的configPort函数放在这里

    void readLogs(int sp, LineCallback callback)
    {
        const int buff_size = 200;
        char buffer[buff_size];
        memset(buffer, '\0', buff_size);

        struct pollfd fds[1];
        fds[0].fd = sp;
        fds[0].events = POLLIN;

        while (true)
        {
            int pollResult = poll(fds, 1, 200);
            if (pollResult > 0 && (fds[0].revents & POLLIN))
            {
                ssize_t bytesRead = read(sp, buffer, sizeof(buffer)-1); // 留1字节给终止符
                if (bytesRead > 0)
                {
                    buffer[bytesRead] = '\0'; // 确保字符串终止
                    line_buffer += buffer; // 追加到缓存

                    // 分割并处理完整的行
                    size_t newline_pos;
                    while ((newline_pos = line_buffer.find('\n')) != std::string::npos)
                    {
                        std::string line = line_buffer.substr(0, newline_pos+1); // 包含换行符
                        if (!line.empty() && line[0] == '$') // 判断是否为NMEA语句
                        {
                            callback(line);
                        }
                        // 移除已处理的行,保留剩余不完整内容
                        line_buffer = line_buffer.substr(newline_pos+1);
                    }

                    memset(buffer, '\0', buff_size);
                }
            }
            else if (pollResult == -1)
            {
                std::cerr << "Poll error occurred." << std::endl;
                break;
            }
        }
    }
};

3. 简化main函数逻辑

确保回调函数逻辑简洁,若不需要sc_dt可直接移除捕获列表:

int main()
{
    Read_Class rc;
    const int serial = rc.openSerial();
    rc.configPort(serial);
    if (serial >= 0)
    {
        rc.readLogs(serial, [](std::string &line)
                    {
                        std::cout << line << std::endl;
                    });
    }
    close(serial);
    return 0;
}

验证方法

修改后无需插拔GPS设备,直接运行程序:

  1. 执行stty -F /dev/ttyUSB0查看串口属性,确认参数与配置一致。
  2. 程序应稳定输出完整的NMEA格式GPS日志行,不再出现单字节读取导致的崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:15:33