ToF 测距传感器 VL6180 使用踩坑记

news2025/2/22 11:06:42
VL6180 使用坎坷过程  ...... by  矜辰所致

前言

最近项目上用到一款测距传感器 VL6180 ,实际网上资料已经很多了,而且都有现成的 Demo ,甚至拿来直接用都可以,实际上在使用 STM32 芯片做测试的时候,参考网上的现成例程,一切看起来都是正常的,但是在移植到项目需要的 51 上的时候,真的是一波三折,问题频出。

上一篇文章写到的 单片机中的 nop 函数 也是因为在移植的过程中遇到了问题,所以特地记录分析了一下,那么本文 主要就来说明一下在移植 VL6180 驱动的过程中遇到的问题,以及如何解决的。(本文的驱动为 软件 I2C 驱动)

我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!

目录

  • 前言
  • 一、 基本介绍
    • 1.1 测距范围
    • 1.2 引脚以及原理图
  • 二、驱动程序
    • 2.1 VL6180 传感器驱动
    • 2.2 I2C 通用驱动
    • 2.3 us 延时函数
  • 三、移植问题说明
    • 3.1 硬件设计注意事项
    • 3.2 驱动移植
    • 3.3 VL6180 主要问题(DataNotReady 单次读取一直等不到 Ready)
      • `while((VL6180X_ReadByte(...&0x04)) `
      • 尝试解决
    • 3.4 解决问题
  • 结语

一、 基本介绍

传感器描述的基本介绍这里就不多说了,也不是什么新的传感器了,ST 官网和网上资料一大堆,简单来说,就是一款可以 测距离 和 光照强度 的传感器,通讯接口为 I2C 。

参考资料可以直接从 ST 官网下载,本文主要针对他的 测距功能说明。

1.1 测距范围

可以测量的距离手册上是写的为 0 ~ 62 cm 最大(根据测量环境决定)。

个人建议如果你需要测量的是 20cm 以内的范围,传感器可以完美的适配。

如果大于 20cm ,就得斟酌一下,虽然我只需要测量 10 cm 以内的范围,但是发现超过 20 cm 的话准确度没那么高。(也许是我大距离没有认真测试,也许与我的测试环境有关。) 对于可以测量距离更远的传感器, ST 公司也有另一个型号的传感器:VL53L 系列的 ToF 测距传感器,测距可以达数米范围。

1.2 引脚以及原理图

扯远了,我们回来看看我们的 VL6180 ,主要看看传感器引脚以及如何自己画原理图以及 PCB。

当然,大多人接触到传感器都是现成的小模块,比如某宝买的小板子,使用起来直接给 3.3V GND SCL 和 SDA 接口连接即可,如下图:

在这里插入图片描述

关于上面某宝 VL6180 小模块的说明,模块上的 SCL SDA 是已经做过电平转换的,因为 SCL 和 SDA 是需要上拉到 2.8V ,所以使用小模块我们不管接 3.3V 或者 5V 的 IO,都可以直接连接使用, 但是如果 使用的单片机 为 1.8V 的 IO ,那么小模块不可以直接连接。

我在最开始的时候测试使用的是 STM32 ,也是直接用的现成的模块,但是因为后面移植到 1.8V 的单片机上(其实可以在小板子上面去掉电平转换的 MOS管后飞线使用),所以需要了解引脚以及原理图设计,所以这里还是需要说明一下这一部分,手册中对于引脚的定义如下:

在这里插入图片描述

手册上面推荐的原理图如下:

在这里插入图片描述

上图中红色部分,7脚本来是 NC ,但是建议连接 GND(不接的话一般正常使用也是没有问题的),因为在另一份官方文档中有这样的建议,如下图:

在这里插入图片描述
不仅如此,因为传感器使用的过程中,我遇到了一些问题,在官方的社区看到了 ST的工程师有过类似的回复,如下图:

在这里插入图片描述

以上介绍的是芯片的使用原理图,大家如果自己画 PCB ,直接按照上面的来就行了。

