I2C电气特性
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
如下图所示:
I2C总线只有两根双向信号线:
SDA : Serial Data Line : 数据线
SCL : Serial Clock : 时钟线
总线寻址
I2C总线协议规定:从设备采用7位的地址。D7~D1:从设备地址。D0位:数据传送方向位,为“0”时表示主设备向从设备写数据,为“1”时表示主机由从设备读数据。主设备发送地址时,总线上的每个从设备都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主设备寻址,根据R/W位将自己确定为发送器或接收器。
如下图所示:
从设备的地址由固定部分和用户自定义部分组成:
1、 固定部分:D7-D4 共4位决定的。这是由从设备的生产厂商生产时就已确定的值。
2、 用户自定义部分:D3-D1 共3位。这3位通常对应设备的3个引脚(A0~A2)。把3个引脚接到不同的电平上,就可以形成一个3位的数值。
举例说明:EEPROM芯片AT24C08
I2C总线时序
空闲状态:
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。
起始状态:
在时钟线SCL保持高电平期间,数据线SDA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。
数据位传送:
I2C总线上的所有数据(地址和数据)都是以8位一个字节为单位传送的。
应答位:
发送器每发送一个字节,就在时钟脉冲第9位释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,定为有效应答位ACK,表示接收器已经成功地接收了该字节;应答信号为高电平时,定为非应答位(NACK),表示接收器没有成功接收该字节。在第9位,SCL线为高电平(ACK),应答信号在SDA线上。
结束状态:
在时钟线SCL保持高电平时,数据线SDA被释放,使得SDA返回高电平(即正跳变),称为I2C总线的停止信号。
时序如下图所示:
EEPROM简介 — (在I2C中作为从机)
EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器,是一种类似于flash的固态存储器,但是与flash相比又存在一些区别:
1、EEPROM 可以按位擦写,而FLASH只能大片擦除。
2、EEPROM 一般容量都不大,一般都在64Kbit以下。
地址查找:
可以通过 开发板的原理图 和 EEPROM的芯片手册 查找到EEPROM的地址:
开发板原理图,以TQ2440为例:
EEPROM芯片以AT24C02A为例,在芯片手册中有:
红框中就是该芯片的地址。前四位是固定部分,后四位是用户通过电路自定义的部分。
I2C裸机代码编写 — EEPROM的使用
以TQ2440为例,初始化:
时序图:
发送数据:
在上图中,在写入数据之前还需要加上:写入字节的地址。
读取数据:
具体的流程应该以参考代码中为准,以下图所示:
参考代码:
//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;j<length;j++)
{
if(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]);
}
}