最近学习stm32单片机,用VL53L0X开发这个传感器花费了大量的时间和精力。写这个博客是为了记录你的学习过程,另一个是为了感谢网民的帮助。我一直坚持分享的精神,同时把公众送给公众。用这个博客来感谢你的帮助。
以前没学过stm第一次开始做32stm32.我用了很多关于里面的东西。Linux要理解的思想,可能会有很多不正确的解释。希望大家指出共同进步
VL53L0X最远距离为2m,精度可以是2mm。原理非常简单,将激光发射到反射器将激光折射回接收元件,然后VL53L0X内部计算出时间和距离最后通过stm32单片机打印出来。VL53L0X基于通信I2C的,关于I2C你可以搜索其他协议。如果你将来有时间,我会写一篇文章,这里就不赘述了。
首先,我们知道VL53L0X设备地址为0x52(可以修改初始状态的地址)。然后我们来看看。VL53L0X需要接到stm32引脚。分别是GND,VDD,SDA,SCL,XSHUT(这里不用GPIO1)。下面是VL53L0X与stm32引脚对应关系:
SDA-->PA2,SCL-->PA3,XSHUT-->PA5(如有必要,可同时修改vl53l0x_i2c.c,vl53l0x_i2c.h和vl53l0x.c内部对应的引脚)
原理性的东西我暂时不说太多以后有时间会补充,这里注意一下,0x52写命令,0x53是阅读命令,跟随I2C读写命令有关。I2C最后一个在传输中bit是0代表四write,最后一个bit是1代表是read。这里再说一句,有网友的设备地址可能是0x为什么29?假如仔细看他们的I2C读写操作函数会发现它们I2C读写操作是左移一位地址的操作,0x左移一位是0x大家可以算一下。那么读操作呢?左移一位或上1后,它将成为0x53,读地址。我用的是0x52,因为I2C操作函数没有左移,但读取地址或最后一个。请注意此代码。下面的代码分析将讨论。然后直接看代码。
if(vl53l0x_init(&vl53l0x_dev)) //vl53l0x初始化 { printf("VL53L0X_Init Error!!!\r\n"); delay_ms(200); } else { printf("VL53L0X_Init OK\r\n"); VL53L0X_RdByte(&vl53l0x_dev,0xc0,&data); printf("register1:0x%x\r\n",data); printf("vl53l0x_dev.I2cDevAddr = 0x%x\r\n",vl53l0x_dev.I2cDevAddr); } if(vl53l0x_set_mode(&vl53l0x_dev,mode)) //配置测量模式 { printf("Mode Set Error!!!!\r\n"); } else printf("Mode Set OK!!!\r\n"); while(1) { //执行一次测量 Status = vl53l0x_start_single_test(&vl53l0x_dev,&vl53l0x_data,buf); if(Status==VL53L0X_ERROR_NONE) printf("d: %4imm\r\n",Distance_data);//打印测量距离 else printf("Measurement is Error!!!!\r\n"); }
上面是main部分函数代码。vl53l0x_init(&vl53l0x_dev);
VL53L0X_Error vl53l0x_init(VL53L0X_Dev_t *dev) { GPIO_InitTypeDef GPIO_InitStructure; VL53L0X_Error Status = VL53L0X_ERROR_NONE; VL53L0X_Dev_t *pMyDevice = dev; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 配置XSHUT引脚 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); pMyDevice->I2cDevAddr = VL53L0X_Addr;//I2C地址,默认0x定义为宏 pMyDevice->comms_type = 1; //I2C通信,0代表spi pMyDevice->comms_speed_khz = 400; //I2C本手册上有速率 VL53L0X_i2c_init();//这是I2C配置的初始化SDA和SCL引脚的,自己去看吧 //使能XSHUT引脚 VL53L0X_Xshut=0; delay_ms(30); VL53L0X_Xshut=1; delay_ms(30); vl53l0x_Addr_set(pMyDevice,0x52);//这里设置VL53L0X的地址的,但这里似乎没有成功。可自行研究 if(Status!可以自行研究 if(Status!=VL53L0X_ERROR_NONE) goto error; Status = VL53L0X_DataInit(pMyDevice);//这个是VL53L0X自己的函数,设备的初始化 if(Status!=VL53L0X_ERROR_NONE) goto error; delay_ms(2); Status = VL53L0X_GetDeviceInfo(pMyDevice,&vl53l0x_dev_info);///这是他自己获取设备信息的函数 if(Status!=VL53L0X_ERROR_NONE) goto error; error: if(Status!=VL53L0X_ERROR_NONE) { print_pal_error(Status);//打印错误信息 return Status; } return Status; }
这个函数是初始化的VL53L0X传感器。首先是引脚,然后设置通信模式和速度,初始化设备,获取设备信息。其实里面修改的设备地址并不成功,可以自己修改debug进去看看,至于为什么不成功是他的判断条件有问题,有兴趣可以看看。然后我们来看看vl53l0x_set_mode(&vl53l0x_dev,mode)这个函数
VL53L0X_Error vl53l0x_set_mode(VL53L0X_Dev_t *dev,u8 mode) { VL53L0X_Error status = VL53L0X_ERROR_NONE; uint8_t VhvSettings; uint8_t PhaseCal; uint32_t refSpadCount; uint8_t isApertureSpads; //vl53l0x_reset(dev); status = VL53L0X_StaticInit(dev); ///设备初始化 if(status!=VL53L0X_ERROR_NONE) goto error; delay_ms(2); status = VL53L0X_PerformRefCalibration(dev, &VhvSettings, &PhaseCal); if(status!=VL53L0X_ERROR_NONE) goto error; delay_ms(2); status = VL53L0X_PerformRefSpadManagement(dev, &refSpadCount, &isApertureSpads); if(status!=VL53L0X_ERROR_NONE) goto error; delay_ms(2); }
这个函数有很多内容,我暂时说两个,后面的实际上是设置,虽然我暂时不明白,但我比较了很多代码基本上是这个过程,所以没有具体的研究。这里的mode分为默认、高精度、长距离、高速四种测量模式。我只测量长距离和默认。默认最远距离为1.25m精度在3左右mm-1cm,长距离最可以测量2.1m精度在4左右cm左右。
以上两个函数分别介绍。这两个函数都是官方提供的API
VL53L0X_PerformRefCalibration这个函数呢?Ref校准有什么作用?根据官方解释
这是一个参考校准,但他没有说什么是参考校准,我也不知道。它还说它将消除中断。我不明白,但这是非常重要的。如果不执行参考,则无法在测量前测量此函数。
VL53L0X_PerformRefSpadManagement,该函数也应在测量前执行,否则会出错。
我个人的理解是,这两个函数实际上是启动传感器发射脉冲,但我不知道这是否正确
然后基本结束了。说说我耗时的地方
u8 VL_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
VL_SDA_IN();
for(i=0;i<8;i++ )
{
VL_IIC_SCL=0;
delay_us(4);
VL_IIC_SCL=1;
receive<<=1;
if(VL_READ_SDA)receive++;
delay_us(4); //1
}
if (!ack)
VL_IIC_NAck();
else
VL_IIC_Ack();
return receive;
}
就是上面这个函数,这个函数是不正确的。这个是I2C的read函数,后来参考一位网友的代码修改成如下:
u8 VL_IIC_Read_Byte(void)
{
unsigned char i,receive=0;
VL_SDA_IN();//
VL_IIC_SDA = 1;
delay_us(4);
for(i=0;i<8;i++ )
{
receive<<=1;
VL_IIC_SCL=0;
delay_us(4);
VL_IIC_SCL=1;
delay_us(4);
if(VL_READ_SDA)
receive |= 0x01;
delay_us(4); //1
}
VL_IIC_SCL = 0;
return receive;
}
至于这两个差别我在这里就不分析了,大家自行理解。我就是卡在这里花了两个星期才搞定这个传感器。这里面说明,I2C最重要的还是协议实现是否正确,以后写I2C的项目,一定要写好read和write个人推荐第二个read的写法。因为很多单片机都是这样写的。然后第一种写法是一个网友分享的代码里面的,该网友说他是可以执行的,但是在我的环境里没办法执行。所以拿到别人的源码不一定就是可以用的,要根据自己的情况修改,当然不是说别人的代码有错,毕竟别人验证成功了,只能说情况不一样,结果就可能不一样。
最后,这篇文章应该还有补充的,里面还有还有很多知识的,而且最近我开始做VL53L1X这个传感器可以达到4m。以后再更新。
参考链接:
https://download.csdn.net/download/loop222/10489957这个我就是基于这个链接的源码做修改的,但是这个源码在我的环境情况下不能直接使用,就是上面那个read函数的问题
基于stm32f103的VL53L0X红外测距_stm32vl53l0x-C代码类资源-CSDN下载我参照这位网友的read函数做了修改,然后就可以执行了。
至于我自己上传的代码今晚上传还没通过审核,所以需要等一下,通过审核之后我会把链接放上来,另外上面两个链接的代码我也有的,如果原作者同意的话我也可以通过邮件发给有需要的网友。
2019.4.24晚于广州
补充:我自己修改的代码,下载链接如下:VL53L0X+stm32激光测距_激光测距传感器stm32-C代码类资源-CSDN下载
补充:4m的VL53L1X的激光测距已经可以了,链接如下:vl53l1x+stm32激光测距分析(待修改)_tiramisu_L的博客-CSDN博客_vl53l1x