需要说明的是,官方推荐的 上拉是到 2.8V or 1.8V,我测试的时候发现 GPIO0 接 1.8V 不行,需要接2.8V。

然后 SCL 和 SDA ,1.8V 或者 2.8V 都可以,注意如果单片机的 IO 口为 1.8V高电平,那么可以直接连接,如果是 3.3V 或者 5V,就需要做电平转换。

GPIO1 不使用的话就什么都不接, 根据以上说明,那么传感器配上驱动就能正常使用了。

二、驱动程序

不管是自己画的 PCB 还是买的小模块,准备好了硬件后,就得写驱动程序了。

一个 软件 I2C 驱动包括,通用驱动(实现 i2c 起始信号,结束信号,读写功能的驱动) 和 传感器驱动(针对使用的传感器实现不同的数据读写的驱动)。

2.1 VL6180 传感器驱动

文章开头也说了,VL6180 传感器驱动程序实际上网上有现成的,比如博主找到的就是下面博文提供的驱动:

STM32驱动VL6180X测距

驱动不用多说,大家可以直接参考,我也确实在 STM32 上面测试成功了,结果如下图:

在这里插入图片描述

那对于上面推荐文章的驱动,我也是直接拿过来用的,当然,I2C 的通用驱动程序得自己实现,相信大家自己手头也应该有一份,我这里也再次放一遍,这个驱动好像是从以前正点原子那儿看到的一直沿用下来的。

2.2 I2C 通用驱动

i2c.c:

#include "i2c.h"

// ------------------------------------------------------------------
void i2c_init(void)  {
  
// the SDA and SCL pins are defined as input with pull up enabled
  // pins are initialized as inputs, ext. pull => SDA and SCL = high

}
// ------------------------------------------------------------------
// send start sequence (S)

void I2C_Start(void)
{
   MYIIC_DATA_HIGH;
   delay_us(5);
   MYIIC_CLK_HIGH;
   delay_us(10);
   MYIIC_DATA_LOW;
   delay_us(10);
   MYIIC_CLK_LOW; //使SCL置低,准备发送或者接受数据
   delay_us(10);
}

// ------------------------------------------------------------------
// send stop sequence (P)
void I2C_Stop(void)
{ 
   MYIIC_DATA_LOW;
   delay_us(5);
   MYIIC_CLK_LOW;
   delay_us(10);
   MYIIC_CLK_HIGH;
   delay_us(5);
   MYIIC_DATA_HIGH;
   delay_us(10); 
}
// ------------------------------------------------------------------
// returns the ACK or NACK
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 I2C_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	// MYSDA_IN;//SDA设置为输入
   for(i=0;i<8;i++ )
	{
        MYIIC_CLK_LOW;    //SCL为由低变高,在SCL高的时候去读 SDA的数据
        delay_us(10);
		MYIIC_CLK_HIGH;
        receive<<=1;  //第一次这里还是0,第二次开始每次接收的数据做移动一位,从高位开始接收
        if(MYIIC_DATA_STATE)receive++;   //如果数据为1,++以后就是1,数据为0,不执行就是0; 
		  delay_us(10); 
   }					 
   if (!ack)
        IIC_NAck();//发送nACK
   else
        IIC_Ack(); //发送ACK   
   return receive;
}

// ------------------------------------------------------------------

u8 I2C_Wait_Ack(void)
{
    u8 ucErrTime=0;
	delay_us(5);
    MYIIC_DATA_HIGH;delay_us(5);       //MCU DATA 置高,外面高就是高,外面低就是低
    MYIIC_CLK_HIGH; delay_us(5);       //CLK 高电平期间数据有效
    while(MYIIC_DATA_STATE)             //低电平为有应答,高电平无应答    
    {
      ucErrTime++;
		if(ucErrTime>250)
		{
			I2C_Stop();
			return 1;
		}  
    }
		delay_us(10);
    MYIIC_CLK_LOW;
    return 0;
}

