stm32 uses TOFSense module ranging tutorial

stm32 uses TOFSense module ranging tutorial

What is TOFSense

Simply put, TOFSense is a laser ranging module, and the small black frame in front of it is where it measures the distance, that is, where the laser is emitted. It has two communication modes: serial port and can bus, and two working modes: active output and query output. This article will introduce its serial active output mode based on stm32.

preparation for programming

The official website has two documents about it, one is a data manual, which mainly introduces the characteristics and data of the product itself, such as typical specifications, mechanical dimensions, configuration and functions, typical data performance and communication protocols, etc.; one is a user manual, mainly used In order to guide users to get started, it includes example tutorials of configuring modules through NAssistant, protocol analysis examples, etc. If you just want to get started quickly and use it to measure distances, just look at the part of the user manual. Now that we know that it is serial communication, we only need to know the format of its data frame, and then write the corresponding data parsing program.

Read the manual and know that the format of its data frame is as follows:

As can be seen from the above figure, when TOFSense uses serial communication, a data frame sent is 16 bytes. The first byte is the frame header, which is fixed at 0x57, and the last byte is the frame end, which is also the checksum of the entire data, and its value is the sum of all the previous bytes. The effective distance data is 8, 9, and 10 bytes of data, and other data are not deliberately ignored. What we get here is only the protocol representation of the distance value, that is, the hexadecimal data. How to calculate the actual distance value? There is also a paragraph in the manual:

Knowing these, we can write the serial port receiving program.

Program source code

Here I use the stm32f1 core board of Punctual Atom, use serial port 2 to receive the data from TOFSense, and use serial port 1 to send it to the computer for display on the computer's serial port assistant. If you use other f1 boards, the program basically does not need to be modified; if you use other serial ports, change the initialization program of the serial port to the corresponding port, that is, the pin. I have basically posted all the program codes here, and you can use them directly.

Writing of usart.h:

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 

#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
#define EN_USART2_RX      1   //使能(1)/禁止(0)串口2接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //串口1接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//串口1接收状态标记	

extern u8 USART_RX_BUF[USART_REC_LEN]; //串口2接收缓冲,最大USART_REC_LEN个字节
extern u16 USART2_RX_STA;     //串口2接收状态标记

extern u32 Active_Distance;  

//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
void uart2_init(u32 bound);
#endif

Writing of usart.c:

#include "sys.h"
#include "usart.h"	  
 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{
    
     
	int handle; 

}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{
    
     
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{
    
          
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 

 
#if EN_USART1_RX   //如果使能了串口1接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //串口1接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //串口1接收状态标记	  
  
void uart_init(u32 bound)
{
    
    
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
    
    
	u8 Res;
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
    
    
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
    
    
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
    
    
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{
    
    	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
    
    
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 	
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();  											 
#endif
} 
#endif

#if EN_USART2_RX //如果使能了串口2接收

u8 USART2_RX_BUF[USART_REC_LEN];     //串口2接收缓冲,最大USART_REC_LEN个字节.
u16 USART2_RX_STA=0;       //串口2接收状态标记
u32 Active_Distance;  //定义主动输出距离,单位mm

void uart2_init(u32 bound)
{
    
    
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART2,GPIOA时钟

	//USART2_TX   GPIOA.2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.2

	//USART2_RX	  GPIOA.3初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3  

	//Usart2 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式

  USART_Init(USART2, &USART_InitStructure); //初始化串口2
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART2, ENABLE);                    //使能串口2

}

void USART2_IRQHandler(void)
{
    
    
	static u8 count = 0;
	u16 check_sum = 0; //定义校验和
	u8 i;
	static u8 flag = 0; //定义数据帧开始标志
	if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET) //串口2接收中断
	{
    
    
		if(USART_ReceiveData(USART2) == 0x57) flag = 1;
		if(flag)
		{
    
    
			USART2_RX_BUF[count++] = USART_ReceiveData(USART2);
			
			if(count==16) //接收到一帧数据即16个字节
			{
    
    
				if(USART2_RX_BUF[0]==0x57)
				{
    
    
					for(i=0;i<15;i++)
					{
    
    
						check_sum = check_sum + USART2_RX_BUF[i];  //
					}
				}
				if((check_sum&0x00ff)==USART2_RX_BUF[15])  //判断校验和是否满足即数据的有效性
				{
    
    
					//协议帧中的数据是小端模式存储的,必须先恢复成16进制数据再换算成10进制
					Active_Distance = (USART2_RX_BUF[10] <<16| USART2_RX_BUF[9]<<8|USART2_RX_BUF[8]);  //除以1000即是m
				}
				count = 0;
				flag = 0;
			}
			
		}
	}
}

#endif	

Writing of main.c:

#include "sys.h"
#include "delay.h"
#include "usart.h"
 

int main(void)
{
    
    		
	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	delay_init();	    	 //延时函数初始化	  
	uart_init(115200);	 //串口初始化为115200
	uart2_init(921600);  //串口2初始化为921600
	printf("TOFSense开始测距,模式为主动输出模式\r\n");
  while(1)
	{
    
    
		//每200ms显示一次距离
		printf("测得的距离为:%d",Active_Distance);
		printf("mm\r\n");
		delay_ms(200);
	}	 
} 

running result

It should be noted that the official documentation says it very clearly:

When over-range in short-range mode, the distance output is a fixed value of -0.01, hexadecimal 0xFFFFF6.
When the range is overrange in medium distance mode, the distance output 1~2m jumps randomly. At this time, you can refer to the signal strength and distance status for judgment.
When the range is exceeded in long distance mode, the data output 1~2m randomly jumps. At this time, you can refer to the signal strength and distance status for judgment.

So don't measure too far to avoid exceeding the range. In addition, I found that the distance measured when the obstacle is pasted is not 0, which is consistent with most of the laser ranging modules on the market, that is, its measurement has an upper and lower limit range, pay attention to within the range use.

Note

The default serial port baud rate of TOFSense is 961200. If you need to modify it, you need to go to the official website to download the matching host computer, and use the USB-TTL module to connect TOFSense to the computer and open the configuration interface of the host computer to modify it. This interface can also be very intuitive. See data and status information.

Official website address: https://www.nooploop.com

Guess you like

Origin blog.csdn.net/weixin_51426754/article/details/119282934