【硬件通信协议】1. 详细解析IIC通信协议

【硬件通信协议】1. 详细解析IIC通信协议版权声明:欢迎交流 https://blog.csdn.net/sishuihuahua/article/details/88128761 </div> <linkrel=”stylesheet”href=”https://csdnimg.cn/release/phoenix/template/c…

大家好,又见面了,我是你们的朋友全栈君。

				版权声明:欢迎交流					https://blog.csdn.net/sishuihuahua/article/details/88128761				</div>
							            <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-f57960eb32.css">
					<div class="htmledit_views" id="content_views">
            <h1 style="margin-left:0cm;"><a name="t0"></a>1、IIC简介</h1>

        I2C(Inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它只需要两根信号线来完成信息交换。I²C的一个特殊优势是微控制器只需两个通用I / O引脚和软件即可控制器件芯片网络。I2C最早是飞利浦在1982年开发设计并用于自己的芯片上,一开始只允许100kHz、7-bit标准地址。1992年,I2C的第一个公共规范发行,增加了400kHz的快速模式以及10-bit扩展地址。

        在I2C的基础上,1995年Intel提出了“System Management Bus” (SMBus),用于低速设备通信,SMBus 把时钟频率限制在10kHz~100kHz,但I2C可以支持0kHz~5MHz的设备:

  • 普通模式(100kHz即100kbps)、
  • 快速模式(Fm)(400kHz)、
  • 快速模式+(Fs+)(1MHz)、
  • 高速模式(Hs)(3.4MHz)、
  • 超高速模式(UFm)(5MHz)。

注:基于IIC是Master与Slave模式,所以两者间的通信要保持时钟频率的一致。IIC是半双工。

2、 IIC应用

       I²C适用于外围设备,其简单性和低制造成本比速度更重要。I²C总线的常见应用包括:

  • 通过小型ROM配置表描述可连接设备,以实现“ 即插即用 ”操作,例如:
  1. 双列直插式内存模块(DIMM)上的串行存在检测(SPD)EEPROM
  2. 通过VGADVIHDMI连接器为显示器提供扩展显示识别数据(EDID)。
  • 通过SMBus对PC系统进行系统管理;
  1. SMBus引脚分配在常规PCIPCI Express连接器中。
  • 访问保持用户设置的实时时钟NVRAM芯片。
  • 访问低速DACADC
  • 更改显示器中的对比度,色调和色彩平衡设置(通过显示数据通道)。
  • 改变智能扬声器的音量。
  • 控制小型(例如功能手机OLEDLCD显示器。
  • 读取硬件监视器和诊断传感器,例如风扇的速度。
  • 打开和关闭系统组件的电源

3、IIC协议

       I2C协议把传输的消息分为两种类型的帧:

                地址帧 —— 用于master指明消息发往哪个slave;

                数据帧(单个或者连续) —— 由master发往slave的数据(或由slave发往master),每一帧是8-bit的数据。

       通常我们所说的IIC读写是相对于Master来说的。

        SCL变为低电平后,数据置于SDA线上,并在SCL线变为高电平后进行采样。时钟边沿和数据读/写之间的时间由总线上的器件定义,并且在芯片与芯片之间会有所不同。

下图描述的是一个IIC完整时序图,从左往右依次看,大致总结为两类:

IIC写寄存器的标准流程

  1. Master发起START
  2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
  3. Slave发送ACK
  4. Master发送reg addr(8bit),等待ACK
  5. Slave发送ACK
  6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
  7. Slave发送ACK第6步和第7步可以重复多次,即顺序写多个寄存器
  8. Master发起STOP

IIC读寄存器流程:

  1. Master发送I2Caddr(7bit)和 W操作1(1bit),等待ACK
  2. Slave发送ACK
  3. Master发送reg addr(8bit),等待ACK
  4. Slave发送ACK
  5. Master发起START
  6. Master发送I2C addr(7bit)和 R操作1(1bit),等待ACK
  7. Slave发送ACK
  8. Slave发送data(8bit),即寄存器里的值
  9. Master发送ACK
  10. 第8步和第9步可以重复多次,即顺序读多个寄存器

https://cdn.sparkfun.com/assets/6/4/7/1/e/51ae0000ce395f645d000000.png

下面会详细这些流程:

3.1 开始条件(Start Condition)

        要启动地址帧,Master将SCL保持为高电平并将SDA拉低。也就是说,开始条件表现为:SCL为高电平时,SDA线上的高电平到低电平的跳变即定义了START条件。这使得所有Slave都注意到传输即将开始。如果两个Master希望一次获得总线的所有权,则无论哪个设备将SDA拉低,第一个将SDA拉低的将获得对总线的控制权。Master可以发出重复启动,启动新的通信序列而不放弃对其他Master的控制; 我们稍后再谈。 

https://img-blog.csdn.net/20170820192543246?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFzb25jaGVuX2diZA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

 

3.2 地址帧(Address Frame)

        在任何新的通信序列中,地址帧始终是第一个。对于7位地址,地址首先输出最高有效位(MSB),然后是R / W位,指示这是读(1)还是写(0)操作。

        帧的第9位是NACK / ACK位。所有帧(数据或地址)都是这种情况。一旦发送帧的前8位,接收设备就可以控制SDA。如果接收设备在第9个时钟脉冲之前没有将SDA线拉低,则可以推断出接收设备要么没有接收到数据,要么不知道如何解析消息。在这种情况下,则由master来决定如何处理(stop或repeated start condition),也就是代码中等待超时需要做什么处理。

【硬件通信协议】1. 详细解析IIC通信协议

 

3.3 数据帧(Data Frame)

        在发送地址帧之后,可以开始传输数据。Master将以规则的间隔继续生成时钟脉冲,数据将由Master或Slave置于SDA上,具体取决于R / W位是否指示读或写操作。数据帧的数量是任意的,并且大多数从器件将自动递增内部寄存器,这意味着后续读取或写入将来自下一个寄存器。

3.3.1 写一个寄存器:

http://img609.ph.126.net/j7OZijBSkop1sPycnNnBfQ==/1892919218380911741.jpg

3.3.2 写多个寄存器:

http://img535.ph.126.net/j7w9Dcj4sYiPaZA5jV1Emg==/1297318167660661668.jpg

3.3.3 读一个寄存器:

http://img837.ph.126.net/0_dFcVHoWn1lQOLdn6-w7Q==/770678486235691412.jpg

3.3.4读多个寄存器:

http://img611.ph.126.net/L9QOsROhgTgCezJCVcQvRQ==/1669991036826428611.jpg

3.4 停止条件(Stop Condition)

        一旦发送了所有数据帧,主设备将生成停止条件。停止条件由SCL上0-> 1转换后 SDA上的0-> 1(低到高)转换定义,SCL保持高电平。在正常的数据写操作时,SDA上的值应该不会当SCL为高电平改变,以避免错误的停止条件。图如3.1小节所示。

4、IIC协议的高级特性

4.1 10-bit地址

       在10-bit地址的I2C系统中,需要两个帧来传输slave的地址。第一个帧的前5个bit固定为b11110,后接slave地址的高2位,第8位仍然是R/W位,接着是一个ACK位,由于系统中可能有多个10-bit slave设备地址的高2bit相同,因此这个ACK可能由多有slave设备设置。第二个帧紧接着第一帧发送,包含slave地址的低8位(7:0),接着该地址的slave回复一个ACK(或NACK)。

注意:10-bit地址的设备和7-bit地址的设备在一个系统中是可以并存的,因为7-bit地址的高5位不可能是b11110。实际上对于7-bit的从设备地址,合法范围为b0001XXX-b1110XXX,’X’表示任意值,因此该类型地址最多有112个(其他为保留地址[1])。

        两个地址帧传输完成后,就开始数据帧的传输了,这和7-bit地址中的数据帧传输过程相同。

https://cdn.sparkfun.com/assets/1/2/b/e/4/51ae005dce395ff21a000003.png

4.2 重复开始条件(repeated start condition)

        有时master需要在一次通信中进行多次消息交换(例如与不同的slave传输消息,或切换读写操作),并且期间不希望被其他master干扰,这时可以使用“重复开始条件”

        在一次通信中,master可以产生多次start condition,来完成多次消息交换,最后再产生一个stop condition结束整个通信过程。由于期间没有stop condition,因此master一直占用总线,其他master无法切入。

        为了产生一个重复的开始条件,SDA在SCL低电平时拉高,然后SCL拉高。接着master就可以产生一个开始条件继续新的消息传输(按照正常的7-bit/10-bit地址传输时序)。重复开始条件的传输时序如下图所示:

【硬件通信协议】1. 详细解析IIC通信协议

 

4.3 时钟拉伸

       有时,低速slave可能由于上一个请求还没处理完,尚无法继续接收master的后续请求,即master的数据传输速率超过了slave的处理能力。这种情况下,slave可以进行时钟拉伸来要求master暂停传输数据。

       通常时钟都是由master提供的,slave只是在SDA上放数据或读数据。而时钟拉伸则是slave在master释放SCL后,将SCL主动拉低并保持,此时要求master停止在SCL上产生脉冲以及在SDA上发送数据,直到slave释放SCL(SCL为高电平)。之后,master便可以继续正常的数据传输了。可见时钟拉伸实际上是利用了时钟同步的机制(见下文),只是时钟由slave产生。

       如果系统中存在这种低速slave并且slave实现了clock stretching,则master必须实现为能够处理这种情况,实际上大部分slave设备中不包含SCL驱动器的,因此无法拉伸时钟。

       所以更完整的I2C数据传输时序图为:

【硬件通信协议】1. 详细解析IIC通信协议

 

4.4 时钟同步和仲裁

        如果两个master都想在同一条空闲总线上传输,此时必须能够使用某种机制来选择将总线控制权交给哪个master,这是通过时钟同步和仲裁来完成的,而被迫让出控制权的master则需要等待总线空闲后再继续传输。在单一master的系统上无需实现时钟同步和仲裁。

4.5时钟同步

        时钟同步是通过I2C接口和SCL之间的线“与”(wired-AND)来完成的,即如果有多个master同时产生时钟,那么只有所有master都发送高电平时,SCL上才表现为高电平,否则SCL都表现为低电平。

4.6总线仲裁

        总线仲裁和时钟同步类似,当所有master在SDA上都写1时,SDA的数据才是1,只要有一个master写0,那此时SDA上的数据就是0。一个master每发送一个bit数据,在SCL处于高电平时,就检查看SDA的电平是否和发送的数据一致,如果不一致,这个master便知道自己输掉仲裁,然后停止向SDA写数据。也就是说,如果master一直检查到总线上数据和自己发送的数据一致,则继续传输,这样在仲裁过程中就保证了赢得仲裁的master不会丢失数据。

        输掉仲裁的master在检测到自己输了之后也不再产生时钟脉冲,并且要在总线空闲时才能重新传输。

        仲裁的过程可能要经过多个bit的发送和检查,实际上两个master如果发送的时序和数据完全一样,则两个master都能正常完成整个的数据传输。

维基百科参考代码:


 
 
 
  1. // Hardware-specific support functions that MUST be customized:
  2. #define I2CSPEED 100
  3. void I2C_delay( void);
  4. bool read_SCL( void); // Return current level of SCL line, 0 or 1
  5. bool read_SDA( void); // Return current level of SDA line, 0 or 1
  6. void set_SCL( void); // Do not drive SCL (set pin high-impedance)
  7. void clear_SCL( void); // Actively drive SCL signal low
  8. void set_SDA( void); // Do not drive SDA (set pin high-impedance)
  9. void clear_SDA( void); // Actively drive SDA signal low
  10. void arbitration_lost( void);
  11. bool started = false; // global data
  12. void i2c_start_cond( void) {
  13. if (started) {
  14. // if started, do a restart condition
  15. // set SDA to 1
  16. set_SDA();
  17. I2C_delay();
  18. set_SCL();
  19. while (read_SCL() == 0) { // Clock stretching
  20. // You should add timeout to this loop
  21. }
  22. // Repeated start setup time, minimum 4.7us
  23. I2C_delay();
  24. }
  25. if (read_SDA() == 0) {
  26. arbitration_lost();
  27. }
  28. // SCL is high, set SDA from 1 to 0.
  29. clear_SDA();
  30. I2C_delay();
  31. clear_SCL();
  32. started = true;
  33. }
  34. void i2c_stop_cond( void) {
  35. // set SDA to 0
  36. clear_SDA();
  37. I2C_delay();
  38. set_SCL();
  39. // Clock stretching
  40. while (read_SCL() == 0) {
  41. // add timeout to this loop.
  42. }
  43. // Stop bit setup time, minimum 4us
  44. I2C_delay();
  45. // SCL is high, set SDA from 0 to 1
  46. set_SDA();
  47. I2C_delay();
  48. if (read_SDA() == 0) {
  49. arbitration_lost();
  50. }
  51. started = false;
  52. }
  53. // Write a bit to I2C bus
  54. void i2c_write_bit( bool bit) {
  55. if (bit) {
  56. set_SDA();
  57. } else {
  58. clear_SDA();
  59. }
  60. // SDA change propagation delay
  61. I2C_delay();
  62. // Set SCL high to indicate a new valid SDA value is available
  63. set_SCL();
  64. // Wait for SDA value to be read by slave, minimum of 4us for standard mode
  65. I2C_delay();
  66. while (read_SCL() == 0) { // Clock stretching
  67. // You should add timeout to this loop
  68. }
  69. // SCL is high, now data is valid
  70. // If SDA is high, check that nobody else is driving SDA
  71. if (bit && (read_SDA() == 0)) {
  72. arbitration_lost();
  73. }
  74. // Clear the SCL to low in preparation for next change
  75. clear_SCL();
  76. }
  77. // Read a bit from I2C bus
  78. bool i2c_read_bit( void) {
  79. bool bit;
  80. // Let the slave drive data
  81. set_SDA();
  82. // Wait for SDA value to be written by slave, minimum of 4us for standard mode
  83. I2C_delay();
  84. // Set SCL high to indicate a new valid SDA value is available
  85. set_SCL();
  86. while (read_SCL() == 0) { // Clock stretching
  87. // You should add timeout to this loop
  88. }
  89. // Wait for SDA value to be written by slave, minimum of 4us for standard mode
  90. I2C_delay();
  91. // SCL is high, read out bit
  92. bit = read_SDA();
  93. // Set SCL low in preparation for next operation
  94. clear_SCL();
  95. return bit;
  96. }
  97. // Write a byte to I2C bus. Return 0 if ack by the slave.
  98. bool i2c_write_byte( bool send_start,
  99. bool send_stop,
  100. unsigned char byte) {
  101. unsigned bit;
  102. bool nack;
  103. if (send_start) {
  104. i2c_start_cond();
  105. }
  106. for (bit = 0; bit < 8; ++bit) {
  107. i2c_write_bit((byte & 0x80) != 0);
  108. byte <<= 1;
  109. }
  110. nack = i2c_read_bit();
  111. if (send_stop) {
  112. i2c_stop_cond();
  113. }
  114. return nack;
  115. }
  116. // Read a byte from I2C bus
  117. unsigned char i2c_read_byte( bool nack, bool send_stop) {
  118. unsigned char byte = 0;
  119. unsigned char bit;
  120. for (bit = 0; bit < 8; ++bit) {
  121. byte = (byte << 1) | i2c_read_bit();
  122. }
  123. i2c_write_bit(nack);
  124. if (send_stop) {
  125. i2c_stop_cond();
  126. }
  127. return byte;
  128. }
  129. void I2C_delay( void) {
  130. volatile int v;
  131. int i;
  132. for (i = 0; i < I2CSPEED / 2; ++i) {
  133. v;
  134. }
  135. }

【硬件通信协议】2. SPI通信协议

 

参考链接:

[1]: http://www.nxp.com/docs/en/user-guide/UM10204.pdf I2C总线规格书和用户手册Rev.6 
[2]: https://learn.sparkfun.com/tutorials/i2c SparkFun I2C手册

[3]: https://blog.csdn.net/lingfeng5/article/details/73361833

[4]: https://en.wikipedia.org/wiki/I%C2%B2C

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139511.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • SQL Server2008安装详细教程[通俗易懂]

    SQL Server2008安装详细教程[通俗易懂]1.将光盘文件解压成文件夹格式,(解压过程比较慢,请耐心等待);2.打开开始菜单的设置;3.打开设置后,点击更新和安全,然后进入;4.在Windows安全中心,将其关闭(注意我这里已经关闭了);5.然后再到安装包文件夹目录,找到setup.exe文件,右击,以管理员身份运行;6.右击运行后,会出来这个页面(如果没有出现这个页面,请直接跳转至第14步),然后点击下载并安装此功能,进入下一步;7.进入下一个页面后,你会发现它会出来一个正在下载所需的文件的页面,然后等待就行;8

  • 学习笔记 – EasyUI官方网站演示[通俗易懂]

    学习笔记 – EasyUI官方网站演示[通俗易懂]EasyUI官方网站演示撰写:2016/03/21更新:2016/04/07博客地址:http://www.cnblogs.com/gibbonnet/p/5362801.html演示地址:h

  • 栈上分配存储器的方法 alloca 抽样

    栈上分配存储器的方法 alloca 抽样

  • 西门子PLC_300F系列PLC_初始化MMC卡实验教程 S_L01[通俗易懂]

    西门子PLC_300F系列PLC_初始化MMC卡实验教程 S_L01[通俗易懂]西门子300F安全PLC忘记安全密码没有读卡器如何清空MMC卡西门子300FPLC安全密码操作前注意事项本次实验使用的硬件设备将新硬件进行组态和IP分配使用此硬件配合MMC进行操作西门子300FPLC安全密码300系列PLC在下载程序前必须设定一个安全密码,此密码会写在MMC卡里面,而且无法通过PLC拨码初始化,如果忘记密码可以通过使用西门子官方读卡器进行格式化,但是绝对不能插在普通读卡器或者带有读卡器的电脑上,这样操作会导致内存卡误格式化,损坏MMC卡。本实验将讲解如何在没有西门子官方读卡器的情

  • 微信火柴人html5小游戏,20个好玩的微信小游戏推荐!你玩过几个?「建议收藏」

    50000+游戏爱好者已加入我们!每天推荐好玩游戏!加入我们,沐沐带你发现好游戏!只有你想不到,没有我找不到的好游戏!「良心好游戏推荐」搜罗了好玩的微信小游戏大全,模拟经营游戏、恐怖游戏、消除游戏、休闲游戏、益智游戏、吃鸡游戏、烧脑游戏、解谜游戏大全、换装游戏、射击游戏、吃鸡小游戏、像素游戏一个都不少!还有游戏攻略哦!每天都会推荐好玩的小游戏。————————————————————————不知不…

  • 迭代与递归的区别「建议收藏」

    迭代与递归的区别「建议收藏」迭代和递归的区别:从“编程之美”的角度看,可以借用一句非常经典的话:“迭代是人,递归是神!”来从宏观上对二者进行把握。递归:重复调用函数自身实现循环称为递归;    递归实际上不断地深层调用函数,直到函数有返回才会逐层的返回,递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,因此,递归涉及到运行时的堆栈开销(参数必须压入堆栈保存,直到该层函数调用返回为止),所以有可能导致堆…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号