最近项目用到一款接近光传感器VCNL4040,在连续读16位数据的时候,发现低八位读出来是正确的,但是高八位读出来的都是0xFF。我看Datasheet上面每次读完数据之后都是主机主动应答,程序也是这么写的,但最后的结果还是不对。不知道论坛里面大佬们知道是什么问题吗???救救孩子吧- void VCNL4040_WrData(uchar cmd,uchar data_L,uchar data_H)
- {
- IIC_Start();
-
- IIC_Send_Byte(Write_SlaveAddress);//写模式
- IIC_Wait_Ack();
- IIC_Send_Byte(cmd);//发送命令代码
- IIC_Wait_Ack();
-
- IIC_Send_Byte(data_L);//发送数据低八位
- IIC_Wait_Ack();
- IIC_Send_Byte(data_H);//发送数据高八位
- IIC_Wait_Ack();
-
- IIC_Stop();//产生一个停止条件
- }
- //函数名称:VCNL4040_RdData(uchar cmd)
- //功能描述: 从VCNL4040指定命令代码中读出16位数据
- //参数说明:Cmd为指定命令代码
- //-----------------------------------------------------------------------------------------
- u16 VCNL4040_RdData(uchar cmd)
- {
- u16 data=0,data_H=0;
- u16 data_L=0;
-
- IIC_Start();//产生一个开始条件
-
- IIC_Send_Byte(Write_SlaveAddress);//写操作
- IIC_Wait_Ack();
-
- IIC_Send_Byte(cmd);//发送命令代码
- IIC_Wait_Ack();
-
- IIC_Start();
- IIC_Send_Byte(Read_SlaveAddress);//读操作
- IIC_Ack();
-
- data_L=IIC_Read_Byte(0);//读出16位数据中的低八位
- IIC_Ack();
-
-
- data_H=IIC_Read_Byte(0);//读出16位数据中的高八位
- IIC_Ack();
-
- IIC_Stop();//产生一个停止条件
-
- data = data_L+data_H*256;//16bit数据
-
- return data;
- }
复制代码 上面是我写的读写函数
- //产生ACK应答
- void IIC_Ack(void)
- {
- IIC_SCL_LOW;
- IIC_SDA_Init(0);
- IIC_SDA_LOW;
- delay_us(2);
- IIC_SCL_HIGH;
- delay_us(2);
- IIC_SCL_LOW;
- }
复制代码 这是IIC的ACK应答函数。确实没看出问题到底出在哪里啊
|
明白这点将读数据的函数修改为
亲测,可以正确读取设备16位ID数据
也谢谢楼主,把解决的方法列了出来。
用的正点原子的IIC驱动,IIC_Read_Byte()函数里面已经包含了ACK与NACK,如果我再在后面加上多余的应答函数,数据读出来就出现问题了
附上我的软件,波形图(开始+发生地址),以及硬件原理图,希望得到楼主的帮助,谢谢! 另外我用的是PIC的MCU,IO的写法稍微有点区别.
void IIC_Start(void)
{
SDA_OUT();
IIC_SDA=1;
IIC_SCL=1;
__delay_us(4);
IIC_SDA=0;
__delay_us(4);
IIC_SCL=0;
}
void IIC_Stop(void)
{
SDA_OUT();
IIC_SCL=0;
IIC_SDA=0;
__delay_us(4);
IIC_SCL=1;
IIC_SDA=1;
__delay_us(4);
}
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN();
IIC_SDA=1;__delay_us(1);
IIC_SCL=1;__delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
__delay_us(2);
IIC_SCL=1;
__delay_us(2);
IIC_SCL=0;
}
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
__delay_us(2);
IIC_SCL=1;
__delay_us(2);
IIC_SCL=0;
}
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
__delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
__delay_us(2);
IIC_SCL=0;
__delay_us(2);
}
}
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
__delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
__delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
void VCNL4040_WrData(uint8_t cmd,uint8_t data_L,uint8_t data_H)
{
IIC_Start();
IIC_Send_Byte(Write_SlaveAddress);//写模式
IIC_Wait_Ack();
IIC_Send_Byte(cmd);//发送命令代码
IIC_Wait_Ack();
IIC_Send_Byte(data_L);//发送数据低八位
IIC_Wait_Ack();
IIC_Send_Byte(data_H);//发送数据高八位
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
}
uint16_t VCNL4040_RdData(uint8_t cmd)
{
uint16_t data=0,data_H=0;
uint16_t data_L=0;
IIC_Start();//产生一个开始条件
IIC_Send_Byte(Write_SlaveAddress);//写操作
IIC_Wait_Ack();//主机等待应答
IIC_Send_Byte(cmd);//发送命令代码
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(Read_SlaveAddress);//读操作
IIC_Wait_Ack();
data_L=IIC_Read_Byte(1);//读出16位数据中的低八位,应答
data_H=IIC_Read_Byte(0);//读出16位数据中的高八位,不应答
IIC_Stop();//产生一个停止条件
data = data_L+data_H*256;//16bit数据
return data;
}
哈哈哈。抱歉没有及时回复你。最开始我也以为地址是0x60,后面发现这些个IIC器件手册上面写的7bit地址是不包含读写操作的,当然有些手册的地址是会包含读写的。这7bit从机地址用起来是需要左移一位变成8位,像VCNL4040手册上面写的0x60左移一位就是0xC0了,最后一位用来设置读写操作。