void IIC_Ack(void)
{
	MYIIC_CLK_LOW;    //SCL为低,SDA为低,SCL为高,SDA为低,应答低电平有效,SCL为低,产生应答信号
	// MYSDA_OUT;
	MYIIC_DATA_LOW;
	delay_us(10);
	MYIIC_CLK_HIGH;
	delay_us(10);
	MYIIC_CLK_LOW;
	delay_us(10);
	MYIIC_DATA_HIGH;
}

void IIC_NAck(void)
{
	MYIIC_CLK_LOW;     //SCL为低,SDA为高,SCL为高,SCL为低
	// MYSDA_OUT;
	MYIIC_DATA_HIGH;
	delay_us(10);
	MYIIC_CLK_HIGH;
	delay_us(10);
	MYIIC_CLK_LOW;
}

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void I2C_Send_Byte(u8 txd)
{                        
   u8 t;   
	// MYSDA_OUT; 	    
   MYIIC_CLK_LOW;  //拉低时钟开始数据传输   ,SCL为低,SDA变高或者变低(数据位),SCL变高,SCL变低,期间SDA为1既1,为0既0
   for(t=0;t<8;t++)  //一个字节8位,一位一位发送
    {              
     MYIIC_CLK_LOW;
		if((txd&0x80)>>7)   //从最高位开始发送,如果是1,发送高电平
			MYIIC_DATA_HIGH;
		else
			MYIIC_DATA_LOW;
		txd<<=1; 	      //SDA处理完毕,此时可以将SCL拉高接受数据,拉高以后延时拉低
		delay_us(10);   //对TEA5767这三个延时都是必须的
		MYIIC_CLK_HIGH;
		delay_us(10); 
		MYIIC_CLK_LOW;	
		delay_us(5);
    }	 
}

i2c.h:

#ifndef _I2C_H_INCLUDED
#define _I2C_H_INCLUDED

#include "main.h"
#include "Datadef.h"
                         
// ------------------------

// ------------------------
// command's
#define I2C_WRITE             0 
#define I2C_READ              1
#define I2C_ACK               0
#define I2C_NACK              1


void I2C_Start(void);
void I2C_Stop(void);
u8 I2C_Wait_Ack(void);
void IIC_NAck(void);
void IIC_Ack(void);
void I2C_Send_Byte(u8 txd);
u8 I2C_Read_Byte(unsigned char ack);

#endif

引脚宏定义:

#define MYIIC_CLK_HIGH      HAL_GPIO_WritePin(I2C1_PORT,Pin_SCL,GPIO_PIN_SET) 
#define MYIIC_CLK_LOW  	    HAL_GPIO_WritePin(I2C1_PORT,Pin_SCL,GPIO_PIN_RESET) 

#define MYIIC_DATA_HIGH   HAL_GPIO_WritePin(I2C1_PORT,Pin_SDA,GPIO_PIN_SET) 
#define MYIIC_DATA_LOW    HAL_GPIO_WritePin(I2C1_PORT,Pin_SDA,GPIO_PIN_RESET)
#define MYIIC_DATA_STATE  HAL_GPIO_ReadPin(I2C1_PORT,Pin_SDA)

2.3 us 延时函数

延时函数单独拿出来说,因为在 HAL 库中没有现成的 us 延时函数,需要我们自己实现。

void delay_us(uint32_t Delay)
{
	uint32_t cnt = Delay * 8;   // 32Mhz ,  Delay * 8 其他主频适当调整
  	uint32_t i = 0;
  	for(i = 0; i < cnt; i++)__NOP();
}

实现的方式呢就是使用 NOP 函数,实际上当初在刚使用这个 us 的时候我都没有太在意这个延时的时间,因为反正能用,但是直到一直 VL6180 传感器到 51 单片机的时候,让我不得不去认真对待这个函数能够延时的时间,具体的可以参考我的上一篇博文:

单片机中的 nop() 延时以及其相关的基础扩展

但是如果大家在 stm32 上用,上述延时是可行的,根据自己的时钟频率适当调整即可,对于时钟频率比较低的单片机来说,需要好好计算,具体原因我在上面这篇博文中已经详细说明。

