两路VL53L0X激光测距传感器的使用

本文档记录了在STM32F103RCT6开发板上使用两个VL53L0X激光测距传感器的实践过程,包括初始化错误、测量数据异常和多传感器协同工作的问题。问题1主要是接口配置错误,问题2是测量数据类型不匹配导致的错误,问题3是多个传感器地址冲突。解决方案涉及引脚配置、数据类型匹配及传感器地址重置。代码示例展示了如何解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

两个VL53L0X激光测距传感器的使用

序言
最近在项目中需要用到VL53L0X激光传感器进行测距,于是大概的研究了一下。开始使用的是正点原子的例程,但是项目中要使用到两个,于是乎对其进行了部分更改移植,目前来说在自己画的板子上(STM32F103RCT6)能够正常运行,但是中间也是弄了好几天,目前来看,问题都是出在很多小问题上,因此在这里做个总结。

首先将我使用到的相关参考资料在这里贴出来,有需要的伙伴可以去看一下,这里我就不重复他们资料里面的问题了,只是将我使用过程中所遇到的问题指出来说一下

参考资料
1、一个VL53L0X激光测距:正点原子的Vl53L0X激光测距传感器的例程, 可以自己移步到这里下载:正点原子激光测距传感器
2、四个VL53L0X激光测距:四路Vl53L0X激光测距

问题1:
问题描述
由于我是结合了这两份资料来进行移植修改的,因此,程序代码多少和这两份代码都有一些差异。首先是我只使用一个VL53L0X来进行测距,发现串口总是打印接口错误

问题原因
确实就如返回的错误状态所说,接口错误,也就是说,自己画的板子,接口和程序里面的不匹配,导致初始化总是失败,这里的接口错误可能有好几个地方:一是AT24C02的模拟IIC引脚初始化和位带操作这里没有匹配;二是VL53L0X的模拟IIC和片选引脚XSH没有配置对,这里一定要多仔细一点,只要是报接口错误,多半都是要么没有初始化,要么宏定义(位带操作)没有修改,或者是没有开启相应的时钟等

问题2:
问题描述
在修改了所有的引脚定义以后,初始化成功。但是发现又有一个新的问题,那就是测量数据总是异常,一会儿是7、8,一会儿又跳变到65530等较大的数字;有时候确是120mm以内正常,超过以后就数值异常。

问题原因
这个问题可能有两个原因,第一个就是没有校准,所以导致测量数据异常,重新进行校准即可;这里还有第二个原因,也是困扰我两天的问题,这里贴出来。前面说过,我是结合了两份代码进行修改的,因此很多地方就是两份代码都用到过,例如这里:
原子的测量函数:

//VL53L0X 单次距离测量函数
//dev:设备I2C参数结构体
//pdata:保存测量数据结构体
VL53L0X_Error vl53l0x_start_single_test(VL53L0X_Dev_t *dev,VL53L0X_RangingMeasurementData_t *pdata,char *buf)
{
	VL53L0X_Error status = VL53L0X_ERROR_NONE;
	uint8_t RangeStatus;	
	status = VL53L0X_PerformSingleRangingMeasurement(dev, pdata);//执行单次测距并获取测距测量数据
	if(status !=VL53L0X_ERROR_NONE) return status;
	RangeStatus = pdata->RangeStatus;//获取当前测量状态
    memset(buf,0x00,VL53L0X_MAX_STRING_LENGTH);
	VL53L0X_GetRangeStatusString(RangeStatus,buf);//根据测量状态读取状态字符串
	Distance_data = pdata->RangeMilliMeter;//保存最近一次测距测量数据
    return status;
}

另一份代码的测量函数:

//执行单次测量
uint16_t vl53l0x_start_single_test(VL53L0X_Dev_t *pdev,VL53L0X_RangingMeasurementData_t *pdata)
{
    int i = 0, j = 0, sum = 0;
    VL53L0X_Error status = VL53L0X_ERROR_NONE;
    if(vl53l0x_status != VL53L0X_ERROR_NONE)
        return vl53l0x_status;
    status = VL53L0X_PerformSingleRangingMeasurement(pdev, pdata);   //VL53L0X执行单一测量范围
    if(status != VL53L0X_ERROR_NONE) {
        printf("error:Call of VL53L0X_PerformSingleRangingMeasurement\n");
        return status;
    }
    for(i = 0; i < 5; i++)	//这里的代码估计是想要滤波之类的,但是好像没用,可以直接删除
        sum += pdata->RangeMilliMeter;	
    pdata->RangeMilliMeter = sum / 5;	
    return pdata->RangeMilliMeter;
}

