TOF10120 小激光的使用 | 封装为函数直接调用 | 手把手写HAL库 | 附官方标准库例程 | 可直接调用 | 串口
目录放给大家,只是需要源码的文末自取。 但是兄弟们不要忘记点个赞,收个藏呀! 谢谢各位了!
(链接定期更新,记得收藏! )
1. TOF10120传感器介绍
TOF是一款比较常用的近距离测距传感器,精度相对较高,价格也相对亲民。最近需要使用它来实现小车贴墙行走,所以在朋友的推荐下使用了这款传感器。但是在购买时店家给的手册很难读懂,后期调试代码时也出现了很多问题,所以打算写一篇教程来帮助大家更快入手这款传感器,帮助各位少走弯路。
这里贴上官方手册。
TOF10120官方手册 提取码:bai1
2. 如何使用
拿到一款传感器,首先要问自己,究竟它是什么通信方式。
TOF10120用了串口。
这里引用@Z小旋的教程,大家根据它来配置就好
STM32CubeMx串口配置
但是这里用一点需要重视!
在手册中提到TOF10120通信的波特率为9600bit
所以在教程中,要将这里
改成9600
记得开中断!
之后了解该传感器的通信原理,这里必须要从官方给的说明书中获取或者是官方给出的源代码。
说明书前面给到了。
这里给出官方给的stm32的标准库代码,格式很标准但是对于初学者不是很有好,这里我也贴出来。使用标准库的同学可以直接搬来用,但是用HAL库的直接看这篇博客就好。
官方TOF10120代码 提取码:bai1
那么这款传感器究竟是什么读取原理呢?
通过手册我们知道,在串口通信模式下,我们如果要读取距离信息要使用蓝色框住的这条命令
但是在 说明 栏目下提到,必须要在被动读取模式下才有效
所以在写代码之前,必须要在初始化代码中发出 “被动读取命令”
那什么是被动读取命令呢?
往下翻手册,发现
所以现在思路很明确了,先开启被动读取命令吗,再循环读取距离就好了
但是注意,这里的返回值
这个返回值应该是一串字符,而不是现成的数据,这一点是通过阅读源码得知的。想了解的可以自己下载下来看。
所以,我们还有一步是要将字符转换为16进制数,也就是我们后期提到的数据解算。
所以现在思路就很明确了。
设置被动读取模式
循环发送读取命令
循环数据结解算
接下来讲代码思路。
3. 代码思路
作为嵌入式程序员,我们不仅要知道功能怎么实现,更要将代码进行函数封装以达到直接调用的目的。这样不仅会让你移植程序更加简单便捷,而且整体的代码风格会更加清新简约。
这里将封装三个函数
-
初始化部分,也就是设置被动读取模式 -
循环发送读取命令的部分 -
数据解算 = 数据接收 + 数据转换
3.1 .被动读取模式 + 循环读取命令
两者其实是一类型的功能,都是通过串口,向TOF发送一系列字符作为指令,所以这里通过操作寄存器的方式实现此功能。
void Send_Data_To_UART(uint8_t dat){
USART1->DR = dat;
while(!(USART1->SR & (1 << 6))); //循环发送,直到结束
}
有了这个函数做基础,我们来书写设置被动读取模式的函数,也就是初始化函数
void Tof_Init(void){
Send_Data_To_UART('s');
Send_Data_To_UART('5');
Send_Data_To_UART('-');
Send_Data_To_UART('1');
Send_Data_To_UART('#'); //Set to passive reading mode
HAL_Delay(10);
HAL_UART_Receive_DMA(&huart1,tof0_dma_buff,TF_BUFFER_SIZE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
}
可以看到,我们顺便开启了串口 空闲中断
此函数在初始化部分调用
之后再写设置发送读取命令的函数
void Get_Data_From_TOF0(void){
Send_Data_To_UART('r');
Send_Data_To_UART('6');
Send_Data_To_UART('#');
}
此函数再rtos的任务 或者while 中循环调用
两大基本函数定义完成,接下来是数据解算了
3.2 .数据解算
数据解算分为数据接收 + 数据转换
3.2.1数据的接收。
首先是数据的接收
这是放在 中断中的
if(huart->Instance== USART1){ //小激光串口
Get_Data_From_TOF0();//发送读取命令
HAL_UART_Receive_DMA(&huart1,tof0_dma_buff,TF_BUFFER_SIZE); //开启DMA传输
T0f_Conversion_HEX( tof0_dma_buff, &tof0 );//数据转换
}
第三个后面会讲到
3.2.2 数据的转换
接下来要做的是数据转换。
我们知道,TOF接受到的数据格式直接就是字符串,所以为了得到距离信息,我们要做到提取以字符串形式存在的距离数字并储存在变量中。
这就引出了我们在 数据转换中要做的 第一步:判断有效数字的位置
3. 2.1 判断有效数字的位置
上文已经提到,TOF这个传感器直接给出 ‘L’ ‘’=’’ ‘X’ ‘X’ ‘X’ ‘m’ ‘m’ 或 ‘L’ ‘’=’’ ‘X’ ‘X’ ‘m’ ‘m’ 的字符串,这里的X就是我们要的有效数字,既然要提取这些数字,第一想法自然是区分两种情况了。
数组 | 字符 | 字符 |
---|
0 | L | L | 1 | = | = | 2 | X | X | 3 | X | X | 4 | X | m | 5 | m | m | 6 | m | |
在这张表格中就可以很清晰的看出,区分两个数组的方法就是看【4】位置上的元素,如果是m 那么有两个 X,也就是说距离是两位数,如果不是m,那么它就是个三位数。 (由于TOF是可以测距到2000mm的,那么如果是四位数的量级你也要自己会写,这里限于篇幅原因只写到三位数,当做作业自己去完善试试)
于是给出以下代码进行判断
void T0f_Conversion_HEX( uint8_t tof0_dma_buff[], int *ptof0 ){
int i = 0;
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 6 && tof0_dma_buff[i] == 'm' ){
break;
}
}
printf("%d\n",i);
}
对上述代码进行拆分讲解,看懂的可以略过。
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 6 && tof0_dma_buff[i] == 'm' ){
break;
}
}
定义i变量后对数组进行遍历,发现字符 ‘m’ 后跳出循环,第一步, 选择有效数字 完成。
数据解算的第二步:解算为有效数字
3.2.2 解算为有效数组
由于不想增加思考难度,所以我这里打算对每种情况进行分类讨论,这样做虽然代码量较大,但是易于理解,这也是我写这篇教程的初心。如果大家有改善化的写法,欢迎放在评论区进行交流
我们直接讨论 i = 5的情况,也就是
此时大家发现,[2] 位乘100,[3] 位乘10,[4] 位乘1相加就是最后的准确数字 所以,我们只需要的到键入以下代码即可。
void T0f_Conversion_HEX( uint8_t tof0_dma_buff[], int *ptof0 ){
int i = 0;
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
*ptof0 = tof0_dma_buff[i - 3] *100 +
tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}
}
printf("%d\n",i);
}
但是一个关键的问题是,我们每一位的数字实际上是ASCII码而不是16进制数,所以这样的数据执行现在的操作是不可行的,所以必须在注释位置进行进制转换。我们直接引用这位博主ASCII码转16进制的源码。
ASCII码转十六进制
这里贴出源码
unsigned char HexToChar(unsigned char bChar){
if((bChar>=0x30)&&(bChar<=0x39)){
bChar -= 0x30;
}
else if((bChar>=0x41)&&(bChar<=0x46)) {
bChar -= 0x37;
}
else if((bChar>=0x61)&&(bChar<=0x66)) {
bChar -= 0x57;
}else {
bChar = 0xff;
}
return bChar;
}
我们在解算函数中调用
void T0f_Conversion_HEX( uint8_t tof0_dma_buff[], int *ptof0 ){
int i = 0;
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 3] = HexToChar(tof0_dma_buff[i - 3]);
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 3] *100 +
tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}else if( i == 6 && tof0_dma_buff[i] == 'm' ){
break;
}
}
printf("%d\n",i);
}
至此,三位数的解算完成,我们补全所有代码。
void T0f_Conversion_HEX( uint8_t tof0_dma_buff[], int *ptof0 ){
int i = 0;
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 3] = HexToChar(tof0_dma_buff[i - 3]);
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 3] *100 +
tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}else if( i == 6 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 4] = HexToChar(tof0_dma_buff[i - 4]);
tof0_dma_buff[i - 3] = HexToChar(tof0_dma_buff[i - 3]);
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 4] * 1000 +
tof0_dma_buff[i - 3] * 100 +
tof0_dma_buff[i - 2] * 10 +
tof0_dma_buff[i - 1] * 1;
break;
}
}
}
给你们把四位数的也贴出来了,不得点赞收藏转发支持一下???
???
4.核心代码
初始化(被动读取):
void Send_Data_To_UART(uint8_t dat){
USART1->DR = dat;
while(!(USART1->SR & (1 << 6)));
}
void Tof_Init(void){
Send_Data_To_UART('s');
Send_Data_To_UART('5');
Send_Data_To_UART('-');
Send_Data_To_UART('1');
Send_Data_To_UART('#');
HAL_Delay(10);
HAL_UART_Receive_DMA(&huart1,tof0_dma_buff,TF_BUFFER_SIZE);
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
}
发送读取命令:
void Get_Data_From_TOF0(void){
Send_Data_To_UART('r');
Send_Data_To_UART('6');
Send_Data_To_UART('#');
}
数据解算:
if(huart->Instance== USART1){
Get_Data_From_TOF0();
HAL_UART_Receive_DMA(&huart1,tof0_dma_buff,TF_BUFFER_SIZE);
T0f_Conversion_HEX( tof0_dma_buff, &tof0 );
}
void T0f_Conversion_HEX( uint8_t tof0_dma_buff[], int *ptof0 ){
int i = 0;
for( ; i < 10; i++ ){
if( i == 4 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}else if( i == 5 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 3] = HexToChar(tof0_dma_buff[i - 3]);
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 3] *100 +
tof0_dma_buff[i - 2] *10 +
tof0_dma_buff[i - 1] *1;
break;
}else if( i == 6 && tof0_dma_buff[i] == 'm' ){
tof0_dma_buff[i - 4] = HexToChar(tof0_dma_buff[i - 4]);
tof0_dma_buff[i - 3] = HexToChar(tof0_dma_buff[i - 3]);
tof0_dma_buff[i - 2] = HexToChar(tof0_dma_buff[i - 2]);
tof0_dma_buff[i - 1] = HexToChar(tof0_dma_buff[i - 1]);
*ptof0 = tof0_dma_buff[i - 4] * 1000 +
tof0_dma_buff[i - 3] * 100 +
tof0_dma_buff[i - 2] * 10 +
tof0_dma_buff[i - 1] * 1;
break;
}
}
}
!后记
这篇文章太小众了,完整代码就不发了,如果需要可以在评论区留下你的邮箱,我会在当日发送。
如果喜欢这篇文章,可以在右下角点个 收藏
如果你也对嵌入式开发和算法感兴趣,可以点点关注,我的下一篇文章为你而来哦!
拜拜!
|