三、移植问题说明

好的,到目前为止,我们介绍了传感器,介绍了驱动程序,一切看起来都很正常,那我测试的时候也确实都正常。

对于本次项目需求:需要用一款 51 单片机,IO 口电平1.8V ,使用软件 I2C 。

3.1 硬件设计注意事项

硬件上也有需要注意的点,如果使用的是文章开头提到的现成的小模块,用 1.8V 的IO 是无法直接连接的,而且不仅仅是自己需要做一个电平转换,还要去掉小板子上面的电平转换,直接把 VL6180 的 SDA 和 SCL 飞线过来,用自己的电平转换,因为 I2C 这种形式的电平转换是有方向要求的。

具体的可查看我的另外一篇博文 结合实际聊聊电平转换电路(常用电平转换电路总结) 在文中有提到过 I2C 的电平转换电路:

在这里插入图片描述

注意好电平,其他地方倒没什么特别的了。

3.2 驱动移植

讲到驱动移植,实际上也没有难点,曾经也是在我以前的文章中,移植过另外一款 I2C 传感器,光照传感器到本次使用的 51 单片机上:BH1750 传感器实战教学 —— 驱动移植篇

所以也没什么好说的,走正常流程。

上面的 VL6180 传感器驱动都可以原封不动的拷贝过来,只需要实现以下 单片机上的 通用驱动,其实单片机上的通用驱动以前也是实现过的,包括 us 延时,直接使用 nop 。

至于 nop 怎么用,能不能类似 STM32 程序中那样写成函数,请查看上面《2.3 us 延时函数》小结推荐的我的上一篇博文,这里直接告诉大家,不能写成函数,那样需要花费相对更多的时间。

3.3 VL6180 主要问题(DataNotReady 单次读取一直等不到 Ready)

在大大小小遇到不同的问题后,最后来了本次最恶心的问题,程序卡在下面这条语句过不去:

