MCU 51-7 I2C Communication EEPROM

Posted darren-pty

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MCU 51-7 I2C Communication EEPROM相关的知识,希望对你有一定的参考价值。

目前常用的微机与外设之间进行数据传输的串行总线主要有I2C总线、SPI总线和SCI总线。

其中I2C总线以同步串行2线方式进行通信(一条时钟线,一条数据线)。

SPI总线则以同步串行3线方式进行通信(一条时钟线,一条数据输入线,一条数据输出线)。 SCI总线是以异步方式进行通信(一条数据输入线,一条数据输出线)。

1-wire,即单线总线,又叫单总线。

 

IIC Communication

I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/O),另一根是时钟线SCL(serial clock)。

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。

技术图片

 

 

I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。

技术图片

 

 

开发板上的 I2C总线以及总线上的设备AT24C02

技术图片

 

 

I2C总线传输协议

数据位的有效性规定

SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。

I2C的起始和终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。

技术图片

 

 

I2C字节的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

技术图片

 

 

I2C字节的传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

应答位的作用

主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据。 当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

I2C写数据流程

技术图片

 

 

在起始信号后必须传送一个从机的地址(7位)我们开发板上的AT24C02地址为0xa0,第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。

I2C读数据流程

技术图片

在读数据时也要先发送器件地址,读写方向为写,因为我们下一帧需要发送从AT24C02内那个单元开始读,之后需在发一次器件地址这个时候读写方向就为读了,接着我们就可以从总线上读取数据。

软件模拟I2C通信时序

I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :

技术图片

 

 

//I2C起始信号程序
void I2C_Start()
{
    SCL = 1;
    _nop_(); //1.08506us
    SDA = 1;
    delay_5us();
    SDA = 0;
    delay_5us();
} 

 

//I2C终止信号程序
void I2C_Stop()
{
    SDA = 0;
    _nop_();
    SCL = 1;
    delay_5us();
    SDA = 1;
    delay_5us();
}

 

//I2C主机检测从机应答

bit Test_ACK()
{
    SCL =  1;    //在时钟总线为高电平期间可以读取应答信号
    delay_5us();
    if (SDA)
    {
        SCL = 0;
        I2C_Stop();
        return(0);
    }
    else
    {
        SCL = 0;
        return(1);
    }
}

 

//I2C主机发送应答

void Master_ACK(bit i)
{
    SCL = 0;
    _nop_();
    if (i)
    {
        SDA = 0;
    }
    else
    {
        SDA = 1;
    }
    _nop_();
    SCL = 1;//数据保持稳定
    _nop_();
    SCL = 0;
    _nop_();
    SDA = 1;
    _nop_();
}

 

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

#define uchar unsigned char
#define uint unsigned  int
#define AT24C02_ADDR  0xa0    //AT24C02地址

/*I2C IO口定义*/
sbit SDA = P2^0;
sbit SCL = P2^1;

/*5us延时*/
void delay_5us()  
{
    _nop_();
}

/*1Ms延时*/
void delay(uint z)
{
    uint x,y;
    for(x = z; x > 0; x--)
        for(y = 114; y > 0 ; y--);
}

/*I2C初始化*/
void I2C_init()    
{
    SDA = 1;
    _nop_();
    SCL = 1;
    _nop_();
}

/*I2C起始信号*/
void I2C_Start()  
{
    SCL = 1;
    _nop_();
    SDA = 1;
    delay_5us();
    SDA = 0;
    delay_5us();
}

/*I2C终止信号*/
void I2C_Stop()
{
    SDA = 0;
    _nop_();
    SCL = 1;
    delay_5us();
    SDA = 1;
    delay_5us();
}

/*主机发送应答*/
void Master_ACK(bit i)        
{
    SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化
    _nop_(); // 让总线稳定
    if (i)     //如果i = 1 那么拉低数据总线 表示主机应答
    {
        SDA = 0;
    }
    else     
    {
        SDA = 1;     //发送非应答
    }
    _nop_();//让总线稳定
    SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号
    delay_5us();
    SCL = 0;//拉低时钟总线, 占用总线继续通信
    _nop_();
    SDA = 1;//释放SDA数据总线。
    _nop_();
}

/*检测从机应答*/
bit Test_ACK()
{
    SCL = 1;
    delay_5us();
    if (SDA)
    {
        SCL = 0;
        _nop_();
        I2C_Stop();
        return(0);
    }
    else
    {
        SCL = 0;
        _nop_();
        return(1);
    }
}

/*发送一个字节*/
void I2C_send_byte(uchar byte)
{
    uchar i;
    for(i = 0 ; i < 8 ; i++)
    {
        SCL = 0;
        _nop_();
        if (byte & 0x80)
        {                
            SDA = 1;    
            _nop_();                   
        }                
        else
        {
            SDA = 0;
            _nop_();
        }
        SCL = 1;
        _nop_();
        byte <<= 1;    // 0101 0100B 
    }
    SCL = 0;
    _nop_();
    SDA = 1;
    _nop_();
}


/*I2C 读一字节*/
uchar I2C_read_byte()
{
    uchar dat,i;
    SCL = 0;
    _nop_();
    SDA = 1;
    _nop_();
    for(i = 0 ; i < 8 ; i++)
    {
        SCL = 1;
        _nop_();
        if (SDA)                
        {
             dat |= 0x01; //
        }
        else
        {
            dat &=  0xfe;    //1111 1110
        }
        _nop_();
        SCL = 0 ;
        _nop_();
        if(i < 7)
        {
            dat = dat << 1;    
        }
    }
    return(dat);
}

/*I2C发送数据*/
bit I2C_TransmitData(uchar ADDR, DAT)
{
    I2C_Start();
    I2C_send_byte(AT24C02_ADDR+0);
    if (!Test_ACK())
    {
        return(0);
    }
    I2C_send_byte(ADDR); 
    if (!Test_ACK())
    {
        return(0);
    }
    I2C_send_byte(DAT);
    if (!Test_ACK())
    {
        return(0);
    }
    I2C_Stop();
    return(1);    
}

/*I2C接收数据*/
uchar I2C_ReceiveData(uchar ADDR)
{
    uchar DAT;
    I2C_Start();
    I2C_send_byte(AT24C02_ADDR+0);
    if (!Test_ACK())
    {
        return(0);
    }
    I2C_send_byte(ADDR);
    Master_ACK(0);
    I2C_Start();
    I2C_send_byte(AT24C02_ADDR+1);
    if (!Test_ACK())
    {
        return(0);
    }
    DAT = I2C_read_byte();
    Master_ACK(0);
    I2C_Stop();
    return(DAT);    
}

void main()
{
    I2C_init();//I2C初始化
    if(!I2C_TransmitData(255,0xf0));    //往AT24C02第255个单元中写入数据0XF0
    {
        P1 = 0;
    }
    delay(5);
    /**/
    P1 = I2C_ReceiveData(255);//从AT24C02第255个单元中读取数据
    while(1);     
}

 

往QX-MCS51开发板的AT24C02内任意一个单元写数据,开发板上电后首先读取此单元的原有的数据,然后赋给程序中的计数变量,计数变量再以5秒的速度+1并且写入AT24C02,当计数大于99时清零再写,用数码管显示。

以上是关于MCU 51-7 I2C Communication EEPROM的主要内容,如果未能解决你的问题,请参考以下文章

ARM Cortex-M3 MCU的I2C&DMA操作被频繁的中断打挂了

在两个 arduino 微控制器上使用 i2c 通信发送字符串

MCU如何选型?

MCU如何选型?

MCU如何选型?

RT-Thread 设备驱动I2C浅析及使用