回首,相濡以沫 发表于 2020-7-27 17:07:31

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应答函数。确实没看出问题到底出在哪里啊:'(




回首,相濡以沫 发表于 2020-7-27 18:14:25

问题解决了,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数据

ts2000 发表于 2020-7-29 10:25:30

STM32的速度快,一般都要加上响应函数吧。

ldptest 发表于 2020-7-29 11:20:53

晕,管管把已解决的问题拿来做今日话题。

也谢谢楼主,把解决的方法列了出来。

回首,相濡以沫 发表于 2020-7-29 17:37:16

ts2000 发表于 2020-7-29 10:25
STM32的速度快,一般都要加上响应函数吧。

用的正点原子的IIC驱动,IIC_Read_Byte()函数里面已经包含了ACK与NACK,如果我再在后面加上多余的应答函数,数据读出来就出现问题了

wzy8430121 发表于 2020-8-6 15:27:05

楼主你好,最近我也在研究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;
}



wzy8430121 发表于 2020-8-6 15:39:15

感谢楼主,我也在做这个,一直没通,后来看了楼主的图, 我看了你的截图,发现地址应该是0XC0 和 0XC1,你标记了SLAVE ADDRESS,谢谢兄弟!我一直以为是0X60 0X61

回首,相濡以沫 发表于 2020-8-6 18:14:10

wzy8430121 发表于 2020-8-6 15:39
感谢楼主,我也在做这个,一直没通,后来看了楼主的图, 我看了你的截图,发现地址应该是0XC0 和 0XC1,你 ...

哈哈哈。抱歉没有及时回复你。最开始我也以为地址是0x60,后面发现这些个IIC器件手册上面写的7bit地址是不包含读写操作的,当然有些手册的地址是会包含读写的。这7bit从机地址用起来是需要左移一位变成8位,像VCNL4040手册上面写的0x60左移一位就是0xC0了,最后一位用来设置读写操作。:lol
页: [1]
查看完整版本: STM32L052C8T6通过I2C模拟读16位数