while((VL6180X_ReadByte(...&0x04))

这条语句是在开启一次单次测量后,等待 Data 准备好的一条语句,需要传感器对应的寄存器对应的某一位置位,然后主机就知道已经有数据了,然后就可以直接获取数据:

在这里插入图片描述

到底为什么呢? 这里必须水一下,吐槽一下这个传感器的这一点,反正我最后是解决了,但是还是觉得恶心。

尝试解决

最开始的时候,把 nop 数量使用了函数封装:

void delay_us(uint32 Delay)
{
  uint32 cnt = Delay * 4;   // 32Mhz 8 ,其他频率其他倍数  	  16Mhz慢一点  4
  uint32 i = 0;
  for(i = 0; i < cnt; i++)_nop_();
}

导致 I2C 周期特别慢,于是有了上文 ms 级别的 I2C 通讯:

在这里插入图片描述

但是注意,奇怪的是这么慢的周期,和传感器的 I2C 通讯一切正常, 至少前面那么多初始化都是正常的。

我们来看一下 VL6180 手册中给出的 I2C 速度为 400kHz :

在这里插入图片描述

那么一般我们都是这么理解的:最大 400Khz ,小于这个周期的也是可以的!(一般都是这么理解,除非传感器又特殊说明不能慢于多少,有些时候有时间要求)

所以我们可以理解为,即便我们 us 延时函数没有写好,也能够正常通行,但是这时间也太慢了,我测试下下来,初始化工作需要消耗 接近 5 s 左右…… 有点离谱,所以我们得改,不用函数封装 nop 了,直接和以前例程一样,直接要多少个写多少个。

```c
void i2c_start(void)  {                                     
  sda_high(); 
		_nop_(); _nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
  scl_high();	
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); 
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
  sda_low();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
	scl_low();                                                   
    	_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
}

然后时间回到了正常的 us 级别,可能会比我们还想要的 5us ,10 us 多一点点 20到 30us 左右吧,但是这样就行了把,测试一下 … …

开始了各种怀疑(期间过程做过的事情)

发现不行,没有数据,折腾了好久,用示波器看一看,程序卡在上面那条语句。

怀疑是不是板子坏了?
换了一块小板子,不行!
换回 STM32 ,因为 STM32 是 3.3V 的IO ,所以也不能直接换,要注意电平转换之类的。
换回 STM32 就是正常的!
会不会是电平转换的问题?
1.8V 我不和 2.8V 转换了,我直接自己画一块板子,VL6180 的SDA 和 SCL 直接与 单片机的 IO 口连接,上拉到1.8V
然后找原理图,画了小板子
期间还发现一个问题,GPIO 拉高到1.8V 不行,得拉高到2.8V
等待自己画的板子… … 到了后测试 … …
还是不行,一样的问题等不到数据OK!
然后开始用示波器分析,先把在 STM32 上正常的流程观察一遍,我甚至都做了如下记录:

在这里插入图片描述

while((VL6180X_ReadByte(VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04));这条语句基本上循环几次就可以正常读到状态位
然后回到 51 的板子上面,从读取 ID 到初始化,到初始化,到读取数据前面一步,一条一条对应,都是正常的……
为了确保初始化的 I2C 通讯成功,我把驱动中的那些写寄存器都加了 while 语句,如果不成功就会卡主,如下图:

if(VL6180X_Read_ID() == VL6180X_DEFAULT_ID)
	{
		while(VL6180X_WriteByte(0x0207, 0x01));
		while(VL6180X_WriteByte(0x0208, 0x01));
		while(VL6180X_WriteByte(0x0096, 0x00));
		while(VL6180X_WriteByte(0x0097, 0xfd));
		while(VL6180X_WriteByte(0x00e3, 0x00));
		......

但是还是一样的问题,前面所有的通讯都正常,卡在等待数据准备好
当然,因为我一直觉得,I2C 时间多一点少一点问题不大,要不然前面也不能正常通讯啊
**
为了更加接近 STM32 的周期,我把 51 时间象征性的缩小了,减少了一些 nop ,还是同样的问题(这里是关键!!!! 考虑到过时间的问题,但是还是没有认真对待)
**
折腾了好久……

为什么? 要是有问题,干脆直接通讯不了,为什么前面所有的通讯都正常,直到给你指令等待回应等不到!!!

反正这个问题断断续续折腾了我好久,然后只能网上找找看大家有没有解决办法,实际上在上面推荐的驱动文章下面也有一些小伙伴有类似的疑问,同时在 C 站也有悬赏问题,但是没有答案的。

以至于我去官网把 VL6180 有关的资料基本全部下载看了一遍 ,想去看看有没有传感器使用注意事项,特别说明什么的 = = !:

在这里插入图片描述

然后还去 ST 官方社区 查看了所有关于 VL6180 的问题,确实也有类似的提问,如下图:

在这里插入图片描述

也没有得到可以的答案,最后自己还提问了,当然,最终还是没有什么结果。

3.4 解决问题

在曾经文章中我提到过 ,软件 I2C 通讯,如果出现一些莫名其妙的问题,大概率都是时间问题 !!

比如说,有的传感器在干某件事情的时候,必须需要等待多少时间后才能去读取,甚至有些传感器必须要快点读取吗,必须在某时间内通讯读取(这种我接触到的相对较少)。

在上面尝试解决问题的时候,我确实有把 51 上的 I2C 驱动的时钟周期放短(就是在 I2C 通用驱动中把 nop 的数量减少。),让他达到 us 级别,大概 30us 左右,接近 STM32 (STM32正常看下来是 15us 到 20us 的时钟周期)但是还是有问题,而且除了等待数据那一步,前面所有的通讯都正常!!!!当时也就没想那么多,因为在以前的不管 SHT21 温湿度传感器还是 BH1750 光照传感器,使用起来都是没有问题的,所以也并不认为因为多几个 nop 才导致了这种问题!

但是最后我实在是翻阅了所有资料,都重新做了几块测试板子,都是同样的问题,根本毫无头绪,没有理由的……

最后的最后,我实在没有办法,还是回到时间上面来把,因为在 51 上几个 nop ,几个 nop 修改也确实改过几次,但是都没有成功,我就想着既然 STM32 上面正常,我可不可以改变一下时间,看看他会不会也会出这样的问题,于是我尝试了一下,如下图:

在这里插入图片描述

我把 STM32 驱动上面的 nop 多加了一些,想看看到时是不是时间太长了,就会卡在那一步……

咦…… 出问题了:

在这里插入图片描述

原来真的是时间问题,读取数据那个地方对时间有要求?

而且我还发现,把上面的函数倍数改回8,也不正常,非得断电重启,而且有时候改回 8 断电重启也不正常,我还得改回 6 断电重启,然后再改回 8 就正常…… ,这种问题我不愿意去琢磨,但是我得出结论就是, VL6180 在某些通讯的时候对时间有要求 !!!!

那在 STM32 上重现了 51 上的问题以后,我就知道,还是时间的问题,于是乎,我老老实实的把 51 上的 i2c 驱动里面每一个函数的 nop 进行了处理,因为 STM32 上面是好的,我算着大概的时间,一一对应上去,我也实在是不愿意去一点一点定位到底是哪个地方的 延时有要求,因为太费时间而且没有什么意义,反正我把所有的函数都处理了一遍……

测试…… !!!! finally !!!!! 有数据了…… (果然还是时间问题,其实我是一肚子想吐槽的,这玩意画了我那么多时间,真是离谱 !!!)

最后用示波器看了一下,此时的通讯周期如下,高电平 10us 内:

在这里插入图片描述

下面上几个最后16MHz 的 51 核上修改的通用驱动(全部放出来有点多,我只是表明,我只是在减少 nop ):

#include "i2c.h"

void i2c_start(void)  {                                     
  sda_high(); 
		_nop_(); _nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
  scl_high();	
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); 
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
  sda_low();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
	scl_low();                                                   
    	_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
//		_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();	
}

//省略。。。。。。                                 
void I2C_Send_Byte(uint8 txd)
{                        
   uint8 t;   
	// MYSDA_OUT; 	    
   scl_low();   //拉低时钟开始数据传输   ,SCL为低,SDA变高或者变低(数据位),SCL变高,SCL变低,期间SDA为1既1,为0既0
   for(t=0;t<8;t++)  //一个字节8位,一位一位发送
    {              
    	scl_low();
		if((txd&0x80)>>7)   //从最高位开始发送,如果是1,发送高电平
		{
			sda_high();
		}
		else
		{
			sda_low();
		}
		txd<<=1; 	      //SDA处理完毕,此时可以将SCL拉高接受数据,拉高以后延时拉低
			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
		scl_high();
			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
		scl_low();	
			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();
    }	 
}

//省略。。。。。。   


uint8 I2C_Wait_Ack(void)
{
    uint8 ucErrTime=0;
	_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
    sda_high();
	_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
    scl_high(); 
	_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//			_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
    while(sda_read())             //低电平为有应答,高电平无应答    
    {
      ucErrTime++;
		if(ucErrTime>250)
		{
			I2C_Stop1();
			return 1;
		}  
    }
	_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
//	_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();

    scl_low();
    return 0;
}
//省略。。。。。。   

不想多说了,因为想想也没有太多的意义,一个简单的传感器,一个简单的 I2C 通讯,只是告诉大家,如果大家遇到类似的问题,大概率和我一样是时间问题。

结语

对于本次传感器出现的问题,花了我不少时间,真的是很奔溃的期间。

问题的根本也许是因为自己一直有误区,认为 I2C 通讯时间只要差不多,在规定的范围内大差不差即可,也或许是因为自己不够谨慎,从一开始就把时序时间调节成一样。

也反应一个问题,做东西遇到问题如果不严谨的对待,一个简单的小问题可能会浪费自己大量时间。给自己一个教训,也给大家一个忠告,再简单的事情,也需要认真对待,才能高效的去完成它。

好了,本文就到这里,感谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1162520.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

有多个网站的话申请什么样的SSL证书比较好?

在当今互联网时代&#xff0c;许多组织和个人都需要同时管理多个网站&#xff0c;这可能包括公司内部网站、在线商店、博客等。为了确保这些网站的安全性和数据保护&#xff0c;选择适合管理多个网站的SSL证书至关重要。今天小编就为大家详细介绍下&#xff0c;不同情况下多个网…

linux远程桌面管理工具xrdp

一、概述 我们知道&#xff0c;我们日常通过vnc来远程管理linux图形界面&#xff0c;今天分享一工具Xrdp&#xff0c;它是一个开源工具&#xff0c;允许用户通过Windows RDP访问Linux远程桌面。 除了Windows RDP之外&#xff0c;xrdp工具还接受来自其他RDP客户端的连接&#xf…

720VR全景视觉源码系统+一键生成网络三维实景 带完整的搭建教程

720VR全景视觉源码系统是一种基于HTML5和VR技术开发的源码系统&#xff0c;通过该系统&#xff0c;用户可以轻松创建具有高度真实感的3D实景体验。该系统具有易用性、可定制性以及高度可扩展性等特点&#xff0c;能够满足不同类型用户的视觉需求。借助720VR全景视觉源码系统&am…

欧科云链研究院:如何降低Web3风险,提升虚拟资产创新的安全合规

在香港Web3.0行业&#xff0c;技术推动了虚拟资产投资市场的快速增长&#xff0c;但另一方面&#xff0c;JPEX诈骗案等行业风险事件也接连发生&#xff0c;为Web3行业发展提供了重要警示。在近期的香港立法会施政报告答问会上&#xff0c;行政长官李家超表示&#xff0c;与诈骗…

Problem J. Prime Game--2018南京ICPC

解析&#xff1a; 分解每一个数&#xff0c;并且记录其前面相同素因子的位置&#xff0c;然后每次加上这段距离乘后面一直到结尾的距离。 #include<bits/stdc.h> using namespace std; const int N1e65; int n,a[N]; int t[N],idx; int pre[N]; void func(int x){idx0;f…

计算样本方差和总体方差

例如&#xff0c;给出了三个数据&#xff0c;194、183、175&#xff0c;现在计算样本方差和总体方差。 手工计算 它们的平均值 样本方差 总体方差 用excel计算 样本方差 总体方差

系列七、Mybatis的二级缓存

一、概述 Mybatis的二级缓存是多个sqlSession共享的&#xff0c;其作用域是mapper的同一个namespace&#xff0c;不同的sqlSession执行两次相同的查询&#xff0c;mybatis会将第一次执行完的数据放到二级缓存中&#xff08;坑&#xff1a;需要执行close操作&#xff0c;要不然不…

OSPF高级特性1(重发布,虚链路)

目录 OSPF高级特性(1) 一、OSPF不规则区域类型 二、解决方案 1、使用虚连接 演示一&#xff1a;非骨干区域无法和骨干区域保持连通 演示二&#xff1a;骨干区域被分割 2、使用多进程双向重发布 OSPF高级特性(1) 一、OSPF不规则区域类型 产生原因&#xff1a;区…

一文明白如何使用常用移动端(Android)自动化测试工具 —— Appium

自动化测试 自动化测试大家都有所了解&#xff0c;近十年来&#xff0c;自动化测试这项技能也一直是软件测试从业者想要掌握的一项技能&#xff0c;根据有关调研显示&#xff0c;希望掌握自动化测试技能的人十年来都约占七成 本文会带来自动化测试中的移动端&#xff08;Andro…

使用稳定扩散和SAM修改图像内容

推荐稳定扩散AI自动纹理工具&#xff1a; DreamTexture.js自动纹理化开发包 介绍 大型语言模型 &#xff08;LLM&#xff09; 和基础计算机视觉模型的最新突破为编辑图像或视频解锁了新的界面和方法。您可能听说过修复、复绘、生成填充和文本到图像;这篇文章将向您展示如何通过…

4.多层感知机-3GPT版

#pic_center R 1 R_1 R1​ R 2 R^2 R2 目录 知识框架No.1 多层感知机一、感知机1、感知机2、训练感知机3、图形解释4、收敛定理5、XOR问题6、总结 二、多层感知机1、XOR2、单隐藏层3、单隐藏层-单分类4、为什么需要非线性激活函数5、Sigmoid函数6、Tanh函数7、ReLU函数8、多类分…

uniapp 编译到模拟器(mumu)

一开始我是用逍遥模拟器&#xff0c;但这个玩意突然不好使了&#xff0c;一直加载卡在这页面 1、下载 官网下载&#xff1a;mumu模拟器12 2、打开mumu多开器&#xff0c;在右上角adb查看端口号 3、打开mumu模拟器 4、打开HBuiderX 工具—设置—运行配置 5、配置电脑的系统…

为什么Python爬虫教程众多,而专业工程师稀缺?

当谈到Python爬虫时&#xff0c;我们实际上在谈论网络爬虫&#xff0c;这是一种用编程技术从网页中提取数据的方法。Python爬虫在许多领域都有广泛应用&#xff0c;包括数据分析、数据挖掘和网络信息搜集。随着互联网的蓬勃发展&#xff0c;Python爬虫技术也在不断进化。 如果…

揭秘!自动化测试效率提升30%如何达成

一个全新的应用需要经过需求设计、应用开发、应用测试&#xff0c;及应用上架等几个阶段之后&#xff0c;才能到达用户手中。在应用测试中&#xff0c;测试的类型根据不同的开展时机&#xff0c;可以分为单元测试、集成测试、专项测试&#xff0c;以及上架测试。 单元测试指对软…

网络爬虫开发软件Screaming Frog SEO Spider mac中文版软件特点

Screaming Frog SEO Spider mac是一款SEO工具&#xff0c;可以帮助用户进行网站的SEO优化和分析。 Screaming Frog SEO Spider mac软件特点 网站爬取&#xff1a;可以快速扫描整个网站并列出所有内部和外部页面&#xff0c;包括URL&#xff0c;标题&#xff0c;描述和头信息等…

OSPF 高级特性3

一、OSPF安全特性 1、OSPF报文验证&#xff1a; 区域验证模式&#xff1a;在区域下配置一致的密码才能加入同一个区域。 [r3-ospf-1-area-0.0.0.0]authentication-mode md5 1 cipher 123456 接口验证模式&#xff1a;链路两端的接口必须配置一致的密码才能建立邻居关系 [r5-Gig…

成功创建百度百科词条,必备关键编辑技巧揭密!

公司成立后&#xff0c;可以创建自己的百度百科全书词条。然而&#xff0c;经常搜索可发现&#xff0c;有些企业词条只显示相关基本信息&#xff0c;而有些企业词条则包含了大量信息&#xff0c;显然与企业规模有关。 企业百科词条的内容是非常重要的。它应该包括企业的介绍&am…

Python语言高级实战-基于协程的方式来实现异步并发编程(附源码和实现效果)

实现功能 协程是一种轻量级的线程&#xff0c;可以在代码中定义异步任务&#xff0c;并在需要时挂起和恢复执行。Python提供了asyncio库来支持协程异步编程。使用async def await的方式定义协程。 async 用来声明一个函数为异步函数&#xff0c;异步函数的特点是能在函数执行…

前端H5用Canvas画布做类似银行签名的操作

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>签名页面</title> </head> <body> <canvas id"signatureCanvas" width"400" height"200"></canvas> <bu…

WebGIS面试题(第三期)

WebGIS面试题&#xff08;第三期&#xff09;&#xff08;某公司&#xff09;上机笔试题 以下题目为南京某公司上机笔试题&#xff0c;题目仅为部分题目&#xff0c;全部题目在公众号{GISer世界}&#xff0c;答案仅供参考&#xff0c;需要电子版在公众号{GISer世界}内回复“面…