UART采集完整的帧数据
项目背景
当前项目需要Linux系统下接收串口传感器的数据,数据包长度固定。
串口发送端校验
传感器串口与Windows系统下的串口助手已经接通,可以保障每包的数据都完整的发送,且连续发送30分钟也不存在丢包的情况。
这足以说明我们的发送端完成正常,且UART常见的波特率、停止位这些的我们设置没有问题。
Linux系统串口接收问题
但接入Linux系统后,数据的采集变得很奇怪。可以看到数据包的包头和包尾,但中间的间隔却出现问题。既无法得到正确的数据,也没有对数据进行分包。
新知识1:存粹作为数据收发的串口配置
串口在Linux中属于终端设备,Linux下串口的设置会比Windows要丰富不少,我们在Linux中若要把一个串口完全作为数据收发(而不是字符串形式以换行为结尾的“聊天框”)。我们需要配置的部分不仅仅是我们在Windows和stm32F系列单片机下的波特率、停止位、数据位、校验位。

场景建议:降低发送端数据发送频率
这么做的好处是避免包和包的杂糅,这可以避免后续拼包的影响。笔者这里把发送端的数据降低到2s一包。
收发数据的串口配置方法
在Linux下的串口,我们还需要更多的操作。
博客 Linux 串口读取 中给出了相应配置代码,实测有效。博客上也有其他人实测有效的,非常好。该博主还对代码的每条配置都详细进行了注释,非常感谢
以下是我借鉴该博客用豆包写的一份简化版,注释可能也更加容易理解。如果你只是用串口,完全可以复制参考博客中提供的代码;如果你想更深入了解,可以参考博客 Linux串口属性设置。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
// 配置串口
int configure_serial(int fd, int baudrate) {
struct termios options;
// 获取当前串口配置
if (tcgetattr(fd, &options) != 0) {
perror("tcgetattr");
return -1;
}
// 清除波特率相关的位
cfmakeraw(&options);
// 设置波特率
switch (baudrate) {
case 9600:
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
break;
case 19200:
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
break;
case 115200:
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
break;
default:
fprintf(stderr, "Unsupported baudrate
");
return -1;
}
// 设置数据位为8位
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// 设置停止位为1位
options.c_cflag &= ~CSTOPB;
// 设置无校验位
options.c_cflag &= ~PARENB;
// 禁用硬件流控制
options.c_cflag &= ~CRTSCTS;
// 启用接收
options.c_cflag |= CREAD;
// 设置非规范模式(不依赖换行符等特殊字符)
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 清除软件流控制
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// 设置输出模式
options.c_oflag &= ~OPOST;
// 设置读取超时(这里设置为0.1秒)
options.c_cc[VTIME] = 1; // 单位为十分之一秒
options.c_cc[VMIN] = 0;
// 应用配置
if (tcsetattr(fd, TCSANOW, &options) != 0) {
perror("tcsetattr");
return -1;
}
return 0;
}
int main() {
int fd;
char buffer[256];
ssize_t n;
// 打开串口设备
fd = open("/dev/ttyUSB0", O_RDONLY | O_NOCTTY | O_NDELAY);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
// 配置串口
if (configure_serial(fd, 9600) != 0) {
close(fd);
return EXIT_FAILURE;
}
// 读取串口数据
while (1) {
n = read(fd, buffer, sizeof(buffer));
if (n > 0) {
// 处理接收到的数据,这里简单打印
for (int i = 0; i < n; i++) {
// 可以根据雷达数据帧的格式进一步解析
printf("%02X ", buffer[i] & 0xFF);
}
printf("
");
} else if (n == 0) {
// 无数据
} else {
perror("read");
}
}
close(fd);
return EXIT_SUCCESS;
}
于是串口接收数据就从这样子零零散散几个字节,且找不到完整的包数据

变成了这样子,包包完整,非常漂亮。当然这是2秒一包,后续我们将继续改进,加快发送频率

————————————————————2025/04/26 未完待续————————————————————————
新知识2:流控
问题一:拼帧
串口的读取操作是七层网络模型中的物理层操作,在其之上如果要实现对于帧的完整接收就不仅仅是读取串口数值那么容易。因为串口只是接收数据,不会判断一个完整的帧数据是否到来,而是根据数据传输是否终止。如果数据传输中止,则给系统读的信息,调用read()读取数据。但是完整的帧数据是上层需要的,底层不会处理。这样就需要上层来控制完整帧的接收。
参考博客
arm-linux串口接收出错
串口接收一帧数据及解析
解析串口-接收完整数据帧
但是拼帧,需要数据完整















暂无评论内容