适用于C51的VL53L0X驱动(基于arduino的VL53L0X驱动)

本文介绍了作者基于C51单片机STC8H3K64S4如何修改并集成VL53L0X激光测距模块的驱动,优化了IIC通信,提供了详细的初始化代码和解决IIC数据不稳定问题的方法。

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

1.简介

在研究了各位大神的代码后,结合购买模块时商家提供的arduino的VL53L0X驱动,我修改ardiuno的驱动写出了兼容C51的VL53L0X驱动。我的测试的单片机是STC8H3K64S4,IIC通信使用的是STC硬件的IIC。大家只需要把我的IIC驱动和显示代码修改成自己的,最后添加一个定时1ms的定时器作为VL53L0X的时基,就可以直接拿来用了。


这是本人第一次发帖,做的工作也比较简单,如果有什么做的不好的还请谅解。

参考的网站:
VL53L0X激光测距模块的单片机驱动程序 - 51单片机

使用上面网站的代码写出来的程序,这个代码只有最基本的测距功能,测距误差大概在±5%。对我刚刚接触VL53L0X时启发很大。
在使用商家提供的arduino的VL53L0X驱动后,由于设置了VL53L0X的相关寄存器,精度可以达到±1%。当然超过模块的1.2m的测距范围精度还是会差很多。

 

2.上代码

主函数 main.c
人机交互的相关代码替换成自己的就行了

void main()
{
	unsigned int temp;
	unsigned char high;
	//初始化
	gpio();
	Timer2_Init();  			//初始化定时器,用于传感器的时基
	UartInit();     			//初始化串口,用与显示传感器结果
	P16 = 0;    	 			 	//我的开发板的LED
	EA = 1;						
	IIC_Init();						//初始化IIC,用的是STC的硬件IIC
	
	

	VL53L0X_Init(0); 	//初始化VL53L0X的相关功能寄存区
	setTimeout(500);  //设置超时时间 500ms
	
	//模式选择
	//#define LONG_RANGE
	//#define HIGH_ACCURACY
	#define HIGHT_SPEED
	
	//长距离测距的相关API设置
	#if defined LONG_RANGE
  // lower the return signal rate limit (default is 0.25 MCPS)
  VL53L0X_setSignalRateLimit(0.1);
  // increase laser pulse periods (defaults are 14 and 10 PCLKs)
  VL53L0X_setVcselPulsePeriod(0, 18);
  VL53L0X_setVcselPulsePeriod(1, 14);
	#endif

	//高速测距的相关API设置
	#if defined HIGH_SPEED
		// reduce timing budget to 20 ms (default is about 33 ms)
		VL53L0X_setMeasurementTimingBudget(20000);
	//高精度测距的相关API设置
	#elif defined HIGH_ACCURACY
		// increase timing budget to 200 ms
		VL53L0X_setMeasurementTimingBudget(200000);
	#endif  
	
	while(1)
	{
		//读取一次VL53L0X的数据
		temp = VL53L0X_readRangeSingleMillimeters();
		
		//可以按照自己的显示模块修改
		//向串口发送距离结果
		//转换成ASCLL码		
		Uart_Send("Distance is " ,12);
		high = 0x30 + temp / 10000;
		Uart_Send(&high ,1);
		high = 0x30 + temp % 10000 / 1000;
		Uart_Send(&high ,1);
		high = 0x30 + temp % 1000 / 100;
		Uart_Send(&high ,1);
		high = 0x30 + temp % 100 / 10;
		Uart_Send(&high ,1);
		high = 0x30 + temp % 10;
		Uart_Send(&high ,1);
		Uart_Send("mm" ,2);
		//捕捉VL53L0X的超时
		if(VL53L0X_timeoutOccurred())
			Uart_Send("Timeout", 7);
		Uart_Send("\r\n", 2);
	}
}

void Uart1() interrupt 4
{
	if(RI)
		RI = 0;
}

VL53L0X的驱动 VL53L0X.c

代码太多了只显示一部分,详情看附件,大家用的时候记得添加时基就行了。

// Most of the functionality of this library is based on the VL53L0X API
// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted
// or paraphrased from the API source code, API user manual (UM2039), and the
// VL53L0X datasheet.

#include "inc/VL53L0X.h"
#include "inc/VL53L0X_IIC.h"
#include "NodeHandler.h"
#include <string.h>


unsigned char stop_variable;
unsigned long measurement_timing_budget_us;
unsigned int io_timeout = 0, timeout_start_ms, t2;
bit did_timeout;


//===================================
//你自己的定时器时基定时1ms
void Time2() interrupt 12
{
	t2++;
}
//==================================================


// Defines /////////////////////////////////////////////////////////////////////

// The Arduino two-wire interface uses a 7-bit number for the address,
// and sets the last bit correctly based on reads and writes
#define ADDRESS_DEFAULT 0x52

// Record the current time to check an upcoming timeout against
#define startTimeout() (timeout_start_ms = t2)

// Check if timeout is enabled (set to nonzero value) and has expired
#define checkTimeoutExpired() (io_timeout > 0 && ((unsigned  int)t2 - timeout_start_ms) > io_timeout)

// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs
// from register value
// based on VL53L0X_decode_vcsel_period()
#define decodeVcselPeriod(reg_val)      (((reg_val) + 1) << 1)

// Encode VCSEL pulse period register value from period in PCLKs
// based on VL53L0X_encode_vcsel_period()
#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1)

IIC驱动
有人喜欢模拟IIC,有人喜欢硬件IIC,他们各有各的优点,大家按照自己的喜好替换调我的IIC驱动就行了

#define VL53L0X_address 			0x52

//IIC写8位数据
void VL53L0X_Write(unsigned char address, unsigned char value)
{
	P_SW2 = 0xB0;
	Start();
	SendData(VL53L0X_address);
	RecvACK();
	SendData(address);
	RecvACK();
	SendData(value);
	RecvACK();
	Stop();
	Delay();
	P_SW2 = 0x00;
}

//IIC写16位数据
void VL53L0X_Write16Bit(unsigned char address, unsigned int value)
{
	P_SW2 = 0xB0;
	VL53L0X_Write(address++, (value >> 8) & 0xFF);
	VL53L0X_Write(address++, value & 0xFF);
	P_SW2 = 0x00;
}

//IIC写32位数据
//void VL53L0X_Write32Bit(unsigned char address, unsigned long value)
//{
//	Start();
//	SendData(VL53L0X_address);
//	RecvACK();
//	SendData(address);
//	RecvACK();
//	SendData((value >> 24) & 0xFF);
//	RecvACK();
//	SendData((value >> 16) & 0xFF);
//	RecvACK();
//	SendData((value >> 8) & 0xFF);
//	RecvACK();
//	SendData(value & 0xFF);
//	RecvACK();
//	Stop();
// 	Delay();
//}

//写多位数据
void VL53L0X_WriteMulti(unsigned char address, unsigned char const * src, unsigned char count)
{
	P_SW2 = 0xB0;	
	while(count--)
	{
		VL53L0X_Write(address++, *(src++));
	}
	P_SW2 = 0x00;
}

//IIC读8位数据
unsigned char VL53L0X_Read(unsigned char address)
{
	unsigned char receive;
	P_SW2 = 0xB0;
	Start();
	SendData(VL53L0X_address);
	RecvACK();
	SendData(address);
	RecvACK();
	
	Start();
	SendData(VL53L0X_address + 1);
	RecvACK();
	receive = RecvData();
	SendNAK();
	Stop();
	Delay();
	P_SW2 = 0x00;
	
	return receive;
}

//IIC读16位数据
unsigned int VL53L0X_Read16Bit(unsigned char address)
{
	unsigned int receive;
	P_SW2 = 0xB0;
	receive = VL53L0X_Read(address++) << 8;
	receive |= VL53L0X_Read(address);
	P_SW2 = 0x00;
	
	return receive;
}

//IIC读多位数据
void VL53L0X_ReadMulti(unsigned char address, unsigned char * dst, unsigned char count)
{
	while(count--)
	{
		*(dst++) = VL53L0X_Read(address++);
	}
	P_SW2 = 0x00;
}

3.其他
说一个我的IIC编程遇到的问题,我在IIC连续读SDA的数据的时候会存在有时读到的数据不稳定的情况
连续读的时候,如下读两个字节

//IIC读16位数据
unsigned int VL53L0X_Read16Bit(unsigned char address)
{
	unsigned int receive;
	IICStart();
	IICSendData(VL53L0X_address);
	IICWaitACK();
	IICSendData(address);
	IICWaitACK();
	IICStop();
	
	IICStart();
	IICSendData(VL53L0X_address + 1);
	IICWaitACK();

    //VL53L0X的内部寄存器索引会在读完一个寄存器后自加1
	receive = IICRecvData() << 8;
	IICSendACK(1);
    //索引加1后读下一个寄存器的数据
	receive |= IICRecvData();
	IICSendACK(0);

	IICStop();
	
	return receive;
}

VL53L0X的内部寄存器索引会在读完一个寄存器后自加1,索引加1后读下一个寄存器的数据。
那么如果这个自加1的索引自加的速度跟不上你IIC读取的速度呢。
这样IIC读取的数据就会不可靠。
连续读两个字节,我修改成连续两次读一个字节后,数据的读取就稳定许多了

//IIC读16位数据
unsigned int VL53L0X_Read16Bit(unsigned char address)
{
	unsigned int receive;
	P_SW2 = 0xB0;
	receive = VL53L0X_Read(address++) << 8;   //连续两次读取一个字节
	receive |= VL53L0X_Read(address);
	P_SW2 = 0x00;
	
	return receive;
}

或者大家有更好的见解也可以告诉我,毕竟我也是刚刚开始研究IIC,对IIC的认识也可能有错误(说不定我的程序就是建立在BUG上的  XD)

4.结果

5.工程源码及附件

链接:https://pan.baidu.com/s/1w-9VU3cBssHT22YotEhmUg 
提取码:hkvx

我在51黑电子论坛也写了一篇文章,但是因为编辑问题导致原帖子显示有问题,我又来csdn写了一篇

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值