小言_互联网的博客

STM32----IIC详解

372人阅读  评论(0)

一.IIC简介

1.IIC总线概述

IIC总线是飞利浦公司研发的两线制串行通信总线,IIC两线制包括:串行时钟线(SCL)和串行数据线(SDA)。串行时钟线(SCL)只能由主器件控制,串行数据线(SDA)实现双向数据传输(IIC通信属于同步、半双工串行通信)。IIC总线遵从主/从结构,可以实现一个主器件和多个从器件之间的通信,并且从器件永远不会主动给主器件发送数据。器件发送数据到总线上,则定义为发送器,器件从总线上读取数据,则定义为接收器(主器件和从器件都可以是发送器也可以是接收器)谁接收谁应答。

2.IIC总线物理结构

上拉电阻作用:确保总线空闲时为高电平

3.IIC总线通信方式

通过器件地址建立通信。器件地址:固定地址(4)+ 可编程地址(3)+ 读写为(0读1写)

IIC设备地址是一个7位地址,并且这个7位地址分成两部分,分别是固定地址(器件地址)和可编程地址(芯片管脚地址)。 4+3

  • 固定地址:IIC器件在生产时,芯片厂家已经固化在芯片内部的地址,使用者不可更改。
  • 可编程地址:由IIC器件地址管脚上的电平状态决定。地址管脚接电源则表示数字“1”,地址管脚接地则表示数字“0”。(可以根据器件说明修改地址 例如:迈莱芯MLX90640修改设备地址方法,I2C挂载多个MLX90640
  • 最低位为控制字节,控制读写方向(指主机的读写方向)。

IIC总线数据传输速度在标准模式下可达100Kbit/S,快速模式下可达400Kbit/S以及高速模式下可达3.4Mbit/S。

 

4.IIC模式配置

1)引脚配置

 

  • SCL,SDA可以配置成推挽输出、开漏输出(上拉电阻输出1)
  • SCL,SDA也可以配置成开漏输出、开漏输出(开漏输出为防止多个器件存在短路)  

                注意:必须器件内部自带上拉电阻      或者外界接上拉电阻   或者软件设置上拉电阻

  • SCL,SDA也可以配置成推挽输出、推挽输出与浮空输入(通过切换模式)

2)开漏输出和线与

硬件IIC:会自动配置为开漏输出,(不推荐不稳定)

软件IIC

推挽输出:输出0,N-MOS激活。 输出1,P-MOS激活

开漏输出(不带上拉电阻):输出0,N-MOS激活。   输出1,P-MOS不会激活,不会输出高电平

开漏输出(带上拉电阻):输出0,N-MOS激活。   输出1,P-MOS激活

简言之:开漏输出必须有上拉电阻才能输出高电平。目前单片机GPIO口可以通过软件设置配置上下拉

开漏输出的作用:

  1. 防止短路: 在一些情况下(比如总线), 多个GPIO口可能会连接在同一根线上, 存在某个GPIO输出高电平, 另一个GPIO输出低电平的情况. 如果使用推挽输出, 你会发现这个GPIO的VCC和另一个GPIO的GND接在了一起, 也就是短路了(凉凉了). 如果换成开漏输出呢? VCC和GND多了个电阻, 这样电路就是安全的.所以总线一般会使用开漏输出.
  2. 实现线与,减少一个与门,简化逻辑。同时当多个器件通讯的时候,因为线与. 如果主设备A拉高SDA时, 已经有其他主设备将SDA拉低了. 由于 1 & 0 = 0 那么主设备A在检查SDA电平时, 会发现不是高电平, 而是低电平. 说明其他主设备抢占总线的时间比它早, 主设备A只能放弃占用总线. 如果是高电平, 则可以占用.(SDA为高电平的时候才可以与器件进行通信,)

二.三种模式区别

IIC之所以分成三种模式,是由于SCL与SDA保持时间长短不同所决定的。例如:标准模式下要求SCL高电平保持时间最小为4.7us,快速模式下要求SCL高电平保持时间最小为0.7us. 这也是为什么高速率可以兼容低速率,而低速率不能兼容高速率的原因。

  标准/S 快速/F 高速/HSE
速率 100KHZ 400KHZ 3.4MHZ

例如:数据保持时间(为SCL低电

平时数据允许保存最长时间)

 

0.9us 72或150ns
器件寻址 7位(128个器件) 7位或10位(1024个器件) 7位或10位(1024个器件)
优化功能   快速模式器件的输入有抑制毛刺的功能
 
Hs 模式器件的输出可以抑制毛刺
 

 

更多区别请查看IIC协议规范

三.时序分析

