【STM32训练—TOF激光测距模块】第一篇、STM32驱动TOF10120测量距离

本文详细介绍了如何使用STM32单片机配合TOF10120测距模块,包括模块工作原理、IIC接线与编程实现。通过代码示例展示了如何读取并处理滤波后的距离数据,并提供了数据参考手册链接。

目录

第一部分、前言

1、TOF10120激光测距模块的基本知识

2、TOF的引脚

3、与单片机的接线

第二部分、工程代码

1、代码功能描述

2、tof.h文件

3、tof.c文件

4、main.c文件

第三部分、总结

1、TOF10120的数据参考手册

2、完整的工程代码

第一部分、前言

1、TOF10120激光测距模块的基本知识

        模块最佳距离测量范围:10cm~180cm。这里的范围是指这个模块在这个距离范围内测到的数据都非常准确,而不是指我只能测量这个范围的距离,其它距离测量不了,超过这个距离范围,误差会大一点而已。

        工作电压:3.3V~5V都可以,不容易烧。

        通讯协议:这个模块支持串口协议IIC协议,串口协议默认波特率9600,但是我这个工程是利用IIC协议调试的,因为单片机的串口毕竟有限。

2、TOF的引脚

        下图是我模块的样子,从左到右的引脚对应如下表格

绿线SCL
蓝线SDA
白线TXD
黄线RXD
红线VCC
黑线GND

3、与单片机的接线

        本次调试利用的是IIC协议,没有用到串口,因此白线黄线就可以不要了,后来我直接把这根线直接给剪掉了😂。

        IIC协议是模拟出来,绿线SCL —— PB12;蓝线SDA —— PB13,注意:单片机用的核心板C8T6。

第二部分、工程代码

1、代码功能描述

        串口一不断将距离数据打印在电脑的串口助手,每隔1s打印一次,LED闪烁。注意:这里得到的距离数据已经是模块内部滤波后的数据,这种得到的数值比较平滑。如果想要得到实时距离,需要更改寄存器的值,这里请自行参考TOF10120的技术手册。

2、tof.h文件

#ifndef __TOF_H
#define __TOF_H
#include "sys.h"

//   绿线SCL —— PB12
//   蓝线SDA —— PB13


//管脚宏定义
#define I2C_SCL_PIN          	GPIO_Pin_12
#define I2C_SCL_PORT    		GPIOB
#define I2C_SCL_CLK       		RCC_APB2Periph_GPIOB

#define I2C_SDA_PIN             GPIO_Pin_13
#define I2C_SDA_PORT          	GPIOB
#define I2C_SDA_CLK           	RCC_APB2Periph_GPIOB


//IO操作函数
#define USERI2C_SCL_1   	GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN)
#define USERI2C_SCL_0   	GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN)
#define USERI2C_SDA_1   	GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN)
#define USERI2C_SDA_0   	GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN)

#define USERI2C_READ_SDA  (GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_PIN))

#define I2C_DEVID    			164//0XA4


//函数声明
void UserI2c_Start(void);

void UserI2c_Init(void);

unsigned char SensorWritenByte(unsigned char Devid, unsigned char *TXBuff, unsigned char SubAdd, unsigned char Size);
unsigned char SensorReadnByte(unsigned char Devid, unsigned char *RXBuff, unsigned char SubAdd, unsigned char Size);


#endif

3、tof.c文件

#include "tof.h"
#include "delay.h"

#include "usart.h"

//变量定义
unsigned char devid;
unsigned int i2cread_interval;
unsigned char dirt_send_flag;
unsigned char dirt_detection_flag;
unsigned short int length_val;
unsigned short int length_aveval;


