随笔记-获取TOFSense的数据
TOFSense
TOFSense是Nooploop深圳空循环的一款激光测距传感器,前面的黑框就是激光发射与接收的地方。模块激光发射视场角有27°,也就是说实际上模块打出去的激光覆盖范围是一个顶点在黑框上,类似圆锥的形状参考下图,打出的是范围激光但只输出一个值,看模块手册说内部通过多次测量根据数据大小占比来输出占比高的数据,基于这种机制作为避障用就很优秀1~3个模块就可以覆盖小车前方,本文将基于stm32f1介绍它的数据接收处理问题。
UART模式
TOF这个模块可以通过UART与CAN进行通信,这里先介绍UART模式,想要获取数据需要先看一下模块发送数据包的组成以及波特率等问题,通过产品手册可以了解到TOF出厂波特率是921600,这个注意一下单片机波特率要一致,或者可以通过他们的上位机NAssistant使用usb转ttl更改TOF模块的波特率,数据协议如下,还是很好理解的,前两位固定为0x57 0x00然后id、测距距离(dis)、距离状态指示(dis_status)、信号强度(signal_strength)校验和,知道协议具体组成以及对应位后就可以进行编程了。
官方有提供协议解析包,将下面红框中的文件添加到自己的工程中,不会就百度如何添加.c .h文件以及文件路径到工程中
然后看一下解析包做了什么,主要看nlink_tofsense_frame0.c/.h文件就可以,其他几个文件就是一些通用数据处理转换以及头文件,nlink_tofsense_frame0.h里面声明了一个结构体g_nts_frame0,结构体内有一个函数指针,结构体中的result就是解析后的保存数据用的结构体,进入nlink_tofsense_frame0.c中,可以看到函数指针指向uint8_t UnpackData(const uint8_t *data, size_t data_length),那么它就是解析函数了,从参数看我们需要给它传数据以及数据大小。OK 至此我们已经知道了解析包中干了什么,既然知道了需要传入数据那么我们只需要获取到数据然后传入即可。获取数据的步骤就是先用串口接数据,然后调用解析包中的解析函数将数据传入即可正常获取解析数据。
至于接收数据则有很多种方法了,根据之前了解到的固定帧头0x57来判断接收或者使用官方例程中通过UART的空闲中断以及DMA接数据的方法都是可以的
1、使用帧头判断接收,在回调函数中进行处理
main.c
/* USER CODE BEGIN 0 */
uint8_t u_rx_buf[16];
//重定向print
#include "stdio.h"
int fputc(int ch, FILE *f) //重定向printf函数
{
HAL_UART_Transmit(&huart2, (void*)&ch, 1, 1000);
return ch;
}
#include "nlink_tofsense_frame0.h"
//重写回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
static uint8_t c = 0;
if (c == 0)
{
if (u_rx_buf[0] == 0x57)
{
c++;
HAL_UART_Receive_IT(&huart1, &u_rx_buf[1], 16 - 1);
}
else
{
HAL_UART_Receive_IT(&huart1, &u_rx_buf[0], 1);
}
}
else
{
if (g_nts_frame0.UnpackData(u_rx_buf, sizeof(u_rx_buf)/sizeof(u_rx_buf[0])))
{
printf("接收的数据为:%f\r\n", g_nts_frame0.result.dis); //解析成功
}
HAL_UART_Receive_IT(&huart1, &u_rx_buf[0], 1);
c = 0;
}
}
/* USER CODE END 0 */
int main(void)
{
...
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,&u_rx_buf[0],1);
/* USER CODE END 2 */
...
while (1)
{
}
}
第二种方法是TOF官网提供例程的接收方式,通过空闲中断以及DMA进行接收,在中断服务函数中处理
main.c
/* USER CODE BEGIN 0 */
uint8_t u_rx_buf[16];
//重定向print
#include "stdio.h"
int fputc(int ch, FILE *f) //重定向printf函数
{
HAL_UART_Transmit(&huart2, (void*)&ch, 1, 1000);
return ch;
}
/* USER CODE END 0 */
int main(void)
{
...
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 启用串口空闲中断
HAL_UART_Receive_DMA(&huart1, u_rx_buf, sizeof(u_rx_buf)); // 开启DMA接收
/* USER CODE END 2 */
...
while (1)
{
}
}
中断处理文件stm32f1xx_it.c中
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
#include "nlink_tofsense_frame0.h"
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) // 确认是否是空闲中断
{
uint32_t TOF_datalen;
__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清除串口空闲中断标志位
HAL_UART_AbortReceive(&huart1); // 关闭DMA传输
TOF_datalen = sizeof(u_rx_buf) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); // 计算接收数据长度
if (g_nts_frame0.UnpackData(u_rx_buf, TOF_datalen)) // 解析协议数据
{
printf("接收的数据为:%f\r\n",g_nts_frame0.result.dis);
}
memset(&u_rx_buf, 0, sizeof(u_rx_buf));
HAL_UART_Receive_DMA(&huart1, u_rx_buf, sizeof(u_rx_buf)); // 开启DMA接收
}
/* USER CODE END USART1_IRQn 1 */
}
运行结果