这里暂时不讨论函数的内容,只看返回值,大家可以发现原子的返回值是状态,而该代码的返回值直接就是测得的距离值。而我的问题正好出在这里,我在原子的代码基础上进行更改,但是返回值改成了测量数据,从而导致我的测量距离只有120mm,大于120mm以后,数据就变成了65530等较大的数字。原因就在返回值这里,原子哥的函数返回值类型是u8类型,而实际的测量数据则是u16的类型,从而导致返回的数据在大于某个数值以后,就将低位给省略了,因此导致错误。所以大家一定要注意函数返回值和实际返回值的数据类型是否匹配。

问题3:
问题描述
在将上面两个问题都解决以后,将两个VL53L0X都接入板子,发现总是只有最后一个才能正常工作。

问题原因
这个问题原子哥其实说过。正点原子的VL53L0用户手册上也写明了再次使能时设备地址会恢复为0x52,这是一个坑,一定要注意
但是由于他的历程只有一个传感器,因此就不存在这个问题,所以大家如果使用多个VL53L0X时,在初始化的时候不能按照原子哥的初始化步骤来,一定要先将IIC和所有的VL53L0X片选引脚XSH拉低,然后逐个开启(拉高)片选引脚XSH,在使能了片选引脚以后,就不能再次将该引脚拉低,否则就只有最后一个才能工作(最后一个是正常初始化,相当于只有一个)

这里贴出主要代码,没有贴出来的部分几乎都是原子的例程代码,如校准和模式设定等函数。想要工程文件的朋友可以直接移步到文末下载
代码:
主函数:

int main(void)
{	
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	Uart1_init(115200);	 	 //串口1

 	LED_Init();			     	//LED端口初始化	
	OLED_Init();
	
	VL53L0X_Init();			//vl53l0x初始化	

	OLED_ShowString(0,0,"Dis[0]:",16);
	OLED_ShowString(0,16,"Dis[1]:",16);
	OLED_Refresh_Gram();	
	//死循环
	while(1)
	{			
		VL53L0x_GetDistance();		//获取距离值
		OLED_ShowNum(80,0,Distancebuff[0],5,16);//显示ASCII字符的码值 
		OLED_ShowNum(80,16,Distancebuff[1],5,16);//显示ASCII字符的码值 	
		OLED_Refresh_Gram();		//更新显示到OLED 								
	}	
}

初始化函数:

