你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32L052C8T6通过I2C模拟读16位数

[复制链接]
回首,相濡以沫 提问时间:2020-7-27 17:07 /
最近项目用到一款接近光传感器VCNL4040,在连续读16位数据的时候,发现低八位读出来是正确的,但是高八位读出来的都是0xFF。我看Datasheet上面每次读完数据之后都是主机主动应答,程序也是这么写的,但最后的结果还是不对。不知道论坛里面大佬们知道是什么问题吗???救救孩子吧
  1. void VCNL4040_WrData(uchar cmd,uchar data_L,uchar data_H)
  2. {                   
  3.                 IIC_Start();
  4.        
  5.                 IIC_Send_Byte(Write_SlaveAddress);//写模式
  6.                 IIC_Wait_Ack();       

  7.                 IIC_Send_Byte(cmd);//发送命令代码
  8.                 IIC_Wait_Ack();       
  9.        
  10.                 IIC_Send_Byte(data_L);//发送数据低八位
  11.                 IIC_Wait_Ack();       

  12.                 IIC_Send_Byte(data_H);//发送数据高八位
  13.                 IIC_Wait_Ack();       
  14.        
  15.                 IIC_Stop();//产生一个停止条件               
  16. }


  17. //函数名称:VCNL4040_RdData(uchar cmd)
  18. //功能描述: 从VCNL4040指定命令代码中读出16位数据
  19. //参数说明:Cmd为指定命令代码
  20. //-----------------------------------------------------------------------------------------
  21. u16 VCNL4040_RdData(uchar cmd)
  22. {                                  
  23.                 u16  data=0,data_H=0;                           
  24.                 u16 data_L=0;
  25.        
  26.                 IIC_Start();//产生一个开始条件
  27.        
  28.                 IIC_Send_Byte(Write_SlaveAddress);//写操作
  29.                 IIC_Wait_Ack();         
  30.                
  31.                 IIC_Send_Byte(cmd);//发送命令代码
  32.                 IIC_Wait_Ack();       
  33.        
  34.                 IIC_Start();
  35.                 IIC_Send_Byte(Read_SlaveAddress);//读操作
  36.                 IIC_Ack();       
  37.        
  38.                 data_L=IIC_Read_Byte(0);//读出16位数据中的低八位       
  39.                 IIC_Ack();       
  40.        

  41.        
  42.                 data_H=IIC_Read_Byte(0);//读出16位数据中的高八位
  43.                 IIC_Ack();       
  44.        
  45.                 IIC_Stop();//产生一个停止条件
  46.        
  47.                 data = data_L+data_H*256;//16bit数据
  48.        
  49.                 return data;
  50. }
复制代码
上面是我写的读写函数
  1. //产生ACK应答
  2. void IIC_Ack(void)
  3. {
  4.                 IIC_SCL_LOW;
  5.                 IIC_SDA_Init(0);
  6.                 IIC_SDA_LOW;
  7.                 delay_us(2);
  8.                 IIC_SCL_HIGH;
  9.                 delay_us(2);
  10.                 IIC_SCL_LOW;
  11. }
复制代码
这是IIC的ACK应答函数。确实没看出问题到底出在哪里啊




这是Datasheet读写流程。

这是Datasheet读写流程。
收藏 评论7 发布时间:2020-7-27 17:07

举报

7个回答
回首,相濡以沫 回答时间:2020-7-27 18:14:25
问题解决了,IIC驱动是参照正点原子的,原来正点原子的读函数是包含响应函数与不响应函数
  1. //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
  2. uchar IIC_Read_Byte(uchar ack)
  3. {
  4.                 uchar i,receive=0;

  5.                 IIC_SDA_Init(1);//SDA设置为输入

  6.                 for(i=0;i<8;i++ )
  7.                 {
  8.                         IIC_SCL_LOW;
  9.                         delay_us(2);
  10.                         IIC_SCL_HIGH;
  11.                         receive<<=1;
  12.                        
  13.                         if(IIC_SDA_READ)receive++;   
  14.                         delay_us(1);
  15.                 }                                         
  16.                 if (!ack)
  17.                 IIC_NAck();//发送nACK
  18.                 else
  19.                 IIC_Ack(); //发送ACK   
  20.                
  21.                 return receive;
  22. }
复制代码

明白这点将读数据的函数修改为
  1. u16 VCNL4040_RdData(uchar cmd)
  2. {                                  
  3.                 u16  data=0,data_H=0;                           
  4.                 u16 data_L=0;
  5.        
  6.                 IIC_Start();//产生一个开始条件
  7.        
  8.                 IIC_Send_Byte(Write_SlaveAddress);//写操作
  9.                 IIC_Wait_Ack();//主机等待应答
  10.                
  11.                 IIC_Send_Byte(cmd);//发送命令代码
  12.                 IIC_Wait_Ack();       
  13.        
  14.                 IIC_Start();
  15.                 IIC_Send_Byte(Read_SlaveAddress);//读操作
  16.                 IIC_Wait_Ack();               
  17.        
  18.                 data_L=IIC_Read_Byte(1);//读出16位数据中的低八位,应答
  19. //                IIC_Ack();//主机应答
  20.                 data_H=IIC_Read_Byte(0);//读出16位数据中的高八位,不应答
  21. //            IIC_Ack();               

  22.                 IIC_Stop();//产生一个停止条件
  23.        
  24.                 data = data_L+data_H*256;//16bit数据
  25.        
  26.                 return data;
  27. }
复制代码

亲测,可以正确读取设备16位ID数据
小小超 回答时间:2020-7-29 10:25:30
STM32的速度快,一般都要加上响应函数吧。
网络孤客 回答时间: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_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;
}

@X(%%WZ)3`Y%QNL%E$S8_YA.png 673634A60C6FFA3B61EC137486F3104E.jpg

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了,最后一位用来设置读写操作。
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版