I2C总线的使用 --- EEPROM 存储器使用

I2C电气特性

I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。

如下图所示:
I2C总线的使用 --- EEPROM 存储器使用_第1张图片

I2C总线只有两根双向信号线
SDA : Serial Data Line : 数据线
SCL : Serial Clock : 时钟线

总线寻址

I2C总线协议规定:从设备采用7位的地址。D7~D1:从设备地址。D0位:数据传送方向位,为“0”时表示主设备向从设备写数据,为“1”时表示主机由从设备读数据。主设备发送地址时,总线上的每个从设备都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主设备寻址,根据R/W位将自己确定为发送器或接收器。

如下图所示:
I2C总线的使用 --- EEPROM 存储器使用_第2张图片

从设备的地址由固定部分用户自定义部分组成:
1、 固定部分:D7-D4 共4位决定的。这是由从设备的生产厂商生产时就已确定的值。
2、 用户自定义部分:D3-D1 共3位。这3位通常对应设备的3个引脚(A0~A2)。把3个引脚接到不同的电平上,就可以形成一个3位的数值。

举例说明:EEPROM芯片AT24C08
I2C总线的使用 --- EEPROM 存储器使用_第3张图片

I2C总线时序

空闲状态:
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

起始状态:
在时钟线SCL保持高电平期间,数据线SDA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。

数据位传送:
I2C总线上的所有数据(地址和数据)都是以8位一个字节为单位传送的。

应答位:
发送器每发送一个字节,就在时钟脉冲第9位释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,定为有效应答位ACK,表示接收器已经成功地接收了该字节;应答信号为高电平时,定为非应答位(NACK),表示接收器没有成功接收该字节。在第9位,SCL线为高电平(ACK),应答信号在SDA线上。

结束状态:
在时钟线SCL保持高电平时,数据线SDA被释放,使得SDA返回高电平(即正跳变),称为I2C总线的停止信号。

时序如下图所示:
I2C总线的使用 --- EEPROM 存储器使用_第4张图片

EEPROM简介 — (在I2C中作为从机)

EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器,是一种类似于flash的固态存储器,但是与flash相比又存在一些区别:
1、EEPROM 可以按位擦写,而FLASH只能大片擦除。
2、EEPROM 一般容量都不大,一般都在64Kbit以下。

地址查找:
可以通过 开发板的原理图 和 EEPROM的芯片手册 查找到EEPROM的地址:

开发板原理图,以TQ2440为例:
I2C总线的使用 --- EEPROM 存储器使用_第5张图片

EEPROM芯片以AT24C02A为例,在芯片手册中有:
I2C总线的使用 --- EEPROM 存储器使用_第6张图片

红框中就是该芯片的地址。前四位是固定部分,后四位是用户通过电路自定义的部分。

I2C裸机代码编写 — EEPROM的使用

以TQ2440为例,初始化:
I2C总线的使用 --- EEPROM 存储器使用_第7张图片

时序图:
I2C总线的使用 --- EEPROM 存储器使用_第8张图片

发送数据:
I2C总线的使用 --- EEPROM 存储器使用_第9张图片
在上图中,在写入数据之前还需要加上:写入字节的地址。

读取数据:
I2C总线的使用 --- EEPROM 存储器使用_第10张图片
具体的流程应该以参考代码中为准,以下图所示:

I2C总线的使用 --- EEPROM 存储器使用_第11张图片

参考代码:

//I2C总线和EEPROM的使用
#define INTPND (*(volatile unsigned long*)0x4a000010)
#define SRCPND (*(volatile unsigned long*)0x4a000000)
#define INTMSK (*(volatile unsigned long*)0x4a000008)
#define GPECON (*(volatile unsigned long*)0x56000040)
#define GPEUP  (*(volatile unsigned long*)0x56000048)

#define IICCON   (*(volatile unsigned char*)0x54000000)
#define IICSTAT  (*(volatile unsigned char*)0x54000004)
#define IICDS    (*(volatile unsigned char*)0x5400000C)

#define SLAVE_WRITE_ADD 0xa0
#define SLAVE_READ_ADD  0xa1

void delay(int i)
{
    int j = 0;
    while (i--) 
        for(j=0;j<100;j++);
}

