stm32利用TOFSense模块测距教程

TOFSense是什么

简单来讲,TOFSense就是一个激光测距模块,它前面那个小小的黑框框就是它测距的地方即发射激光的地方。它有两种通讯方式:串口和can总线,有两种工作模式:主动输出和查询输出。本文将基于stm32介绍它的串口主动输出模式。

编写程序的准备

官网有两份关于它的资料,一份是数据手册,主要介绍产品本身特性及数据,如典型规格、机械尺寸、配置与功能、典型数据表现及通信协议等;一份是用户手册,主要用于指导用户上手操作,含括了通过NAssistant配置模块的示例教程、协议解析示例等方面。如果只是想快速上手并利用它测距的话,直接看用户手册的部分即可。既然知道了它是串口通信,那么我们只需要知道它的数据帧的格式,然后编写相应的数据解析程序即可。

翻阅手册得知其数据帧的格式如下:

由上图可见,TOFSense在使用串口通信时,发的一个数据帧是16个字节。其中第一个字节是帧头,固定为0x57,而最后一个字节是帧尾,也是整个数据的校验和,其值为前面所有字节相加。而其中有效的距离数据为8、9、10三个字节的数据,其他的数据如果不用刻意忽略。这里我们得到的只是距离值的协议表示即16进制的数据,该怎么计算实际的距离值呢?手册里面还有一段话:

知道了这些,我们便可以编写串口的接收程序了。

程序源码

这里我使用的是正点原子的stm32f1核心板,用串口2接收TOFSense传来的数据,并利用串口1发给电脑在电脑的串口助手显示。如果使用其他f1的板子,程序基本是不用修改的;如果使用其他的串口,改串口的初始化程序为对应的端口即引脚即可。我这里基本把所有的程序代码都贴出来了,直接拿来用即可。

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

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.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_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;//PA10GPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;       //子优先级3NVIC_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); //初始化串口1USART_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();
#endifif(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;  //定义主动输出距离,单位mmvoid 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.2GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2GPIO_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;//PA3GPIO_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 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;       //子优先级3NVIC_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); //初始化串口2USART_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 

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);     //串口初始化为115200uart2_init(921600);  //串口2初始化为921600printf("TOFSense开始测距,模式为主动输出模式\r\n");while(1){//每200ms显示一次距离printf("测得的距离为:%d",Active_Distance);printf("mm\r\n");delay_ms(200);}
} 

运行效果

需要注意一下,官方文档里面说的很清楚:

在短距模式下超量程时, 距离输出固定值-0.01, 十六进制 0xFFFFF6。
在中距模式下超量程时, 距离输出 1~2m 随机跳变。 此时可参考信号强度与距离状态进行判断。
在长距模式下超量程时, 数据输出 1~2m 随机跳变。 此时可参考信号强度与距离状态进行判断。

因此不要测太远的距离以免超出量程。此外我发现当把障碍物贴上去时测得的距离并不是0,这与市面上绝大多数的激光测距模块也是一致的,即它的测量是有一个上下限范围的,注意在范围内使用。

TOFSense默认的串口波特率是961200,如果需要修改,需要去官网下载配套的上位机,并利用USB-TTL模块连接TOFSense到电脑后打开上位机的配置界面修改,在此界面也可以很直观地看到数据及状态的信息。

官网地址:https://www.nooploop.com

stm32利用TOFSense模块测距教程相关推荐

  1. STM32—驱动HC-SR04超声波测距模块

    文章目录 超声波测距原理 HC-SR04工作原理 STM32实现驱动 1.引脚的配置 2.时序控制 3.时间差测量 4.如何将距离测出来 超声波测距原理 利用HC-SR04超声波测距模块可以实现比较精 ...

  2. 【STM32】HAL库 STM32CubeMX教程十五---FMC-SDRAM(一)

    前言: 本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 本文 1首先讲解什么是FMC及SDRAM,W9825G6KH芯片原理,2基于CubeMx创建工程 ...

  3. 【STM32】HAL库 STM32CubeMX教程十四---SPI

    前言: 本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 在我们的HAL库中,对硬件SPI函数做了很好的集成,使得之前SPI几百行代码,在HAL库中,只需 ...

  4. 【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)

    前言: 本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1.芯片: STM32F407ZET6/ STM32F103ZET6 ...

  5. stm32之蓝牙无线超声波测距

    将以前做过的蓝牙和超声波结合就得到蓝牙无线超声波测距 通过hc05蓝牙模块使手机和stm32开发板能互相发送字符串--DMA&空闲中断 stm32f103之HC_SR04超声波测距 在正点原子 ...

  6. stm32利用mqtt与小程序通信

    stm32利用mqtt与小程序通信 stm32连接服务器端 小程序连接服务器 本项目实现的功能如下:通过stm32连接esp8266,然后利用mqtt协议连接到服务器,同时小程序端也连接到服务器,通过 ...

  7. matlab 方波_MATLAB之Simulink(二)利用switch模块将正弦信号变为方波信号

    ##MATLAB之Simulink(二) ##利用switch模块将正弦信号变为方波信号 今天给大家展示一个simulink电路仿真,将正弦信号转化为方波信号. 下面开始教程: 1.首先直接在MATL ...

  8. 浅谈STM32的DMA模块的使用

    浅谈STM32的DMA模块的使用 转自:http://blog.ednchina.com/jack_chang/123085/message.aspx http://article.ednchina. ...

  9. python模块使用_PYthon模块使用教程(最新).doc

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp计算机&nbsp>&nbspPython PYthon模块使用教程(最新).doc63页 本 ...

最新文章

  1. Redis集群官方推荐方案 Redis-Cluster
  2. MySQL如何查看连接数和状态
  3. C++设计模式——适配器模式(对象适配器、类适配器)
  4. .NET Core 出得云端入得本地,微软让跨平台应用勇敢表达
  5. Fabric chaincode开发调试
  6. Hybrid App工作笔记0001---混合App开发过程中_Web是如何调用原生功能的_调用原理
  7. jQuery入门笔记
  8. maven内部运行原理解析
  9. 论文阅读笔记——拥塞控制算法PCC
  10. 高通联机修改IMEI等参数的相关解析
  11. win10电脑开启卓越性能
  12. 微信公众号自动回复如何添加超链接
  13. PIXI.JS一镜到底动画
  14. 尤雨溪:Vue3 将在2022年2月7日成为新的默认版本(你准备好了吗?)
  15. 华为云计算之双活容灾
  16. Centos7 上安装 FastDFS
  17. android 点击返回键home键,appium怎么按下系统按键?如按下返回键、home键等等
  18. 八种基本数据类型(一)
  19. 决策理论与方法——决策概念与分类
  20. 计算机在石油工程中应用文献综述,石油与天然气工程领域工程硕士专业学位基本要求第一部分概况.PDF...

热门文章

  1. IIS发布网站进行访问时提示权限不足的简单解决方法
  2. Cadence Allegro如何修改原点位置
  3. 12306订票流程解析
  4. 优秀的内存规划方法——环形缓冲区(ring buffer)
  5. 【关于如何输出字符串指针指向字符串地址】
  6. 联表查询和嵌套查询—读懂数据库仓储
  7. 程序员必备网站和工具
  8. JasperReport导出excel包含公式
  9. 深度学习框架DeepLearning4J(DL4J)的安装及配置
  10. XP系统下IIS常见的几个问题