以软件模拟推挽输出例100K时序为例

1)起始信号

SCL在高电平期间,SDA出现一个由高到低的跳变(SDA,SCL有最小保持时间 )


  
  1. void IIC_Start( void)
  2. {
  3. SDA_OUT();
  4. SDA_H; //串行数据线高电平(空闲信号)
  5. CLK_H; //串行时钟线高电平(空闲信号)
  6. Delay_us( 5);
  7. SDA_L; //串行数据线拉出下降沿
  8. Delay_us( 5);
  9. CLK_L; //串行时钟线拉出下降沿
  10. }

2)终止信号

SCL在高电平期间,SDA出现由低变高的跳变(SDA,SCL有最小保持时间 )


  
  1. void IIC_Stop( void)
  2. {
  3. SDA_OUT(); //输出模式
  4. CLK_L; //串行时钟线低电平
  5. SDA_L; //串行数据线低电平
  6. CLK_H; //串行时钟线高电平
  7. Delay_us( 5);
  8. SDA_H; //串行数据线高电平 上升沿
  9. Delay_us( 5);
  10. }

 

3)应答信号

SCL在高电平期间SDA始终处于低电平(SCL保持时间<= SDA保持时间)

需要在传输完毕一个字节后发送


  
  1. void IIC_Send_ACK( void)
  2. {
  3. CLK_L;
  4. SDA_OUT(); //输出模式
  5. SDA_L; //串行数据线低电平
  6. Delay_us( 5);
  7. CLK_H; //串行时钟线线高电平 上下
  8. Delay_us( 5);
  9. CLK_L; //串行时钟线线低电平
  10. }

4)非应答信号

SCL在高电平期间SDA始终处于高电平(SCL保持时间<= SDA保持时间)

需要在传输完毕一个字节后发送


  
  1. void IIC_Send_NoACK( void)
  2. {
  3. CLK_L;
  4. SDA_OUT();
  5. SDA_H; //串行数据线为高电平
  6. Delay_us( 5);
  7. CLK_H;
  8. Delay_us( 5);
  9. CLK_L;
  10. }

5)检测应答

SCL为高电平的时候可以读取SDA的状态,因此可以将SDA模式切换为输入模式,读取SDA引脚状态,0位应答,1位非应答

SCL为低电平允许数据发生变化

SDA:为高电平的时候可以占用总线,此时将SDA拉低,开始通信。当为低电平的时候,SDA已经被占用。
SCL: SCL为高电平的时候要求数据稳定  SCL为低电平的时候允许数据改变


  
  1. u8 IIC_Get_ACK( void)
  2. {
  3. u8 ERRTIME = 0; //超时变量
  4. SDA_IN();
  5. //SDA_L;// 没有影响
  6. Delay_us( 5);
  7. CLK_H;
  8. Delay_us( 5);
  9. while( READ_SDA() )
  10. {
  11. ERRTIME++;
  12. if(ERRTIME >= 250)
  13. {
  14. IIC_Stop();
  15. return 1;
  16. }
  17. }
  18. CLK_L;
  19. return 0;
  20. }

 

6)发送数据


  
  1. void IIC_Send_Data(u8 dat)
  2. {
  3. u8 i;
  4. SDA_OUT();
  5. CLK_L;
  6. for(i = 0; i < 8; i++) //分8次传输数据 一位一位传递 串行
  7. {
  8. if(dat & 0x80) //先发最高位 1000 0000
  9. {
  10. SDA_H; //写1
  11. }
  12. else
  13. {
  14. SDA_L; //写0
  15. }
  16. dat <<= 1; //左移操作 次高位-->最高位
  17. Delay_us( 5);
  18. CLK_H;
  19. Delay_us( 5);
  20. CLK_L;
  21. Delay_us( 5);
  22. }
  23. }

7)接收数据

将SDA切换为输入模式。拉高SCK电平,可以读取数据


  
  1. u8 IIC_Read_Data(u8 ack)
  2. {
  3. unsigned char i,date = 0;
  4. SDA_IN();
  5. for(i = 0; i < 8; i++)
  6. {
  7. CLK_L;
  8. Delay_us( 5);
  9. CLK_H;
  10. date <<= 1;
  11. Delay_us( 5);
  12. if( READ_SDA() )
  13. {
  14. date ++;
  15. }
  16. //Delay_us(2);
  17. }
  18. if(ack)
  19. {
  20. IIC_Send_ACK(); //发送应答
  21. }
  22. else
  23. {
  24. IIC_Send_NoACK(); //发送非应答
  25. }
  26. return date;
  27. }

 


转载:https://blog.csdn.net/qq_45604814/article/details/116099878
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场