void VL53L0X_Init(void)
{		
	AT24CXX_Init();				//EEPROM的IIC引脚SCL、SDA初始化	
	while(AT24CXX_Check())
	{
		OLED_ShowString(0,0,"NO 24C02",16); 		
		OLED_Refresh_Gram();
		LED1 =!LED1;
		delay_ms(500);
	}
	VL53L0X_GPIO_init();			//这里一定注意!在引脚初始化时要先将所有的片选失能,然后在初始化时再逐一使能开启!否则其地址就变成了默认值
	vl53l0x_init(&vl53l0x_dev0,0);	//初始化引脚,地址,设备,EEPROM
	vl53l0x_init(&vl53l0x_dev1,1);	//初始化引脚,地址,设备,EEPROM
}
//VL53L0X引脚初始化
void VL53L0X_GPIO_init(void)
{
	GPIO_InitTypeDef 	GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);    //使能AFIO时钟 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);	//使能GPIOC时钟
	 
	//IIC引脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8;  //端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;       //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //50Mhz速度
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_7|GPIO_Pin_8);// 输出高	
	
	//片选
	//XSH1----PB4
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	           //端口配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO口速度为50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure);				   //根据设定参数初始化GPIOB
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);		//禁止JTAG,从而PB4可以当做普通IO口
	//XSH2----PC9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	           //端口配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;      //IO口速度为50MHz
    GPIO_Init(GPIOC, &GPIO_InitStructure);				   	
	//失能片选信号
	VL53L0X_Xshut1=0;	//失能VL53L0X
	VL53L0X_Xshut2=0;
	delay_ms(20);
}
//初始化vl53l0x
//dev:设备I2C参数结构体
VL53L0X_Error vl53l0x_init(VL53L0X_Dev_t *dev,uint8_t VL53L0X_x_id)
{
	VL53L0X_Error 	  Status     = VL53L0X_ERROR_NONE;	//状态定义为0,没有错误
	VL53L0X_Dev_t 	  *pMyDevice = dev;					//这里注意是指针,是地址的传递
	
	//对传感器的寄存器内容进行初始化---------------------------------------------
	pMyDevice->I2cDevAddr		= VL53L0X_Addr;//I2C地址(上电默认0x52)
	pMyDevice->comms_type 		= 1;           //I2C通信模式
	pMyDevice->comms_speed_khz 	= 400;    //I2C通信速率
	
	//根据传入的ID不同,对不同的传感器进行初始化操作
	switch(VL53L0X_x_id)
	{
		case 0:
			VL53L0X_Xshut1=1;	//使能片选
			delay_ms(20);
			vl53l0x_Addr_set(pMyDevice,0x54);	//重新设定地址	
			AT24CXX_Read(50,(u8*)&Vl53l0x_data_1,sizeof(_vl53l0x_adjust));//读取24c02保存的校准数据,若已校准 Vl53l0x_data_1.adjustok==0xAA	
			if(Vl53l0x_data_1.adjustok==0xAA)//已校准
				AjustOK_1=1;	
			else //没校准	
				AjustOK_1=0;
			break;		
		case 1:
			VL53L0X_Xshut2=1;//这里之所以在初始化第二个传感器的时候没有失能第一个,是因为第一个的地址已经变化了,但是第二个的地址仍然是默认地址
			delay_ms(20);
			vl53l0x_Addr_set(pMyDevice,0x56);	
			AT24CXX_Read(100,(u8*)&Vl53l0x_data_2,sizeof(_vl53l0x_adjust));
			if(Vl53l0x_data_2.adjustok==0xAA)//已校准
				AjustOK_2=1;	
			else //没校准	
				AjustOK_2=0;		
			break;
	}
	
	//设置传感器地址、设备初始化、获取设备信息、读取EEPORM数据
	//设备上电初始化
	Status = VL53L0X_DataInit(pMyDevice);		
	if(Status!=VL53L0X_ERROR_NONE) goto error;
	delay_ms(2);
	//获取设备ID信息
	Status = VL53L0X_GetDeviceInfo(pMyDevice,&vl53l0x_dev_info);	
    if(Status!=VL53L0X_ERROR_NONE) goto error;
	//校准--在第一次校准以后手动屏蔽		
//	vl53l0x_adjust(pMyDevice,VL53L0X_x_id);	
	//模式设定
	vl53l0x_set_mode(pMyDevice,0,VL53L0X_x_id);	
	error:
	if(Status!=VL53L0X_ERROR_NONE) 
	{
		print_pal_error(Status);//打印错误信息
		return Status;
	} 	
	return Status;
}

获取距离值:

//VL53L0X获取距离程序
void VL53L0x_GetDistance(void)
{   	
	Distancebuff[0] = vl53l0x_start_single_test(&vl53l0x_dev0,&vl53l0x_data);
	Distancebuff[1] = vl53l0x_start_single_test(&vl53l0x_dev1,&vl53l0x_data);
}
VL53L0X_Error vl53l0x_start_single_test(VL53L0X_Dev_t *dev, VL53L0X_RangingMeasurementData_t *pdata)
{
    VL53L0X_Error status = VL53L0X_ERROR_NONE;
    uint8_t RangeStatus;
    status = VL53L0X_PerformSingleRangingMeasurement(dev, pdata);//执行单次测距并获取测距测量数据
    if(status != VL53L0X_ERROR_NONE) return status;
//    RangeStatus = pdata->RangeStatus;//获取当前测量状态
//    memset(buf, 0x00, VL53L0X_MAX_STRING_LENGTH);
//    VL53L0X_GetRangeStatusString(RangeStatus, buf); //根据测量状态读取状态字符串
//	  printf("State;%i \r\n",RangeStatus);//打印测量状态
	return pdata->RangeMilliMeter;	
}

工程文件
最后将我的工程代码放出来,大家有积分的可以到这里去下载:
VL53L0X激光测距
没有积分的可以留言发邮箱。主要代码都在画圈这几个文件中。另外,我也是新手,水平有限,欢迎各位朋友留言讨论,大佬轻喷。
在这里插入图片描述
注意事项:
1、在实际使用的时候一般不需要原子哥里面的复位函数,可以将其屏蔽;
2、该工程只使用了普通测量模式,需要中断模式的大家自行添加;
3、因为我这里使用的AT24C04,因此在检查有无EEPROM的函数里面大家自己根据自己的板子改一下地址;
4、另外其中一个VL53L0X使用到了PB4引脚,因此这里需要进行引脚重映射;

评论 121
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值