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

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数据
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_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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版