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

LDS-02激光雷达Unix驱动开发:数据包打印不全问题求助

LDS-02激光雷达驱动读取数据不完整问题排查与修复

问题描述

为Unix系统编写LDS-02激光雷达驱动,每个完整数据包为47字节(包含头部0x54和长度字段0x2c)。当前代码通过termios每次读取94字节(2个数据包长度),定位头部+长度组合后尝试打印后续47字节,但每次输出的字节数不足且数量不固定。

问题原因

  1. 打印循环终止条件错误:定位到头部后,使用i<47作为循环终止条件完全错误——这里的i是缓冲区中的索引,不是从0开始的偏移量。例如头部在索引20的位置时,该条件只会打印到索引46,仅27个字节,远不足47个完整数据包。
  2. 缓冲区越界风险:寻找头部时未限制循环边界,当i达到缓冲区最后一个字节时,read_buf[i+1]会访问超出缓冲区的内存,导致未定义行为。
  3. 冗余代码无意义:if(true/*char_are_signed*/)中的循环没有任何实际作用,属于无效代码。
  4. 未处理部分读取情况:虽然设置了VMIN=94,但串口读取可能因数据流中断等原因返回少于94字节,当前代码直接假设读取了94字节,未做任何处理。

修复方案

  • 修正打印循环逻辑:找到头部索引start_idx后,打印从start_idx到start_idx+46的字节(共47个),同时确保不超出本次读取的字节范围。
  • 添加边界检查:寻找头部时,循环终止条件设为start_idx < num_bytes -1,避免访问越界内存。
  • 移除冗余代码:删除无意义的类型转换循环。
  • 处理部分读取场景:当读取字节数不足时,跳过本次处理或继续读取补全缓冲区。

修改后的代码

/*
    This is based on the tutorial presented in this blog article:

    https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
*/

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>

#include <fcntl.h> // Contains file controls like O_RDWR
#include <errno.h> // Error integer and strerror() function
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()

int main(int argc, char* argv[]){
    if (argc < 2) {
        printf("Usage: %s <serial_port>\n", argv[0]);
        return 1;
    }

    int serial_port = open(argv[1], O_RDWR);
    if (serial_port < 0) {
        printf("Error %i from open: %s\n", errno, strerror(errno));
        return 1;
    }

    struct termios tty;
    if(tcgetattr(serial_port, &tty) != 0) {
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
        close(serial_port);
        return 1;
    }

    tty.c_cflag &= ~PARENB; // 无校验位
    tty.c_cflag &= ~CSTOPB; // 1个停止位
    tty.c_cflag |= CS8;     // 8位数据位
    tty.c_cflag &= ~CRTSCTS; // 禁用硬件流控
    tty.c_cflag |= CREAD | CLOCAL; // 启用读取,忽略控制线路
    tty.c_lflag &= ~ICANON; // 禁用规范模式
    tty.c_lflag &= ~ECHO; // 禁用回显
    tty.c_lflag &= ~ECHOE; // 禁用擦除回显
    tty.c_lflag &= ~ECHONL; // 禁用换行回显
    tty.c_lflag &= ~ISIG; // 禁用信号字符解析
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // 禁用特殊字节处理
    tty.c_cc[VTIME] = 1;    // 设置100ms超时,避免无限等待
    tty.c_cc[VMIN] = 94;     // 每次尝试读取94字节
    cfsetspeed(&tty, B115200); // 设置波特率115200

    // 禁用输出特殊处理
    tty.c_oflag &= ~OPOST; 
    tty.c_oflag &= ~ONLCR; 

    // 保存终端设置
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
        close(serial_port);
        return 1;
    }

    unsigned char read_buf[94]; 
    int num_bytes;
    while(1){
        num_bytes = read(serial_port, read_buf, sizeof(read_buf)); 
        if (num_bytes < 0) {
            printf("Error reading: %s\n", strerror(errno));
            close(serial_port);
            return 1;
        }

        if (num_bytes < 2) {
            // 字节数太少,无法寻找头部,跳过
            continue;
        }

        // 寻找头部(0x54)和长度(0x2c)的组合
        size_t start_idx = 0;
        while (start_idx < num_bytes - 1) {
            if (read_buf[start_idx] == 0x54 && read_buf[start_idx+1] == 0x2c) {
                break;
            }
            start_idx++;
        }

        // 检查是否找到有效头部,且剩余字节足够组成完整数据包
        if (start_idx > num_bytes - 47) {
            // 剩余字节不足47,无法获取完整包,跳过
            continue;
        }

        // 打印完整的47字节数据包
        for (size_t i = start_idx; i < start_idx + 47; i++) {
            printf("%02x ", read_buf[i]);
        }
        printf("\n");
    }

    close(serial_port);
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 09:39:49