目录
前言
图解IIC的通信使用过程,使用51单片机的IO口,进行模拟IIC时序与AT24C20进行双向通信
一.I2C
1.1 IIC介绍
I2C(Inter-Integrated Circuit)是一种广泛使用的两线式串行总线协议,主要用于连接低速外设,如EEPROM、传感器、显示器等。IIC也称I2C
以下是一些关于I2C通信协议的关键点:
- 信号线:I2C只需要两根信号线来完成数据的传输,这两根线分别是SCL(串行时钟线)和SDA(串行数据线)。它们都是双向的I/O线,通常需要通过上拉电阻连接至正电源VCC。
- 工作原理:在I2C总线上,可以有一个主控设备和多个从设备。主设备负责产生时钟信号和初始化数据传输,而从设备则响应主设备的请求。SDA和SCL线在总线空闲时都保持高电平状态。
- 速率和模式:I2C有几种不同的速率和模式,包括标准模式、快速模式和高速模式。标准模式下的速率可以达到100Kb/s,而快速模式下可以达到400Kb/s。
- 地址和数据格式:I2C设备拥有7位或10位的地址,用于在总线上识别不同的设备。数据在总线上以字节的形式进行传输,且每个字节后面通常会有一个应答位,以确保数据的完整性。
- 电容负载:由于每个连接到I2C总线的设备都有一定的等效电容,因此总线上的电容负载会影响传输速度。如果电容过大,可能会导致传输错误。
- 硬件实现简单:I2C协议因其引脚少、硬件实现简单以及可扩展性强而被广泛应用于系统内多个集成电路间的通讯。
总的来说,I2C是一种高效、可靠的短距离通信协议,适用于多种电子设备之间的信息交换。它简化了硬件设计,并且能够通过一条总线连接多个设备,极大地提高了系统的集成度和灵活性。
1.2 IIC物理层
I2C 通信设备常用的连接方式如下图所示:
它的物理层有如下特点:
(1)它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在 一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通 讯从机。
(2)一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA),一 条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
(3)每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址 进行不同设备之间的访问。
(4)总线通过上拉电阻接到电源。当 I2C 设备空闲时,会输出高阻态,而 当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
(5)多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定 由哪个设备占用总线。
(6)具有三种传输模式:标准模式传输速率为 100kbit/s,快速模式为 400kbit/s,高速模式下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模 式。
(7)连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。
下面我们来了解下 I2C 总线常用的一些术语:
主机:启动数据传送并产生时钟信号的设备;
从机:被主机寻址的器件;
多主机:同时有多于一个主机尝试控制总线但不破坏传输;
主模式:用 I2CNDAT 支持自动字节计数的模式; 位 I2CRM,I2CSTT,I2CSTP 控制数据的接收和发送;
从模式:发送和接收操作都是由 I2C 模块自动控制的;
仲裁:是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并 使传输不被破坏的过程;
同步:两个或多个器件同步时钟信号的过程;
发送器:发送数据到总线的器件;
接收器:从总线接收数据的器件
二.I2C传输过程
2.1定义管脚
#include "reg52.h" typedef unsigned int u16; //对系统默认数据类型进行重定义 typedef unsigned char u8; //定义EEPROM控制脚 sbit IIC_SCL=P2^1;//SCL时钟线 sbit IIC_SDA=P2^0;//SDA数据线
使用IO口进行IIC时序的模拟
2.2数据有效性规定
I2C 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保 持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态 才允许变化。如下图
每次数据传输都以字节为单位,每次传输的字节数不受限制。
2.3起始和停止信号
SCL 线为高电平期间,SDA 线由高电平向低电平的变化表示起始信号;SCL 线为高电平期间,SDA 线由低电平向高电平的变化表示终止信号。如下图:
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用 的状态;在终止信号产生后,总线就处于空闲状态。
2.4应答和非应答
每当发送器件传输完一个字节的数据后,后面必须紧跟一个校验位,这个校 验位是接收端通过控制 SDA(数据线)来实现的,以提醒发送端数据我这边已经 接收完成,数据传送可以继续进行。这个校验位其实就是数据或地址传输过程中 的响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。
应答信号(ACK)通常是从机向主机发送的信号。
非应答信号(NACK)是由主机发送的。
等待应答信号: 一旦主机发送完地址和读写位,它会释放SDA数据线,即将SDA置为高阻态,并等待从机的应答信号。
2.5总线的寻址方式
I2C 总线寻址按照从机地址位数可分为两种,一种是 7 位,另一种是 10 位。采用 7 位的寻址字节(寻址字节是起始信号后的第一个字节)的位定义如下:
I2C 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据 信号。在起始信号后必须传送一个从机的地址(7 位),第 8 位是数据的传送 方向位(R/W),用“ 0”表示主机发送(写)数据(W),“ 1”表示主机接收 数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望 继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信 号对另一从机进行寻址。
在总线的一次数据传送过程中,可以有以下几种组合方式:
a、主机向从机发送数据,数据传送方向在整个传送过程中不变
注意:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从 机向主机传送。A 表示应答,A 非表示非应答(高电平)。S 表示起始信号,P 表 示终止信号。
b、主机在第一个字节后,立即从从机读数据
c、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复 产生一次,但两次读/写方向位正好相反
三.I2C发送与接收数据
3.1 I2C发送字节
IIC发送一个字节
/******************************************************************************* * 函 数 名 : iic_write_byte * 函数功能 : IIC发送一个字节 * 输 入 : dat:发送一个字节 * 输 出 : 无 *******************************************************************************/ void iic_write_byte(u8 dat) { u8 i=0; IIC_SCL=0; for(i=0;i<8;i++) //循环8次将一个字节传出,先传高再传低位 { if((dat&0x80)>0) IIC_SDA=1; else IIC_SDA=0; dat<<=1; delay_10us(1); IIC_SCL=1; delay_10us(1); IIC_SCL=0; delay_10us(1); } }
3.2 I2C接收数据
接收一个字节
/******************************************************************************* * 函 数 名 : iic_read_byte * 函数功能 : IIC读一个字节 * 输 入 : ack=1时,发送ACK,ack=0,发送nACK * 输 出 : 应答或非应答 *******************************************************************************/ u8 iic_read_byte(u8 ack) { u8 i=0,receive=0; for(i=0;i<8;i++ ) //循环8次将一个字节读出,先读高再传低位 { IIC_SCL=0; delay_10us(1); IIC_SCL=1; receive<<=1; if(IIC_SDA)receive++; delay_10us(1); } if (!ack) iic_nack(); else iic_ack(); return receive; }
四.EEPROM 单字节读写
24C02 是一个基于 I 2C 通信协议的器件,因此从现在开始,我们的 I 2C 和我们的 EEPROM 就要合体了。但是大家要分清楚,I 2C 是一个通信协议,它拥有严密的通信时序逻辑要求, 而 EEPROM 是一个器件,只是这个器件采用了 I 2C 协议的接口与单片机相连而已,二者并没 有必然的联系,EEPROM 可以用其它接口,I 2C 也可以用在其它很多器件上。
I2C 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据 信号。在起始信号后必须传送一个从机的地址(7 位),第 8 位是数据的传送 方向位(R/W),用“ 0”表示主机发送(写)数据(W),“ 1”表示主机接收 数据(R)
4.1EEPROM 写数据
1、EEPROM 写数据流程
第一步,首先是 I 2C 的起始信号,接着跟上首字节,也就是我们前边讲的 I 2C 的器件地 址,并且在读写方向上选择“写”操作。
第二步,发送数据的存储地址。24C02 一共 256 个字节的存储空间,地址从 0x00~0xFF, 我们想把数据存储在哪个位置,此刻写的就是哪个地址。
第三步,发送要存储的数据第一个字节、第二个字节……注意在写数据的过程中, EEPROM 每个字节都会回应一个“应答位 0”,来告诉我们写 EEPROM 数据成功,如果没有 回应答位,说明写入不成功。
在写数据的过程中,每成功写入一个字节,EEPROM 存储空间的地址就会自动加 1,当 加到 0xFF 后,再写一个字节,地址会溢出又变成了 0x00。
4.2 EEPROM 读数据
2、EEPROM 读数据流程
第一步,首先是 I 2C 的起始信号,接着跟上首字节,也就是我们前边讲的 I 2C 的器件地 址,并且在读写方向上选择“写”操作。这个地方可能有同学会诧异,我们明明是读数据为 何方向也要选“写”呢?刚才说过了,24C02 一共有 256 个地址,我们选择写操作,是为了把所要读的数据的存储地址先写进去,告诉 EEPROM 我们要读取哪个地址的数据。这就如 同我们打电话,先拨总机号码(EEPROM 器件地址),而后还要继续拨分机号码(数据地址), 而拨分机号码这个动作,主机仍然是发送方,方向依然是“写”。
第二步,发送要读取的数据的地址,注意是地址而非存在 EEPROM 中的数据,通知 EEPROM 我要哪个分机的信息。 第三步,重新发送 I 2C 起始信号和器件地址,并且在方向位选择“读”操作。
这三步当中,每一个字节实际上都是在“写”,所以每一个字节 EEPROM 都会回应一个 “应答位 0”。
第四步,读取从器件发回的数据,读一个字节,如果还想继续读下一个字节,就发送一 个“应答位 ACK(0)”,如果不想读了,告诉 EEPROM,我不想要数据了,别再发数据了,那 就发送一个“非应答位 NAK(1)”。
五. EEPROM读写代码
5.1 EEPROM 写入数据
在AT24C02(EEPROM )指定地址写入一个字节
/******************************************************************************* * 函 数 名 : at24c02_write_one_byte * 函数功能 : 在AT24CXX指定地址写入一个数据 * 输 入 : addr:写入数据的目的地址 dat:要写入的数据 * 输 出 : 无 *******************************************************************************/ void at24c02_write_one_byte(u8 addr,u8 dat) { iic_start(); iic_write_byte(0XA0); //发送写命令 iic_wait_ack(); iic_write_byte(addr); //发送写地址 iic_wait_ack(); iic_write_byte(dat); //发送字节 iic_wait_ack(); iic_stop(); //产生一个停止条件 delay_ms(10); }
5.2 EEPROM 写入数据
在AT24CXX指定地址读出一个数据
/******************************************************************************* * 函 数 名 : at24c02_read_one_byte * 函数功能 : 在AT24CXX指定地址读出一个数据 * 输 入 : addr:开始读数的地址 * 输 出 : 读到的数据 *******************************************************************************/ u8 at24c02_read_one_byte(u8 addr) { u8 temp=0; iic_start(); iic_write_byte(0XA0); //发送写命令 iic_wait_ack(); iic_write_byte(addr); //发送写地址 iic_wait_ack(); iic_start(); iic_write_byte(0XA1); //进入接收模式 iic_wait_ack(); temp=iic_read_byte(0); //读取字节 iic_stop(); //产生一个停止条件 return temp; //返回读取的数据 }
六.代码案例
实验现象:下载程序后,数码管右4位显示0,按K1键将数据写入到EEPROM内保存, 按K2键读取EEPROM内保存的数据,按K3键显示数据加1,按K4键显示数据清零,最大能写入的数据是255。
使用案例,这里参考普中官方的IIC代码。使用IO口模拟IIC协议,对EEPROM进行数据的读写。大家可以参考一下。
void main() { u8 key_temp=0; u8 save_value=0; u8 save_buf[3]; while(1) { key_temp=key_scan(0); if(key_temp==KEY1_PRESS) { at24c02_write_one_byte(EEPROM_ADDRESS,save_value); } else if(key_temp==KEY2_PRESS) { save_value=at24c02_read_one_byte(EEPROM_ADDRESS); } else if(key_temp==KEY3_PRESS) { save_value++; if(save_value==255)save_value=255; } else if(key_temp==KEY4_PRESS) { save_value=0; } save_buf[0]=save_value/100; save_buf[1]=save_value%100/10; save_buf[2]=save_value%100%10; smg_display(save_buf,6); } }
以上就是本期补齐的内容,欢迎参考指正,如有不懂,欢迎评论或私信出下期!!!