/*******************************************************************************
* Function Name  : UserI2c_Start
* Description    : sck stable in H,sda falling edge
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void UserI2c_Start(void)
{
    USERI2C_SDA_1;
    delay_us(5);
    USERI2C_SCL_1;
    delay_us(5);
    USERI2C_SDA_0;  //START:when CLK is high,DATA change form high to low
    delay_us(5);
    USERI2C_SCL_0;  //钳住I2C总线,准备发送或接收数据
    delay_us(30);  //加30uS延时
}


/*******************************************************************************
* Function Name  : UserI2c_Stop
* Description    : sck stable in H,sda rising edge
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void UserI2c_Stop(void)
{
    USERI2C_SCL_0;
    USERI2C_SDA_0;//STOP:when CLK is high DATA change form low to high
    delay_us(5);
    USERI2C_SCL_1;
    delay_us(5);
    USERI2C_SDA_1;  //发送I2C总线结束信号
    delay_us(5);

    delay_us(30);   //加30uS延时
}

/*******************************************************************************
* Function Name  : UserI2c_Wait_Ack
* Description    : the 9th clock pulse period wait ack
* Input          : None
* Output         : None
* Return         : =0有ack
*								 : =1无ack
*******************************************************************************/
unsigned char UserI2c_Wait_Ack(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    unsigned short int ucErrTime = 0;
    unsigned char RetValue;

    //SDA设置为输入
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;   //上拉输入
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);

    USERI2C_SCL_0;
    delay_us(5);
    USERI2C_SCL_1;

    ucErrTime = 10000;

    while( ucErrTime-- > 0 )
    {
        if(USERI2C_READ_SDA )
        {
            RetValue = 0;
        }
        else
        {
            RetValue = 1;
            break;
        }
    }

    delay_us(1);
    USERI2C_SCL_0;//时钟输出0

    //SDA线输出
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //配置IO的输出速度为50MHz,高低电平状态最高切换频率
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);

    USERI2C_SDA_0;
    delay_us(30);     //加30uS延时
    return RetValue;
}


/*******************************************************************************
* Function Name  : useri2c_ack
* Description    : the 9th clock pulse period, the receiver pulls sda low to
* 							 : acknowledge the receipt of the eight data bits.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void useri2c_ack(void)
{
    USERI2C_SCL_0;
    USERI2C_SDA_0;
    delay_us(5);
    USERI2C_SCL_1;
    delay_us(5);
    USERI2C_SCL_0;
    delay_us(30);     //加30uS延时
}

/*******************************************************************************
* Function Name  : useri2c_nack
* Description    : no acknowledge the receipt of the eight data bits.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void useri2c_nack(void)
{
    USERI2C_SCL_0;
    USERI2C_SDA_1;
    delay_us(5);
    USERI2C_SCL_1;
    delay_us(5);
    USERI2C_SCL_0;
    delay_us(30);     //加30uS延时
}

/*******************************************************************************
* Function Name  : UserI2c_Send_Byte
* Description    : write one data to i2c bus
* Input          : txd-data
* Output         : None
* Return         : None
*******************************************************************************/
void UserI2c_Send_Byte(unsigned char txd)
{
    unsigned char t;

    USERI2C_SCL_0;//拉低时钟开始数据传输
    delay_us(5);

    for(t = 0; t < 8; t++)
    {
        if((txd & 0x80) >> 0)
            USERI2C_SDA_1;
        else
            USERI2C_SDA_0;

        txd <<= 1;
        delay_us(5);
        USERI2C_SCL_1;
        delay_us(5);
        USERI2C_SCL_0;
    }
}


/*******************************************************************************
* Function Name  : UserI2c_Read_Byte
* Description    : read one data from i2c bus
* Input          : None
* Output         : None
* Return         : receoved data
*******************************************************************************/
unsigned char UserI2c_Read_Byte(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    unsigned char i, receive = 0;

    //SDA设置为输入
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;   //上拉输入
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);

    USERI2C_SDA_1;
    USERI2C_SCL_0;
    delay_us(30);

    for(i = 0; i < 8; i++ )
    {
        receive <<= 1;
        USERI2C_SCL_1;
        delay_us(5);

        if(USERI2C_READ_SDA)
            receive++;

        USERI2C_SCL_0;
        delay_us(5);
    }

    USERI2C_SCL_0;

    //SDA线输出
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);

    return receive;
}
/**************************************************************************************************************************/

