搜索
bottom↓
回复: 0

嵌入式必备知识-IIC协议原理解析

[复制链接]

出0入234汤圆

发表于 2021-11-8 10:44:52 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-1-7 17:38 编辑

以下文章来源于:公众号:开源电子网,读取更多技术文章,请扫码关注


讨论发帖图.png



嵌入式必备知识-IIC协议原理解析
IIC概述:

       IIC(Inter-IntegratedCircuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器以及其外围设备,IIC也被成为I2C,其实两者是完全相同的,只是名词不一样而已。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。


IIC特点:

      ① 数据线SDA:数据线用来传输数据;时钟线SCL:时钟线用来同步数据收发

      ② 总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以实现微控制器与器件之间的通信。

      ③ 数据线SDA和时钟线SCL都是双向线路,都通过一个电流源或上拉电阻连接到正的电压,所以当总线空闲的时候,这两条线路都是高电平。

      ④ 总线上数据的传输速率在标准模式下可达100kbit/s在快速模式下可达400kbit/s在高速模式下可达3.4Mbit/s。

      ⑤ 总线支持设备连接个数:同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容是400pF的限制决定,如以下图所示:


       1.jpg


IIC的协议层详解

       1.有效数据:在时钟的高电平周期内,SDA线上的数据必须保持稳定,数据线仅可以在时钟SCL为低电平时改变。


       2.jpg


       2.起始和结束条件:

       起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件,如以下图所示:


       3.jpg



        结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件,如下图所示:


       4.jpg



        注意:注意起始和终止信号都是由主机发出的,总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态。

       3.应答:每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。

       注意:从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答


          5.jpg


        4.数据帧格式:I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。


          6.jpg


       写操作:S为起始信号,SLAVEADDRESS从机地址(7bit)+W(0)一共8位,有阴影的部分是主机发送的,而没有阴影部分是从机发送到主机的,A/A非(0/1),P代表停止信号。


       7.jpg


       读操作:S为起始信号,SLAVEADDRESS从机地址(7bit)+R(1)一共8位,有阴影的部分是主机发送的,而没有阴影部分是从机发送到主机的,A(应答)/A非(0/1),P代表停止信号,注意:假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(nbyte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。


如何编写IIC驱动代码:


          8.jpg


      1.起始信号代码编写:

  1. /* 产生IIC起始信号 */

  2. void IIC_Start(void)

  3. {

  4.     SDA_OUT();       /* sda线输出 */

  5.     IIC_SDA=1;       /* ① */

  6.     IIC_SCL=1;  /* ② */

  7.     delay_us(5);     /* ③ */

  8.     IIC_SDA=0;   /* ④ */

  9.     delay_us(5);  /* ⑤ */

  10.     IIC_SCL=0;       /* ⑥ */

  11. }
复制代码


9.jpg


      上述起始信号函数①代表上图的①,而函数②代表上图②,其他类似。


      2.停止信号代码编写:

  1. /* 产生IIC停止信号 */

  2. void IIC_Stop(void)

  3. {

  4.     SDA_OUT();      /* sda线输出 */

  5.     IIC_SCL=0;      /* ① */

  6.     IIC_SDA=0;      /* ② */

  7.     delay_us(5);    /* ③ */

  8.     IIC_SCL=1;      /* ④ */

  9.     IIC_SDA=1;      /* ⑤ */

  10.     delay_us(5);    /* ⑥ */                              

  11. }

  12. /* 产生ACK应答 */

  13. void IIC_Ack(void)

  14. {

  15.     IIC_SCL=0;      /* ① */

  16.     SDA_OUT();      /* sda线输出 */

  17.     IIC_SDA=0;      /* ② */

  18.     delay_us(5);   

  19.     IIC_SCL=1;      /* ③ */

  20.     delay_us(5);    /* ④ */

  21.     IIC_SCL=0;      /* ⑤ */   

  22. }
复制代码


          10.jpg


        上述起始信号函数①代表上图的①,而函数②代表上图②,其他类似。



        3.主机产生应答代码编写:

  1. /* 不产生ACK应答 */            

  2. void IIC_NAck(void)

  3. {

  4.     IIC_SCL=0;  /* ① */

  5.     SDA_OUT(); /* sda线输出 */

  6.     IIC_SDA=1;  /* ② */

  7.     delay_us(2);

  8.     IIC_SCL=1;  /* ③ */

  9.     delay_us(4); /* ④ */

  10.     IIC_SCL=0;  /* ⑤ */

  11. }
复制代码


11.jpg



          上述起始信号函数①代表上图的①,而函数②代表上图②,其他类似。

         4.主机产生非应答代码编写:

  1. /* 不产生ACK应答 */            

  2. void IIC_NAck(void)

  3. {

  4.     IIC_SCL=0;  /* ① */

  5.     SDA_OUT(); /* sda线输出 */

  6.     IIC_SDA=1;  /* ② */

  7.     delay_us(2);

  8.     IIC_SCL=1;  /* ③ */

  9.     delay_us(4); /* ④ */

  10.     IIC_SCL=0;  /* ⑤ */

  11. }
复制代码


       12.jpg


      述起始信号函数①代表上图的①,而函数②代表上图②,其他类似。


      5.等待从机发送应答信号代码编写:

      思路:先让SDA=1,再判断在一定时间内SDA是否变为0,从而识别出外设有没有发送应答信号。

  1. /* 等待应答信号到来*/

  2. /* 返回值:1,接收应答失败*/

  3. /*         0,接收应答成功*/

  4. u8 IIC_Wait_Ack(void)

  5. {

  6.     u8 ucErrTime=0;

  7.     SDA_IN();      //SDA设置为输入  */

  8.     IIC_SDA=1;delay_us(1);     

  9.     IIC_SCL=1;delay_us(1);   

  10.     while(READ_SDA) /* 获取SDA电平*/

  11.     {

  12.         ucErrTime++;

  13.         if(ucErrTime>250)

  14.         {

  15.             IIC_Stop();

  16.             return 1;

  17.         }

  18.     }

  19.     IIC_SCL=0; /* 时钟输出0 */      

  20.     return 0;  

  21. }
复制代码

第一步.设置SDA为输入

第二步.拉高SDA:主要判断从机应答就是把SDA拉低。

第三步.拉高SCL:数据稳定就是有效数据。

第四步.等待接收器返回应答信号,如果数据线SDA一直为高,就一直等待ucErrTime大于250,并返回1(无效应答),如果数据线SDA为低,返回0(有效应答)。


       6.IIC发送一个字节发送一个字节的代码编写:

       思路:数据传输过程中,数据传输保持稳定(在SCL高电平期间,SDA一直保持稳定,没有跳变),只有当SCL被拉低后,SDA才能被改变在SCL为高电平期间(有效数据时间段),发送数据,发送8次数据,如果数据为1,显然SDA是被拉高;如果数据为0,那么SDA被拉低。

  1. /* IIC发送一个字节*/

  2. /* 返回从机有无应答*/

  3. /* 1,有应答*/

  4. /* 0,无应答  */         

  5. void IIC_Send_Byte(u8 txd)

  6. {                        

  7.     u8 t;   

  8.     SDA_OUT();      

  9.     IIC_SCL=0;      /* ① */

  10.     for(t=0;t<8;t++)

  11.     {              

  12.         IIC_SDA=(txd&0x80)>>7;  /* ② */

  13.         txd<<=1;         /*③ */

  14.         delay_us(2);   /* 对TEA5767这三个延时都是必须的*/

  15.         IIC_SCL=1;    /* ④ */

  16.         delay_us(2);

  17.         IIC_SCL=0;      /* ⑤ */

  18.         delay_us(2);

  19.     }   

  20. }
复制代码

    一开始为什么①把IIC_SCL=0;因为主要IIC_SCL上升沿时准备好数据就是稳定有效数据,例如我们发送txd = 0xA1为例:

     ②:0xA1&0x80为0xA0,0xA0为10100000然后右移7位得到1,如以下图所示:


       13.jpg


      重点绿色的位,就是为1。

      ③:0xA1左移1位,如以下图所示:


       14.jpg


      所以txd = 0x20,懂不?我们重点取绿色的框框。

      ④:IIC_SCL拉高就是为了SDA线上的数据必须保持稳定。

      ⑤:IIC_SCL拉低,为了上升沿时准备好数据就是稳定有效数据。

      其他从②到⑤执行,一样的道理。

      所以经过八次for循环发送数据我们SDA就发送了0xA1。


      7.IIC读1个字节的代码编写:

  1. /* 读1个字节,ack=1时,发送ACK,ack=0,发送nACK   */

  2. u8 IIC_Read_Byte(unsigned char ack)

  3. {

  4.     unsigned char i,receive=0;

  5.     SDA_IN();/*  SDA设置为输入*/

  6.     for(i=0;i<8;i++ )

  7.     {

  8.         IIC_SCL=0;

  9.         delay_us(2);

  10.         IIC_SCL=1;

  11.         receive<<=1;

  12.         if(READ_SDA)receive++;   

  13.         delay_us(1);

  14.     }                    

  15.     if (!ack)

  16.         IIC_NAck();/* 发送nACK */

  17.     else

  18.         IIC_Ack(); /* 发送ACK   */

  19.     return receive;

  20. }
复制代码

       上述也挺简单理解,首先我们先拉低SCL,延时后拉高就是为了读取有效的数据。例如我们读取从机发送的数据为0x05,换成二进制为00000101,然后for循环八次读取,例如READ_SDA为0那么就是receive<<=1为00000000,知道从机发送的00000101这个位时,READ_SDA为1,那么receive++为1,所以再一次receive<<=1为00000001,注意红色标点就是我们第一次发的那个位,最后第八次时候receive<<=1为00000101,然后主机发送应答的话就是继续读取从机的数据,如果主机发送非应答信号,那么从机发送数据结束。

       如果我们使用IIC读取AT24C0X的EEPROM,请参考对应的手册,这里我们只讲解IIC驱动如何编写以及原理分析。

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-19 22:53

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表