void i2c_init()
{
    //1.a 初始化中断
    INTPND |= (1<<27);
    SRCPND |= (1<<27);  
    INTMSK &= ~(1<<27);

    IICCON |= (1<<5); 

    //1.b 设置scl时钟
    IICCON &= ~(1<<6);
    IICCON &= ~(0xf<<0);
    IICCON |= (0x5<<0);

    //2. 设置IICSTAT  
    IICCON |= (1<<4);

    //3.设置引脚功能
    GPECON |= (0x2<<28)|(0x2<<30);
    GPEUP |= (0x3<<14);

    //4.允许产生ACK
    IICCON |= (1<<7);
}

void write_byte(unsigned char xchar, unsigned char daddr)
{   
    //1.设置处理器为主设备+发送模式
    IICSTAT |= (3<<6);

    //2.将从设备的地址写入到IICDS寄存器
    IICDS = SLAVE_WRITE_ADD;
    IICCON &= ~(1<<4);

    //3.写入0xF0写入IICSTAT
    IICSTAT = 0xF0;

    //4.等待ACK的产生
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //5.1写入字节的地址到IICDS寄存器
    IICDS = daddr;
    IICCON &= ~(1<<4);

    //5.2等待ACK的产生
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //6.将要传输的字节数据写入IICDS寄存器
    IICDS = xchar;
    IICCON &= ~(1<<4);   

    //7.等待ACk的产生
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //8.写入0xD0到IICSTAT
    IICSTAT = 0xD0;

    //9.清除中断    
    IICCON &= ~(1<<4); 

    delay(100);
}

void read_data(unsigned char *buf, unsigned char daddr, int length)
{
    int j =0;
    unsigned char unusedata;

    //设置为主设备发送模式
    IICSTAT |= (3<<6);

    //写入从设备地址
    IICDS = SLAVE_WRITE_ADD;
    IICCON &= ~(1<<4);

    //写入0xF0到IICSTAT
    IICSTAT = 0xF0;

    //等待ACK
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //写入eeprom内部地址
    IICDS = daddr;
    IICCON &= ~(1<<4);

    //等待ACK
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //设置为主设备接收模式
    IICSTAT &= ~(3<<6);
    IICSTAT |= (2<<6);

    //写入从设备地址到IICDS
    IICDS = SLAVE_READ_ADD;
    IICCON &= ~(1<<4);

    //写入0xB0到IICSTAT开始接收
    IICSTAT = 0xb0;
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    //写入设备内部地址
    IICDS = daddr;
    IICCON &= ~(1 << 4);
    while((IICCON & (1 << 4)) == 0)
        delay(100);

    //丢掉收到的第1个字节
    unusedata = IICDS;
    IICCON &= ~(1<<4);
    while ((IICCON & (1<<4)) == 0 )
        delay(100);

    for(j=0;jif(j == (length -1))
            IICCON &= ~(1<<7);         

        //从IICDS里取出数据
        buf[j]=IICDS;

        //清除中断
        IICCON &= ~(1<<4);

        //等待中断
        while ((IICCON & (1<<4)) == 0 )
            delay(100);
    }

    //写入0x90到IICSTAT
    IICSTAT = 0x90;

    //清除中断
    IICCON &= ~(1<<4);
}

void i2c_test()
{
    int i=0;
    unsigned char sbuf[256]={0};
    unsigned char dbuf[256]={0};    

    i2c_init();

    for(i=0;i<256;i++)
    {
        sbuf[i] = i+1;
        dbuf[i] = 0;
    }

    printf("dbuf befor I2C read:\r\n");

    for(i =0; i<256;i++)
    {
       if(i%8==0)
           printf("\r\n");

       printf("%d\t",dbuf[i]);  
    }   

    for(i=0;i<256;i++)
        write_byte(sbuf[i],i);

    printf("i2c reading, plese wait!\n\r");

    read_data(dbuf,0,256);

    printf("dbuf after I2C read:\r\n");

    for(i =0; i<256;i++)
    {
       if(i%8==0)
           printf("\r\n");

       printf("%d\t",dbuf[i]);  
    }   
}

你可能感兴趣的:(I2C总线的使用 --- EEPROM 存储器使用)