VL6180 使用坎坷过程 ...... by 矜辰所致
前言
最近项目上用到一款测距传感器 VL6180 ,实际网上资料已经很多了,而且都有现成的 Demo ,甚至拿来直接用都可以,实际上在使用 STM32 芯片做测试的时候,参考网上的现成例程,一切看起来都是正常的,但是在移植到项目需要的 51 上的时候,真的是一波三折,问题频出。
上一篇文章写到的 单片机中的 nop 函数 也是因为在移植的过程中遇到了问题,所以特地记录分析了一下,那么本文 主要就来说明一下在移植 VL6180 驱动的过程中遇到的问题,以及如何解决的。(本文的驱动为 软件 I2C 驱动)
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
目录
- 前言
- [一、 基本介绍](#一、 基本介绍)
-
- [1.1 测距范围](#1.1 测距范围)
- [1.2 引脚以及原理图](#1.2 引脚以及原理图)
- 二、驱动程序
-
- [2.1 VL6180 传感器驱动](#2.1 VL6180 传感器驱动)
- [2.2 I2C 通用驱动](#2.2 I2C 通用驱动)
- [2.3 us 延时函数](#2.3 us 延时函数)
- 三、移植问题说明
-
- [3.1 硬件设计注意事项](#3.1 硬件设计注意事项)
- [3.2 驱动移植](#3.2 驱动移植)
- [3.3 VL6180 主要问题(DataNotReady 单次读取一直等不到 Ready)](#3.3 VL6180 主要问题(DataNotReady 单次读取一直等不到 Ready))
-
- [`while((VL6180X_ReadByte(...&0x04)) `](#
while((VL6180X_ReadByte(...&0x04))
) - 尝试解决
- [`while((VL6180X_ReadByte(...&0x04)) `](#
- [3.4 解决问题](#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 上面测试成功了,结果如下图:
那对于上面推荐文章的驱动,我也是直接拿过来用的,当然,I2C 的通用驱动程序得自己实现,相信大家自己手头也应该有一份,我这里也再次放一遍,这个驱动好像是从以前正点原子那儿看到的一直沿用下来的。
2.2 I2C 通用驱动
i2c.c:
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:
c
#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
引脚宏定义:
c
#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 延时函数,需要我们自己实现。
c
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 单片机的时候,让我不得不去认真对待这个函数能够延时的时间,具体的可以参考我的上一篇博文:
但是如果大家在 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 数量使用了函数封装:
c
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
```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 语句,如果不成功就会卡主,如下图:
c
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 !!!!! 有数据了...... (果然还是时间问题,其实我是一肚子想吐槽的,这玩意画了我那么多时间,真是离谱 !!!)
下面上几个最后16MHz 的 51 核上修改的通用驱动(全部放出来有点多,我只是表明,我只是在减少 nop ):
c
#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 通讯时间只要差不多,在规定的范围内大差不差即可,也或许是因为自己不够谨慎,从一开始就把时序时间调节成一样。
也反应一个问题,做东西遇到问题如果不严谨的对待,一个简单的小问题可能会浪费自己大量时间。给自己一个教训,也给大家一个忠告,再简单的事情,也需要认真对待,才能高效的去完成它。
好了,本文就到这里,感谢大家!