/*******************************************************************************
* Function Name  :SensorWritenByte
* Description    :  Sensor read api.
* Input          : devid-设备i2c地址
*                : *TXBuff-the address of the buffer which to write.
*                : SubAdd-the address read to
*                : Size-the buffer size
* Output         : None
* Return         : None
*******************************************************************************/
unsigned char SensorWritenByte(unsigned char Devid, unsigned char *TXBuff, unsigned char SubAdd, unsigned char Size)
{
    unsigned char i = 0;

    UserI2c_Start();

    UserI2c_Send_Byte( Devid | 0x00 );

    if( 0 == UserI2c_Wait_Ack() )
    {
        UserI2c_Stop();
        return 0;
    }

    UserI2c_Send_Byte( SubAdd & 0xff );

    if( 0 == UserI2c_Wait_Ack() )
    {
        UserI2c_Stop();
        return 0;
    }

    for ( i = 0; i < Size; i++)
    {
        UserI2c_Send_Byte( TXBuff[Size - i - 1] );

        if( 0 == UserI2c_Wait_Ack() )
        {
            UserI2c_Stop();
            return 0;
        }
    }

    UserI2c_Stop();

    return 1;
}


/*******************************************************************************
* Function Name  : SensorReadnByte
* Description    :  Sensor read api.
* Input          : devid-设备i2c地址
*                : *RXBuff-the address of the buffer which stores read content.
*                : SubAdd-the address read from
*                : Size-the buffer size
* Output         : None
* Return         : None
*******************************************************************************/
unsigned char SensorReadnByte(unsigned char Devid, unsigned char *RXBuff, unsigned char SubAdd, unsigned char Size)
{
    unsigned char i = 0;

    UserI2c_Start();

//    UserI2c_Send_Byte( Devid | 0x00 ); //实时距离地址
		UserI2c_Send_Byte( Devid | 0x04 );   //滤波距离地址
    if( 0 == UserI2c_Wait_Ack() )
    {
        UserI2c_Stop();
        return 0;
    }

    UserI2c_Send_Byte( SubAdd & 0xff );

    if( 0 == UserI2c_Wait_Ack() )
    {
        UserI2c_Stop();
        return 0;
    }

    UserI2c_Stop();

    UserI2c_Start();
    //UserI2c_Send_Byte( Devid | 0x01 ); //实时距离地址
		UserI2c_Send_Byte( Devid | 0x05 );   //滤波距离地址


    if( 0 == UserI2c_Wait_Ack() )
    {
	//  UserI2c_Stop();
	//    return 0;
    }

    for ( i = 0; i < Size; i++)
    {

        RXBuff[Size - i - 1] = UserI2c_Read_Byte();

        if((i + 1) == Size)
            useri2c_nack();
        else
            useri2c_ack();

    }

    UserI2c_Stop();

    return 1;
}


/*******************************************************************************
* Function Name  : UserI2c_Init
* Description    : config i2c driver gpio
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void UserI2c_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(	I2C_SCL_CLK, ENABLE );
    RCC_APB2PeriphClockCmd(	I2C_SDA_CLK, ENABLE );

    GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(I2C_SCL_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
    GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);


    GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN); 	//SCL 输出高
    GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN); 	//SDA 输出高
/************************/
/**配置TOF内部寄存器*****/
/**代码改于2022/6/1_dpt**/
/************************/
	devid=I2C_DEVID;       //设置I2C从机地址  默认164 0xA4
	
	dirt_send_flag=1;			 //=0串口主动发送 =1串口或者I2C被动读取
	SensorWritenByte(devid,(unsigned char *)&dirt_send_flag, 0x09, 1);      //0x09设置距离发送方式  1 主机去读取  0 模块发送
	delay_ms(100);
	
	dirt_detection_flag=0; //=0滤波值 =1实时值
	SensorWritenByte(devid,(unsigned char *)&dirt_detection_flag, 0x08, 1);  //0x08设置 距离数据模式   1 实时值 0滤波值
	delay_ms(100);		
	
}

/************************END OF FILE*************************/

4、main.c文件

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

//变量定义
extern unsigned char devid;                      //I2C从机地址声明
extern unsigned short int length_aveval;  //用来存储长度数据

int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断控制器分组设置
	delay_init();                                   //初始化很重要//用不了的函数一般都是没有初始化  TOF模拟IIC用到,所以放到前面
/*串口*/	
	Usart1_Init(115200);
/*LED*/
	LED_Init();
/*Tof*/
  UserI2c_Init();	       //TOF模块初始化引脚,配置寄存器

	while(1)
	{
		LED0 = 0;
		delay_ms(500);
		LED0 = 1;
		delay_ms(500);
		
		SensorReadnByte(devid,(unsigned char *)&length_aveval, 0x04,2);//注意:这里我已更改为滤波距离	  
		printf("%d\r\n",length_aveval);
	}
}

