何处不江南 发表于 2013-11-18 17:19:48

纯手打教程(IIC通信协议)

历时一个双休日。。新鲜出炉一篇教程,全原创希望对各位Arduinoer 有帮助。。同样因为是自己写的,水平有限难免有错误和遗漏的地方,希望大家斧正。。另外也可以一起讨论一些有趣的,无聊的,开心的,伤心的各种话题。。欢迎各种灌水。。 PS:看帖回帖是美德。。。

前言
      我们经常在各种拓展板的介绍中看见,支持UART,SPI,IIC通信协议等等,这些协议在Arduino里面非常简单,可能是一个简单的 Serial.print(), SPI.transfer(),Wire.write()就解决了问题。但是那么这些函数在硬件层次到底是如何实现的呢?想了解这个,首先要了解一些关于微控制器(Arduino MCU等)对于电路的控制方式。      现代控制器的输入输出口也就是数字IO口(某些带有DA功能的口除外)通常只能输出高低电平。所谓高低电平也就是0V和5V的一个电压信号(也有控制器是3.3V(如Flymaple),Arduino Uno是5V),那么只通过低和高如何能发送复杂的信息呢?

为什么需要协议?
      这个时候数学解决了这个问题,传说是起源与周易的二进制数学,就只有2个数,0和1。正好这一对兄弟分别对应低电平和高电平。于是这样就好像可以传信息了嘛,收到高电平(5V)就当作收到1,收到低电平就当作收到0,于是这个时候接收这个信号的芯片就好像在做一个电报接收员一样把一连串的010101信号对应着一个信号表翻译成具体的指令。等等,说到这,喜欢思考的读者应该已经发现,之前我所说的高电平,低电平发送数据有个致命的毛病,如果我要连续发送2个0信号怎么发?我发送2ms(实际可能要快得多)的低电平,到底是想发多少个0?这时候,各位聪明的人类很快就想出了2个办法:      1、限定每个信号的时间,比如规定高低电平持续时间为1ms,那么2ms低电平就代表2个0.   (UART基本是用的这个原理)      2、多添加一个信号线,当这个信号线上的电平发生变化的时候读入之前信号线上的信号(后来这个新加的信号线就叫时钟线,大部分协议都是用的这种方式)      这两种方式各有利弊,第一种只需要1根线,同样由于只有一根线,那么为了减少误传输只能降低传输速度,第二个需要2根线,但可以以很快的速度进行数据传输。也可以再加一组线组成全双工模式(发送数据的同时还可以接收数据)。

什么是IIC协议?
      了解了以上这些,我们就可以回到正题,IIC是什么呢,IIC是以第二种方式运行的一套协议。他由3根线组成分别叫SDA,SCL,GND,SDA为数据线,SCL为时钟线,GND为参考电平,就是0电平,这个线的主要功能是提供一个参考的电压,告诉IC到底多少为0V电势(通常我们说的多少V是说的电势差及电压)。      下面我要说的东西就比较复杂了,对于第一次接触通信协议的童鞋可能有点难以理解,我会尽量写的简单清晰一点。      先说说为什么要用协议,有人可能会想,按照之前所说,当时钟线上电平发生改变(我们形象的称之为上升沿和下降沿)时读入数据线的0或1不就好了,为什么还要什么协议呢?      说说我个人理解,之所以有协议是为了更加稳定也更加的可靠。举个例子,家里敲门,大家可能都有这样的经历,有时候听到敲门然后出门,发现门外并没有人,这就有2个可能,一个是外部原因:外面有什么人或者物不小心敲到了门。或者是内因:自己不小心听错了。无论是什么原因,都造成了我们信息的错误获取(认为有人敲门),所以我们想到一个方法,以后每次敲门都先敲1下,然后连续敲3下,以此表示我要进来这个信号,这样里面的人听到有人敲门后再听到3次短促的敲门才去开门,这样外因或者内因产生的错误都将大大减少,我认为这是发明协议的最初原因。      好了言归正传,现在我们可以说说什么叫IIC协议:      这种总线类型是由飞利浦半导体公司在八十年代初设计出来的,主要是用来连接整体电路,IIC是一种多向控制总线,也就是说多个芯片可以连接在一起,同时每个芯片都可以对其他的芯片进行数据传输。这种方式简化了信号传输总线。但是这种方式也增加了一个传送数据的步骤,就是需要对信号线上的每个芯片定义一个逻辑地址,让芯片知道这个信息是发给他的(类似电话号码的功能),这个之后再说。      IIC协议包含START(类似于第一下敲门)、ACK(里面的人给予回应)、NACK(另一种回答)、STOP(表示数据发送结束) 这几个东西,当然还少不了普通的数据发送。尽管协议中规定START必须,其他几个非必须,但实际上其他三个仍旧非常重要。

