STM32L052C8T6通过I2C模拟读16位数
最近项目用到一款接近光传感器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)
{
u16data=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应答函数。确实没看出问题到底出在哪里啊:'(
问题解决了,IIC驱动是参照正点原子的,原来正点原子的读函数是包含响应函数与不响应函数
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uchar IIC_Read_Byte(uchar ack)
{
uchar i,receive=0;
IIC_SDA_Init(1);//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_LOW;
delay_us(2);
IIC_SCL_HIGH;
receive<<=1;
if(IIC_SDA_READ)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
明白这点将读数据的函数修改为
u16 VCNL4040_RdData(uchar cmd)
{
u16data=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_Wait_Ack();
data_L=IIC_Read_Byte(1);//读出16位数据中的低八位,应答
// IIC_Ack();//主机应答
data_H=IIC_Read_Byte(0);//读出16位数据中的高八位,不应答
// IIC_Ack();
IIC_Stop();//产生一个停止条件
data = data_L+data_H*256;//16bit数据
return data;
}
亲测,可以正确读取设备16位ID数据 STM32的速度快,一般都要加上响应函数吧。 晕,管管把已解决的问题拿来做今日话题。
也谢谢楼主,把解决的方法列了出来。 ts2000 发表于 2020-7-29 10:25
STM32的速度快,一般都要加上响应函数吧。
用的正点原子的IIC驱动,IIC_Read_Byte()函数里面已经包含了ACK与NACK,如果我再在后面加上多余的应答函数,数据读出来就出现问题了 楼主你好,最近我也在研究VCNL4040,但是我这边I2C通讯异常,主要是没有接收到对面发来的ACK从第一次发送 地址(0X60) 就没有收到,后续无论是收发数据都没收到ACK .
附上我的软件,波形图(开始+发生地址),以及硬件原理图,希望得到楼主的帮助,谢谢! 另外我用的是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_tdata=0,data_H=0;
uint16_tdata_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;
}
感谢楼主,我也在做这个,一直没通,后来看了楼主的图, 我看了你的截图,发现地址应该是0XC0 和 0XC1,你标记了SLAVE ADDRESS,谢谢兄弟!我一直以为是0X60 0X61 wzy8430121 发表于 2020-8-6 15:39
感谢楼主,我也在做这个,一直没通,后来看了楼主的图, 我看了你的截图,发现地址应该是0XC0 和 0XC1,你 ...
哈哈哈。抱歉没有及时回复你。最开始我也以为地址是0x60,后面发现这些个IIC器件手册上面写的7bit地址是不包含读写操作的,当然有些手册的地址是会包含读写的。这7bit从机地址用起来是需要左移一位变成8位,像VCNL4040手册上面写的0x60左移一位就是0xC0了,最后一位用来设置读写操作。:lol
页:
[1]