第三部分、总结

1、TOF10120的数据参考手册

        这是博主看的数据手册,我这里面的很多东西都是从数据手册搬运过来,手册链接:TOF10120数据参考手册无需积分下载)。

2、完整的工程代码

        这是博主的完整的代码,可以免费下载,但是你是不是要点个赞,然后再下载?🤞STM32驱动TOF10120激光测距模块的完整代码

        有问题的小伙伴现在可以进群询问啦,当然我所有博客中涉及到的参考资料和工程都在群文件里。之前也在博客中放过群号,但是那个时候没有时间,群里面的小伙伴发的消息我都没时间回复,所以就把群号给屏蔽了,现在读研了,有时间了。欢迎大家的加入。群聊目前处于起步阶段,哈哈哈哈哈😂😂😂
 

### STM32 激光测距教程与示例代码 #### 配置环境准备 为了能够顺利运行STM32激光测距程序,首先需要准备好开发工具链以及必要的库文件。通常情况下会使用Keil MDK或者STM32CubeIDE作为主要的编程平台,并安装对应的HAL库支持。 #### GPIO, 定时器和ADC初始化设置 对于基于STM32的激光测距系统而言,GPIO用于控制信号输入输出;定时器负责触发脉冲序列并计算时间间隔;而ADC则用来读取模拟电压值转换成距离数据。这里给出一段简化版的基础配置代码: ```c #include "stm32f1xx_hal.h" // 初始化GPIO引脚 void MX_GPIO_Init(void){ __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; /*Configure GPIO pin : PA8 */ GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA,&GPIO_InitStruct); } // 设置TIM2为PWM模式 void MX_TIM2_Init(void){ __HAL_RCC_TIM2_CLK_ENABLE(); TIM_HandleTypeDef htim2; htim2.Instance=TIM2; htim2.Init.Prescaler=79; htim2.Init.CounterMode=TIM_COUNTERMODE_UP; htim2.Init.Period=999; htim2.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; if(HAL_TIM_PWM_Init(&htim2)!=HAL_OK){ Error_Handler(); } } ``` 上述代码展示了如何通过标准外设库函数完成基本硬件资源分配[^1]。 #### TOF10120模块驱动实例 当涉及到更专业的ToF(飞行时间)传感器如TOF10120时,则可以参考开源项目中的具体实现方法。下面是一段来自实际项目的片段,它实现了对TOF10120设备的操作接口定义: ```c #define CMD_GET_DISTANCE 0x51 uint8_t tof_read_distance(uint16_t *distance_cm){ uint8_t status; uint8_t buffer[2]; // 发送命令获取当前测量距离数值 i2c_write(CMD_GET_DISTANCE); // 接收返回的数据包 status=i2c_read(buffer,sizeof(buffer)); if(status==sizeof(buffer)){ *distance_cm=(buffer[0]<<8)|buffer[1]; return 1; }else{ return 0; } } ``` 这段代码说明了怎样利用IIC协议向TOF10120发送指令请求最新的距离信息,并解析响应报文得到最终的结果[^2]。 #### 多个VL53L0X传感器协同工作案例 如果应用场景中存在多个相同类型的激光测距仪共存的情况,那么还需要考虑它们之间的同步性和抗干扰能力等问题。针对这种情况,在另一个公开资料里给出了详细的解决方案,其中包括但不限于地址修改机制、轮询策略等方面的内容: ```c static void VL53L0X_SetDeviceAddress(VL53L0X_Object_t* pdev,uint8_t new_addr){ uint8_t data=new_addr<<1; I2C_WriteReg(pdev->i2cHandle,I2C_ADDR_DEFAULT,VL53L0X_REG_I2C_SLAVE_DEVICE_ADDRESS,data); pdev->Addr=new_addr; } for(int i=0;i<SENSOR_COUNT;++i){ vl53lox[i].Addr=DEFAULT_ADDRESSES+i; VL53L0X_SetDeviceAddress(&vl53lox[i],vl53lox[i].Addr); } ``` 此部分代码解释了如何更改各个独立工作的VL53L0X器件内部预设好的通信地址以便区分不同个体,进而达到多路并发采集的目的[^3]。
评论 51
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大屁桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值