IIC协议的具体形式
      IIC发送数据是8位数据(即8个0或1)为一个小单元一起发送。整个协议发送的顺序如下:      通常我们为了方便把设备分为主设备和从设备,我的划分方法是谁控制时钟线(即控制SCL的电平高低变换),谁就是主设备。。。。

[*]       主发从收:主机发送START 信号-> 主机发送地址 -> 从机发送ACK应答 -> (主机发送数据 -> 从机发送ACK应答 (循环)) -> 主机发送STOP信号 或 主机发送 START 信号启动下一次传输
[*]       主收从发:主机发送 START 信号-> 从机发送发地址 -> 主机发送ACK应答 -> (从机发送数据 -> 主机发送ACK应答 (循环)) -> 接受至最后一个字节时,主机发送NACK -> 主机发送 STOP 信号或 主机发送START信号 启动下一次传输
      还是回到大家熟悉的Arduino编程,看下面一段代码:      Wire.beginTransmission(0x04);      Wire.write(0xaa);                     Wire.endTransmission();         0x代表的是16进制数转换为2进制为0x04=0100b 0xaa=10101010b      以上是一段主发从收的代码。      Wire.beginTransmission(0x04);执行了3步主机发送 START信号 -> 主机发送地址-> 从机发送 ACK应答后面的0x04就是从机的地址      Wire.write(0xaa); 执行了2步主机发数据 -> 从机 发送ACK应答 (循环) 参数0xaa就是发送的数据      Wire.endTransmission();   执行了最后一步 主发送STOP信号

IIC协议的底层原理
1、START&STOP
      这就是Arduino上对于IIC协议的软件之所以这么写的原因。说到这里对于只玩Arduino的童鞋应该是完全够了,但是作为聪明的人类,我们远远不能满足于只了解这个封装之后的函数,我们要再往底层走一步,这每个数据的发送,START ACK NACK STOP到底是怎么以电平变化的方式发送的呢?请看下面这个图。
      这个图在工程上我们叫他时序图,第一次看的童鞋可能有点晕,很简单。这个图讲的正是START,和STOP的发送方式。      看一个虚线的地方我们很容易的看到所谓START其实就是在SCL,SDA都为高的时候把SDA变为低电平(拉低)嘛,简直和敲门一样简单嘛,呵呵。       同样第二个虚线的地方,STOP就是在SDA为低,SCL为高的时候把SDA变为高电平(拉高),呵呵呵。
2、DATA INPUT
      下面我们可以来看看IIC协议是如何传输数据的,如下图
      在IIC协议中数据传输也很简单,IIC在SCL(时钟线)为高电平时对SDA进行读取(digitalRead,即采样)读到高电平即收到一个1,读到低即收到一个0。现在只剩下应答信号ACK NACK,了解了这2个就算是完全了解了IIC协议。那么什么叫应答呢?
