LDS-02激光雷达Unix驱动开发:数据包打印不全问题求助
LDS-02激光雷达驱动读取数据不完整问题排查与修复
问题描述
为Unix系统编写LDS-02激光雷达驱动,每个完整数据包为47字节(包含头部0x54和长度字段0x2c)。当前代码通过termios每次读取94字节(2个数据包长度),定位头部+长度组合后尝试打印后续47字节,但每次输出的字节数不足且数量不固定。
问题原因
- 打印循环终止条件错误:定位到头部后,使用
i<47作为循环终止条件完全错误——这里的i是缓冲区中的索引,不是从0开始的偏移量。例如头部在索引20的位置时,该条件只会打印到索引46,仅27个字节,远不足47个完整数据包。 - 缓冲区越界风险:寻找头部时未限制循环边界,当
i达到缓冲区最后一个字节时,read_buf[i+1]会访问超出缓冲区的内存,导致未定义行为。 - 冗余代码无意义:
if(true/*char_are_signed*/)中的循环没有任何实际作用,属于无效代码。 - 未处理部分读取情况:虽然设置了
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
相关产品推荐
相关产品推荐

