借鉴于:蓝桥杯嵌入式快速通关篇,IIC通讯及EEPROM_穿上我的格子衫的博客-CSDN博客
IIC原理超详细讲解---值得一看_Z小旋的博客-CSDN博客_iic
总线空闲:SDA = 1,SCL = 1;
启动信号:SCL = 1,SDA 1 -> 0;
停止信号:SCL = 1,SDA 0 -> 1;
数据传输:SCL为1时,SDA必须保持稳定,即为0或1(停止时相反,所以在停止前要改变SDA都要先把SCL置为0);
SCL为0时,才允许改变SDA状态;
SCL在上升沿时写入数据,在下降沿时读出数据;
应答信号:为0时,是有效应答,为1时,是无效应答;
传输到最后一个字节后,要发送一个无效应答,再发送停止信号;
不管什么信号,其线的变化都要严格与对应时序吻合(包括起始,变化过程,结果)
总线封锁:将SCL锁定为0即可;
多次数据通信,采用复启动信号,即写一个停止信号,再接上一个启动信号;
单片机为主机,24C02为从机;
SCL时钟由主机控制;
初始化时仅需要设置端口为最基本的推挽模式(因为仅需要gpio输出最基本的高低电平)
写时应答信号从机给出;
读时由主机给出;
24C02器件地址:前四位默认为1010,后四位的前三位为A0,A1,A2,均已接地,即为0,
最后一位为读写保护位,写时为0(0xA0),读时为1(0xA1).
Start: IIC开始信号,表示开始传输。
DEVICE_ADDRESS:: 从设备地址,就是7位从机地址
R/W: W(write)为写,R(read)为读
ACK: 应答信号
WORD_ADDRESS : 从机中对应的寄存器地址 比方说访问 OLED中的 某个寄存器
DATA: 发送的数据
STOP: 停止信号。结束IIC
- 主机首先产生START信号
- 然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方 向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)
- 主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
- 这时候主机等待从机的应答信号(A)
- 当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号
- 当主机收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号,
- 主机产生停止信号,结束传送过程。
具体表现为:
void i2c_write(u8 add ,u8 reg ,u8 data)
{ I2CStart();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(reg);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
}
函数void i2c_write(u8 add ,u8 reg ,u8 data);
是iic通讯的写函数,第一个 add是设备的地址,如0xa0是EEPROM的地址,0x38 是扩展板上的三轴加速度计的通讯地址。
第二个形参是寄存器的地址,需要注意的是AT24C02的地址是从0x00~0xff,如果超出该地址值会出现错误。地址和写入或读取的数据的类型都是无符号字符型,其他类型的数据有可能出现错误。就是说,但个地址存入读出的值不会超过255,即存取的的数据范围是0 ~255
主机要从从机读数据时
- 主机首先产生START信号
- 然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令,
- 这时候主机等待从机的应答信号(ACK)
- 当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,
- 当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设 置成接收模式开始读取数据,
- 这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据,当接收完成后,主机发送非应答信号,表示不在接收数据
- 主机进而产生停止信号,结束传送过程。
u8 i2c_read(u8 add ,u8 reg){
u8 data ;
I2CStart();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(reg);
I2CWaitAck();
I2CStart();
I2CSendByte(add+1);
I2CWaitAck();
data =I2CReceiveByte();
I2CWaitAck();
I2CStop();
return data;
}
以AT24C02为例子(与连接芯片的差别为寄存器地址改为了具体储存地址)
24C02是一个2K Bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。
A0,A1,A2:硬件地址引脚
WP:写保护引脚,接高电平只读,接地允许读和写
SCL和SDA:IIC总线
可
以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:
芯片的寻址:
AT24C设备地址为如下,前四位固定为1010,A2~A0为由管脚电平。AT24CXX EEPROM Board模块中默认为接地。A2~A0为000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。
也就是说如果是
写24C02的时候,从器件地址为10100000(0xA0);
读24C02的时候,从器件地址为10100001(0xA1)。
片内地址寻址:
芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。
对应的修改 A2A1A0 三位数据即可
向AT24C02中写数据
操作时序:
- MCU先发送一个开始信号(START)启动总线
- 接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
- 等待应答信号(ACK)
- 发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储>在哪个位置,此刻写的就是哪个地址。
- 发送要存储的数据第一字节、第二字节、…注意在写数据的过程中,E2PROM每个字节都会>回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。
- 发送结束信号(STOP)停止总线
注意:
在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。
写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以
从AT24C02中读数据
读当前地址的数据
2、读随机地址的数据
- MCU先发送一个开始信号(START)启动总线
- 接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)
注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。- 发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。
- 重新发送开始信号(START)
- 发送设备读操作地址(DEVICE ADDRESS)对E2PROM进行读操作 (0xA1)
- E2PROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据
- 如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。发送结束信号(STOP)停止总线
应答信号(ack)
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,
应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答
- 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
- 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
**每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。
应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,
//主机产生应答信号ACK
//1.先拉低SCL,再拉低SDA
//2.拉高SCL
//3.拉低SCL## 标题
void I2C_Ack(void)
{
IIC_SCL=0; //先拉低SCL,使得SDA数据可以发生改变
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
//主机不产生应答信号NACK
//1.先拉低SCL,再拉高SDA
//2.拉高SCL
//3.拉低SCL
void I2C_NAck(void)
{
IIC_SCL=0; //先拉低SCL,使得SDA数据可以发生改变
IIC_SDA=1; //拉高SDA,不产生应答信号
delay_us(2);
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
}
3、连续读数据
E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。不应答发送结束信号后终止传输。
软件IIC和硬件IIC
IIC分为软件IIC和硬件IIC
软件IIC:软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。
硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。
硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。
代码需要注意的点(出现问题也可依次检查)
读取的操作结束后,都建议延迟几毫秒,实测如果不延迟数据极有可能会出错,延迟操作可以加到函数末尾,也可在函数调用后。
调用函数之前,要先使用i2c.c文件中已有的函数i2c_init()来初始化iic要使用到的时钟,引脚等。
读写函数并不能直接存取超过范围的数,如果存入的数不在0到255之间,肯定会出错,地址也一样,要注意范围。
在读取iic的值时,需要再进行一次I2CStart(),不然读取的值也可能会出错!