最近写了一个卡尔曼滤波的算法,运行一次加发送数据竟然要五六个毫秒于是将目光转向DMA发送,一下子单次运行时间减小了两个毫秒,但是在运行过程中发现时而有数据不换行,导致我在MATLAB上读取采集的数据时报错列数不对,所以个人对这个DMA发送做了一点改进,那我就先上代码再做讲解。
#include <stdio.h>
#include <stdarg.h>
#include "stm32f4xx_hal.h"
// 定义全局变量 u_buf 用于存储发送数据
static char u_buf[255]; // 要使缓冲区大小足够大
// 新定义的函数
void UART1_sprintf_DMA_send(const char *format, ...) {
va_list args;
int len;
// 确保上一次 DMA 传输已经完成
if (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY) {
// 如果 UART 不处于就绪状态,则可以在这里添加错误处理逻辑
return;
}
// 使用 vsnprintf 填充缓冲区
va_start(args, format);
len = vsnprintf((uint8_t*)u_buf, sizeof(u_buf), format, args);
va_end(args);
// 检查是否成功填充了缓冲区
if (len > 0 && len < sizeof(u_buf)) {
// 调用 HAL 库函数进行 DMA 发送
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)u_buf, len);
}
}
首先要用到c语言的<stdarg.h>库,它是 C 标准库中的一个头文件,提供了一组宏,用于访问可变数量的参数。<stdarg.h> 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。可变参数的函数通在参数列表的末尾是使用省略号 ... 定义的。
先在c文件中定义一个静态的缓冲区给串口,在函数传参用到一个const指针这个明白吧,就是为了在访问字符数据时不被其它因素修改,保证数据安全性,省略号就是我为了让它具有printf的功能,可传多个参数。
函数内部,定义一个va_list的类型,然后判断上一次缓冲区内的数据是否被DMA传完,下一步来到”va_start(args, format);“,它内部有一个void的指针让args可以指向format之后的参数列表中任何类型的数据,然后利用vsnprintf将数据写入缓冲区并返回数据长度,用va_end结束args的遍历。
最后用if判断发送的数据是否溢出缓冲区,溢出的话那发送了也是残缺的数据,就直接结束了,如果没溢出,就用Hal库的DMA发送函数把数据发送出去。
下面附上串口读取截图:
用串口助手读取了将近两万条数据吧,首先是包含发送过程的程序运行时间降了接近三个毫秒,然后也没有让matlab的读取过程报错列数不对了。
特别是有些哥哥姐姐们做积分运算的时候,对这个微观数据的切片可以忽视常规的串口发送影响将dt划分的更小了。
还可以把这个函数定义成宏:
#define UART1_sprintf_DMA(...) UART1_sprintf_DMA_send(__VA_ARGS__)