文章目录
这一节我们来学习如何使用
51
单片机的
IO
口模拟
I2C
时序,并实现与
AT24C02
(
EEPROM
)之间的双向通信。开发板板载了 1 个
EEPROM
模块,可实现
IIC
通信。本节要实现的功能是:系统运行时,数码管右 3 位显示 0,按
K1
键将数据写入到
EEPROM
内保存,按
K2
键读取
EEPROM
内保存的数据,按
K3
键显示数据加 1,按
K4
键显示数据清零,最大能写入的数据是 255。
一、I2C 介绍
I2C
(Inter-Integrated Circuit
)总线是由 PHILIPS
(飞利浦) 公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。I2C
总线只有两根双向信号线。一根是数据线 SDA
,另一根是时钟线 SCL
。由于其管脚少,硬件实现简单,可扩展性强等特点,因此被广泛的使用在各大集成芯片内。下面我们就从 I2C
的物理层与协议层来了解 I2C
。
1.I2C 物理层
I2C
通信设备常用的连接方式如下图所示:
它的物理层有如下特点:
- 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个
I2C
通讯总线中,可连接多个I2C
通讯设备,支持多个通讯主机及多个通讯从机。 - 一个
I2C
总线只使用两条总线线路,一条双向串行数据线(SDA
),一条串行时钟线(SCL
)。数据线即用来表示数据,时钟线用于数据收发同步。 - 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
- 总线通过上拉电阻接到电源。当
I2C
设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。 - 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
- 具有三种传输模式:标准模式传输速率为
100kbit/s
,快速模式为400kbit/s
,高速模式下可达3.4Mbit/
s,但目前大多I2C
设备尚不支持高速模式。 - 连接到相同总线的
IC
数量受到总线的最大电容400pF
限制。
下面我们来了解下 I2C
总线常用的一些术语:
- 主机:启动数据传送并产生时钟信号的设备;
- 从机:被主机寻址的器件;
- 多主机:同时有多于一个主机尝试控制总线但不破坏传输;
- 主模式:用
I2CNDAT
支持自动字节计数的模式; 位I2CRM
,I2CSTT
,I2CSTP
控制数据的接收和发送; - 从模式:发送和接收操作都是由
I2C
模块自动控制的; - 仲裁:是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并使传输不被破坏的过程;
- 同步:两个或多个器件同步时钟信号的过程;
- 发送器:发送数据到总线的器件;
- 接收器:从总线接收数据的器件。
2.I2C 协议层
I2C
的协议定义了通信的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
-
数据有效性规定
I2C
总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。如下图:
每次数据传输都以字节为单位,每次传输的字节数不受限制。 -
起始和停止信号
SCL
线为高电平期间,SDA
线由高电平向低电平的变化表示起始信号;SCL
线为高电平期间,SDA
线由低电平向高电平的变化表示终止信号。如下图:
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。 -
应答响应
每当发送器件传输完一个字节的数据后,后面必须紧跟一个校验位,这个校验位是接收端通过控制SDA
(数据线)来实现的,以提醒发送端数据我这边已经接收完成,数据传送可以继续进行。这个校验位其实就是数据或地址传输过程中的响应。响应包括“应答(ACK
)”和“非应答(NACK
)”两种信号。作为数据接收端时,当设备(无论主从机)接收到I2C
传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答(ACK
)”信号即特定的低电平脉冲,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK
)”信号即特定的高电平脉冲,发送方接收到该信号后会产生一个停止信号,结束信号传输。应答响应时序图如下:
每一个字节必须保证是 8 位长度。数据传送时,先传送最高位(MSB
),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有 9 位)。
由于某种原因从机不对主机寻址信号应答时(如从机正在进行实时性的处理工作而无法接收总线上的数据),它必须将数据线置于高电平,而由主机产生一个终止信号以结束总线的数据传送。
如果从机对主机进行了应答,但在数据传送一段时间后无法继续接收更多的数据时,从机可以通过对无法接收的第一个数据字节的“非应答”通知主机,主机则应发出终止信号以结束数据的继续传送。
当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA
线,以允许主机产生终止信号。
这些信号中,起始信号是必需的,结束信号和应答信号都可以不要。总结:
- 主机发送数据到从机:
主机产生起始信号,向从机进行数据传输,从机接收到数据后如果希望主机继续发送,就给予应答信号;主机收到应答信号就会继续发送数据;从机接收到数据后如果不希望主机继续发送,就给予非应答信号;发送非应答主机接受后就会产生终止信号。 - 主机从从机读取数据
主机产生起始信号,从机向主机进行数据传输,主机接收到数据后如果希望从机继续发送,就给予应答信号;从机收到应答信号就会继续发送数据;主机接收到数据后如果不希望从机继续发送,就给予非应答信号;发送非应答信号到从机,从机停止数据传输,主机产生终止信号,结束I2C
的通信。
- 主机发送数据到从机:
-
总线的寻址方式
I2C
总线寻址按照从机地址位数可分为两种,一种是7
位,另一种是10
位。采用7
位的寻址字节(寻址字节是起始信号后的第一个字节)的位定义如下:
D7~D1
位组成从机的地址。D0
位是数据传送方向位,为“0”
时表示主机向从机写数据,为“1”
时表示主机由从机读数据。
10 位寻址和 7 位寻址兼容,而且可以结合使用。10 位寻址不会影响已有的 7 位寻址,有 7 位和 10 位地址的器件可以连接到相同的I2C
总线。我们就以 7 位寻址为例进行介绍。
当主机发送了一个地址后,总线上的每个器件都将头 7 位与它自己的地址比较,如果一样,器件会判定它被主机寻址,其他地址不同的器件将被忽略后面的数据信号。至于是从机接收器还是从机发送器,都由R/W
位决定的。从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的 7 位寻址位有 4 位是固定位,3 位是可编程位,这时仅能寻址 8 个同样的器件,即可以有 8 个同样的器件接入到该I2C
总线系统中。 -
数据传输
I2C
总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。在起始信号后必须传送一个从机的地址(7 位),第 8 位是数据的传送方向位(R/W
),用“0”
表示主机发送(写)数据(W
),“1”
表示主机接收数据(R
)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:-
a:主机向从机发送数据,数据传送方向在整个传送过程中不变
注意:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。A
表示应答,A 非
表示非应答(高电平)。S
表示起始信号,P
表示终止信号。 -
b:主机在第一个字节后,立即从从机读数据
-
c:在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好相反
到这里我们就介绍完I2C
总线,由于51
单片机没有硬件IIC
接口,即使有硬件接口我们通常还是采用软件模拟I2C
。主要原因是硬件IIC
设计的比较复杂,而且稳定性不怎么好,程序移植比较麻烦,而用软件模拟IIC
,最大的好处就是移植方便,同一个代码兼容所有单片机,任何一个单片机只要有IO
口(不需要特定IO
),都可以很快的移植过去。
-
二、AT24C02 介绍
AT24C01/02/04/08/16...
是一个 1K/2K/4K/8K/16K
位串行 CMOS
,内部含有128/256/512/1024/2048
个 8
位字节,AT24C01
有一个 8
字节页写缓冲器,AT24C02/04/08/16
有一个 16
字节页写缓冲器。该器件通过 I2C
总线接口进行操作,它有一个专门的写保护功能。我们开发板上使用的是 AT24C02(EEPROM)
芯片,此芯片具有 I2C
通信接口,芯片内保存的数据在掉电情况下都不丢失,所以通常用于存放一些比较重要的数据等。AT24C02
芯片管脚及外观图如下图所示:
芯片管脚说明如下图所示:
AT24C02
器件地址为 7
位,高 4
位固定为 1010
,低 3
位由 A0/A1/A2
信号线的电平决定。 因为传输地址或数据是以字节为单位传送的,当传送地址时,器件地址占 7 位,还有最后一位(最低位 R/W
)用来选择读写方向,它与地址无关。其格式如下:
我们开发板已经将芯片的 A0/A1/A2
连接到 GND
,所以器件地址为1010000
。如果要对芯片进行写操作时,R/W
即为 0
,写器件地址即为 0XA0
;如果要对芯片进行读操作时,R/W
即为 1
,此时读器件地址为 0XA1
。开发板上也将 WP
引脚直接接在 GND
上,此时芯片允许数据正常读写。
I2C
总线时序如下图所示:
三、硬件设计
本实验使用到硬件资源如下:
- 独立按键(K1-K4)
- 动态数码管
EEPROM
模块电路(AT24C02
)
独立按键和动态数码管电路在前面章节都介绍过,这里就不再重复。下面我们来看下A4开发板上 EEPROM
模块电路,如下图所示:
从图中可以看出,芯片的 SCL
和 SDA
管脚是连接在单片机的 P2.1
和 P2.0
上,在介绍 IIC
总线的时候我们说过,为了让 IIC
总线默认为高电平,通常会在 IIC
总线上接上拉电阻,在图中并没有看到 SCL
和 SDA
管脚有上拉电阻,这是因为开发板单片机 IO
都外接了 10K
上拉电阻,当单片机 IO
口连接到芯片的 SCL
和 SDA
脚时即相当于它们外接上拉电阻,所以此处可以省去。
四、软件设计
1.创建多文件工程
本节带领大家创建一个全新的工程,目的是方便日后工程管理、程序移植和维护、易读等。
在电脑上创建一个实验文件夹,为了与教程配套,这里命名为“I2C-EEPROM实验”,然后在该文件夹内新建 App
、Obj
、Public
、User
四个文件夹,如下所示:
App
文件夹:用于存放外设驱动文件,如LED
、数码管、定时器等。Obj
文件夹:用于存放编译产生的 c/汇编/链接的列表清单、调试信息、hex
文件、预览信息、封装库等文件。Public
文件夹:用于存放51
单片机公共的文件,如延时、51
头文件、变量类型重定义等。User
文件夹:用于存放用户主函数文件,如main.c
。
为何要定义这么多文件夹呢?为何不按照之前实验那样直接在 main.c
文件里面写代码?我们先来分析本章实验所需用到哪些功能程序,要用到独立按键、数码管、EEPROM
,所以需要将前面实验编写的独立按键、数码管、延时等程序全部放到 main.c
中来,此时在不增加 EEPROM
驱动程序时,main.c
文件中的代码量已经非常冗长,极其不便于程序的阅读、移植和维护,因此可用多文件管理的办法来使工程易于阅读、移植和维护。操作方法如下:
1.1 新建工程
首先打开 KEILC51
软件,新建一个工程,这个在前面章节已经讲解过,这里不再重复。将工程命名为 template
并保存在“I2C-EEPROM 实验”
文件夹下,然后选择芯片类型为“AT89C52”
,不使用系统创建启动文件,如下所示:
上述操作与前面介绍的创建工程模板是一样的,下面才开始在工程中进行分组管理。
1.2 向工程添加文件
按照需要给工程分组并添加对应文件,这里我们在工程中分 3
组,User
、App
、Public
,至于前面创建的 Obj
文件夹是在工程中无需体现,因为只是编译器生成的一些中间文件和.hex
执行文件。通常在工程组的命名与创建的文件夹名保持一致,方便查找到源文件位置。如下所示:
分组后,在工程中就会出现刚才的分组列表,如下所示:
然后就是给每个组添加对应的.c
源文件,这些源文件和头文件在实验例程中已经给出,大家可直接从那复制到新建的文件夹内,复制后如下所示:
在 App
文件夹中可以看到根据不同外设分别创建了文件夹保存各自驱动文件,这样非常方便程序移植和维护。
然后就将复制过来的文件添加到刚才的工程分组中,如下所示:
可以选择到要添加的.c
文件(红色标记 5),然后鼠标左键双击也可直接添加进去,就免去了点击红色标记 6 这步。添加好后,在右侧就会显示对应组中已添加的文件,如下所示:
同样的方法,将 App
、Public
工程组中文件也添加进去。如下所示:
此时可以看到,在工程栏下每组都已经加入了刚才添加的源文件,如下所示:
如果有遗漏的文件未添加进来,可按照上述方法重新添加。接下来如果直接编译工程肯定会出现一大堆错误,因为在 KEIL
中指定那些文件夹中的头文件路径。
1.3 配置魔术棒选项卡
这一步的配置工作非常重要,很多人编写完程序编译后发现找不到 HEX
文件,还有的人直接编译前面添加好文件的工程出现报错,这些问题都是在这个地方没有配置好导致的。
Output
选项卡中把输出文件夹定位到我们实验目录下的Obj
文件夹,如果想在编译的过程中生成hex
文件,那么Create HEX File
选项勾上。配置如下:
Listing
选项卡中把输出文件夹也定位到我们实验目录下的Obj
文件夹。其它设置默认,配置如下:
C51
选项卡配置,此处目的是将我们前面添加到工程组中的文件路径包括进来,否则程序中调用其他文件夹的头文件则会报错找不到头文件路径,具体步骤如下:
添加的头文件路径是指,