0%

51单片机 —— IIC协议 & EEPROM读写

MCU:AT89S52

EEPROM:AT24C02

IIC

IIC物理层

IIC协议是PHILIPS公司在1980年代开发的串行通信协议,半双工通信,多主从结构(任意时刻只有一主机),低速传输,支持多种传输速率(标准:100Kbit/s、快速:400Kbit/s、高速:3.4Mbit/s),传输距离短,主要用于MCU与外设的通信。IIC一共由两根总线组成:用于数据传输的SDA和用于时序控制的SCL

IIC协议层

主机&从机:控制SCL电平变换的器件即为主机。

IIC协议没有固定的波特率,但有时序的要求。

IIC空闲状态

SCLSDA均保持高电平。

IIC起始信号

SCL保持高电平,SDA由高电平跳变到低电平,产生一个下降沿,表示起始信号。

IIC停止信号

SCL保持高电平,SDA由低电平跳变到高电平,产生一个上升沿,表示停止信号。

IIC响应信号

外设每接收到一字节的数据后,在第9个时钟脉冲到来前,(从机)将SDA稳定置低电平表示应答,(从机)将SDA稳定置高电平表示非应答

IIC数据传输

一字节数据由高位到低位依次传输,在数据传输过程中,主机必须保持SCL为低电平(因为当SCL为高电平时,SDA的任何电平变化都将被视为起始/停止信号)。

IIC寻址信号

主机发送一个起始信号后,接着发送一字节的寻址信号,该寻址信号的前7位表示器件地址(其中前4位固定为1010,后3位由从器件的对应的3个引脚的电平决定),第8位则为数据方向位(0表示主机写入数据,1表示主机读取数据),若从器件存在,则响应应答信号。

IIC时序

起始/停止时序

写周期时序

应答时序

EEPROM读写

开发板上的EEPROM使用IIC协议来读写数据。

数据写入

⑴发送IIC起始信号

⑵发送IIC寻址信号(从器件EEPROM的地址+操作方向)

⑶发送将要存储数据的EEPROM的存储地址

⑷发送将要写入EEPROM的数据

数据读出

⑴发送IIC起始信号

⑵发送IIC寻址信号(从器件EEPROM的地址+操作方向)

⑶发送将要读出数据的EEPROM的存储地址

⑷发送IIC起始信号

⑸发送IIC寻址信号(从器件EEPROM的地址+操作方向)

⑹主机读取从器件返回的数据

举个栗子

一个00~99的计数器,每秒计数一次,具有断电记忆功能(开机后,从关机前的最新数值继续计数),软件模拟IIC协议。

#include<reg52.h>
#include<intrins.h>

bit x=0; //将计时值写入EEPROM的标志位
sbit SCL=P2^1;
sbit SDA=P2^0;
sbit X=P2^4; //引脚P2^4为开发板上DS1302模块的复位引脚
sbit DU=P2^6; //数码管模块段选位
sbit WE=P2^7; //数码管模块位选位

unsigned char num=0;
unsigned char i=0;

unsigned char LEDS[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共阴数码管的段码

void LEDS_IO() //数码管锁存
{
WE=1;
P0=1;
WE=0;
DU=1;
P0=0;
DU=0;
}

void Delay5ms()
{
unsigned char i,j;
i=9;
j=244;
do
{
while(--j);
}
while(--i);
}

void IIC_Delay()
{
_nop_();
_nop_();
}

void IIC_INIT() //IIC初始化
{
X=0; //DS1302模块的复位电平为低电平,令DS1302模块一直处于复位状态,使其I/O端口(P2^0)不输出,避免干扰EEPROM模块的SDA端口(P2^0)
SDA=1;
IIC_Delay();
SCL=1;
IIC_Delay();
}

void IIC_Start() //IIC起始信号
{
SDA=1;
IIC_Delay();
SCL=1;
IIC_Delay();
SDA=0;
IIC_Delay();
}

void IIC_Stop() //IIC停止信号
{
SDA=0;
IIC_Delay();
SCL=1;
IIC_Delay();
SDA=1;
IIC_Delay();
}

void IIC_ACK() //等待应答信号;“应答”信号:SDA=0;“非应答”信号:SDA=1
{
unsigned char i;
SCL=1;
IIC_Delay();
while((SDA==1)&&(i<256)) //若超时,则视为“非应答”
{
i++;
}
SCL=0;
IIC_Delay();
}

void IIC_Write(unsigned char DATA) //IIC发送数据
{
unsigned char i;
unsigned char temp;
temp=DATA;
for(i=0;i<8;i++)
{
temp=temp<<1;
SCL=0;
IIC_Delay();
SDA=CY; //IIC数据的发送从高位开始,需要使用左移指令,而C语言的左移和右移指令,CY参与其中(CY并不是只有在进位和借位中用到),左移出来的一位赋给CY
IIC_Delay();
SCL=1;
IIC_Delay();
}
SCL=0; //将SCL置低电平,准备接收应答信号
IIC_Delay();
SDA=1; //释放SDA,准备接收从器件反馈的应答信号
IIC_Delay();
}

unsigned char IIC_Read() //IIC接收数据
{
unsigned char i;
unsigned char DATA;
SCL=0;
IIC_Delay();
SDA=1;
IIC_Delay();
for(i=0;i<8;i++)
{
SCL=1;
IIC_Delay();
DATA=(DATA<<1)|SDA;
SCL=0;
IIC_Delay();
}
return DATA;
}

void EEPROM_Write(unsigned char ADDRESS,unsigned char DATA) //写入EEPROM
{
IIC_Start();
IIC_Write(0xa0); //发送IIC从器件(EEPROM)地址,并选择“写”操作
IIC_ACK();
IIC_Write(ADDRESS); //发送将要写入的EEPROM的存储地址
IIC_ACK();
IIC_Write(DATA); //发送写入EEPROM的数据
IIC_ACK();
IIC_Stop();
}

unsigned char EEPROM_Read(unsigned char ADDRESS) //读取EEPROM
{
unsigned char DATA;
IIC_Start();
IIC_Write(0xa0);
IIC_ACK();
IIC_Write(ADDRESS);
IIC_ACK();
IIC_Start();
IIC_Write(0xa1); //发送IIC从器件(EEPROM)地址,并选择“读”操作
IIC_ACK();
DATA=IIC_Read();
IIC_Stop();
return DATA;
}

void LEDS_OUTPUT(unsigned char m,unsigned char n) //数码管显示
{
DU=0;
P0=LEDS[m];
DU=1;
DU=0;
WE=0;
P0=0x3e;
WE=1;
WE=0;
Delay5ms();
DU=0;
P0=LEDS[n];
DU=1;
DU=0;
WE=0;
P0=0x3d;
WE=1;
WE=0;
Delay5ms();
}

void main()
{
IIC_INIT();
LEDS_IO();
num=EEPROM_Read(0x00);
TMOD=0x01;
TH0=0x4c00;
TL0=0x4c00;
TR0=1;
ET0=1;
EA=1;
while(1)
{
LEDS_OUTPUT(num/10,num%10);
if(x==1)
{
x=0;
EEPROM_Write(0x00,num);
}
}
}

void T() interrupt 1 //每0.05秒中断一次
{
TH0=0x4c00;
TL0=0x4c00;
i++;
if(i==20) //中断20次,即1秒
{
i=0;
num++;
x=1;
if(num==100)
{
num=0;
}
}
}

参考资料:

第14章 I2C总线与EEPROM

IIC协议超详细解释(适合小白入门)

IIC详解,包括原理、过程,最后一步步教你实现IIC

IIC原理超详细讲解—值得一看