3、ACK&NACK
   所谓应答就是告诉发送数据的一方,我收到了数据。所以说到底,应答也是一种数据,说白了就是0或者1,只不过换了一个发送方。具体地说就是就是A对B发送了8位数据之后,这时B会给A返回一个1位的数据,非0则1,当返回0在IIC协议里称为ACK,返回1称为NACK,但是请注意这里与发送数据有一点不同的地方,即发送的时候SCL,SDA的信号都是有发送方(主机)控制的,但是在ACK的时候SCL是主机控制的,SDA接收方(从机)控制的,所以反应在程序上,你所做的是等!当你控制MCU拉低SCL之后,需要不断读取SDA的值,直到其被从机拉低,才可以认为从机发送了ACK信号。      至此关于IIC的介绍已经全部完成了,下面给大家一个图,让大家对于IIC能有一个更加直观的认识:          至此IIC教程结束,有什么问题,该贴为本人原创希望大家能支持呀,看帖回帖是美德。。。

何处不江南 发表于 2014-3-4 10:18:34

Youyou 发表于 2014-3-3 20:47
网络上发现,很多用时序模拟的IIC协议代码,可靠性有些问题,就是出错机制怎么保障?数据的完整性? ...

靠协议吧 如果不完整就抛弃。。校验和什么的

SUN.HG 发表于 2021-6-27 11:30:29

"当你控制MCU拉低SCL之后,需要不断读取SDA的值,直到其被从机拉低,才可以认为从机发送了ACK信号..........."
作者有误,应为 “当你控制MCU拉高SCL之后......."

何处不江南 发表于 2014-3-3 10:10:49

remotefish 发表于 2014-2-28 22:53
说得通俗易懂,除了最后NACK那里还有点不明白之外。多谢楼主,帮我省了时间。 ...

呵呵很高兴啊 说明没白写

Phoebe 发表于 2013-11-18 17:29:37

看帖回帖是美德{:3_57:}

Ash 发表于 2013-11-18 17:34:32

看帖回帖是美德 {:3_57:}
看完手打教程有点吃手打牛肉丸了..

lisper 发表于 2013-11-18 18:06:46

强大,看不懂

Holiday 发表于 2013-11-18 18:08:36

美德在此处酣畅淋漓地体现出来{:3_62:}

何处不江南 发表于 2013-11-18 18:36:22

lisper 发表于 2013-11-18 18:06
强大,看不懂

擦 哪里不懂。。

Eric 发表于 2013-12-17 11:15:09

看帖回帖好习惯!!!;P

remotefish 发表于 2014-2-28 22:53:44

说得通俗易懂,除了最后NACK那里还有点不明白之外。多谢楼主,帮我省了时间。

ambersea 发表于 2014-3-3 10:41:44

最近一直在欢快的学习各种总线~所以很喜闻乐见楼主造福人类

Angelo 发表于 2014-3-3 16:55:39

在51里面手打过IIC协议的代码,各种艰难~~

用了Arduino之后才知道,原来IIC也能这么好用~

ada 发表于 2014-3-3 17:14:14

看帖回帖的都是好孩纸{:2_39:}

Youyou 发表于 2014-3-3 20:47:52

网络上发现,很多用时序模拟的IIC协议代码,可靠性有些问题,就是出错机制怎么保障?数据的完整性?:dizzy:

Youyou 发表于 2014-3-3 20:49:41

何处不江南 发表于 2013-11-18 18:36
擦 哪里不懂。。

顺便问一句,IIC的从机代码怎么搞?

amy.liao 发表于 2014-3-11 14:37:34

大概明白了,实际上就是一种接收信号的规则~~期待lz下次更新~~~~让我们小白多学点电子知识

qq小黑1 发表于 2015-8-17 14:41:05

好贴,必须顶哈!!:loveliness:

visionsl 发表于 2015-9-20 22:17:50

明白了理论,谢谢楼主!楼主V5

avanyway 发表于 2015-10-3 13:50:14

网上 缺的就是 这种 认真仔细 又无私的 开源精神 赞一个等我学会了我也会这样做的 闻道有先后解惑 亦不辞

marcusme 发表于 2015-11-13 20:30:07

哈,好贴,mark一下。

jczaza 发表于 2015-11-27 17:38:00

赏你一个赞{:5_148:}
页: [1] 2 3
查看完整版本: 纯手打教程(IIC通信协议)