EEPROM I2C操作说明 下载本文

I2C协议

2条双向串行线,一条数据线SDA,一条时钟线SCL。 SDA传输数据是大端传输,每次传输8bit,即一字节。 支持多主控(multimastering),任何时间点只能有一个主控。 总线上每个设备都有自己的一个addr,共7个bit,广播地址全0. 系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。

1.1 I2C位传输

数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;

若SDA发生跳变,则用来表示一个会话的开始或结束(后面讲) 数据改变:SCL为低电平时,SDA线才能改变传输的bit

1.2 I2C开始和结束信号

开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。 结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。

1.3 I2C应答信号

Master每发送完8bit数据后等待Slave的ACK。 即在第9个clock,若从IC发ACK,SDA会被拉低。

若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:

1.4 I2C写流程

写寄存器的标准流程为: 1. Master发起START

2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK 3. Slave发送ACK

4. Master发送reg addr(8bit),等待ACK 5. Slave发送ACK

6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK 7. Slave发送ACK

8. 第6步和第7步可以重复多次,即顺序写多个寄存器 9. Master发起STOP

写一个寄存器

写多个寄存器

1.5 I2C读流程

读寄存器的标准流程为:

1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK 2. Slave发送ACK

3. Master发送reg addr(8bit),等待ACK 4. Slave发送ACK 5. Master发起START

6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK 7. Slave发送ACK

8. Slave发送data(8bit),即寄存器里的值 9. Master发送ACK

10. 第8步和第9步可以重复多次,即顺序读多个寄存器

读一个寄存器

读多个寄存器

1.前言

对于大多数工程师而言,I2C永远是一个头疼的问题。相比UART和SPI而言,I2C的时序要复杂一些,I2C组合变化也丰富一些。在这里以AT24C04为例说明I2C使用过程中的一些注意点。

2.AT24C04操作示意图

图 AT24C04操作示意图

示意图说明:示意图分阐述了4种不同的操作方式,例如写单个存储单元,写多个存储单元,读单个存储单元和写单个存储单元。对于单个操作而言,上部为MCU通过I2C输出的相关指令,下部为I2C设备的响应。例如写单个存储单元操作时,MCU发出I2C启动,设备地址,写标志位等,而I2C设备输出多个ACK。

3.若干说明

3.1 基本操作方式

I2C设备的操作可分为写单个存储字节,写多个存储字节,读单个存储字节和读多个存储字节。相对于AT24C04而言,这些读写动作相对于内部的存储单元而言,对于其他的具备I2C接口的AD或传感器而言,存储单元变成了寄存器单元。虽然存在概念上的差别,但是其操作原理确实一样的。

3.2 无应答

在以上4种情况中,无应答为MCU发出,无应答意为MCU不需要从机输出数据,MCU将会停止本次I2C操作。需要说明的是,无应答并不是一种异常情况。

3.3 I2C设备并不只有一个设备地址

这一点往往被忽略,一般情况下认为在I2C启动信号之后的字节为I2C从机地址(7位)。对于AT24C04而言,内部具有4Kb存储位,合计512字节。若需要访问512字节内容,总共需要9根地址线(8位宽度),那么上图中的存储地址(8位长度)显然还差了一位,那么就需要从设备地址中“借”1位,这就使得AT24C04具有两个I2C地址,例如0x50和0x51。

3.4 存储地址

相对于AT24C04而言,存储地址占1个字节。若换成其他I2C设备,例如ADXL345,存储地址被寄存器地址替代即可,其他操作方式相似。但是像AT24C32或AT24C64这样的大容量EEPROM,则存储地址需要2字节描述,也就意味着需要连续发送两个字节地址信息且高字节在前。其他像BH1750这样的光照芯片,存储地址被具体的操作命令替代,使用I2C设备时需要因地制宜,切不可照搬教条。

3.5 连续读和连续写限制

AT24C04中存在页的概念,一页的大小为8字节,若果在单页的范围内,存储地址累加,若超过该页的最大地址,存储地址回到页开始处。所以对于连续读和连续写而言,最大的操作字节数为8。若需要操作的字节内容超过8字节,则需要进行翻页操作,即写入下一页的起始存储地址。

4 总结

I2C设备有很多种,若掌握基本原理,便可见招拆招,那是I2C总线就不那么难了。

5.参考资料

2. PowerPC的I2C实现

Mpc8560的CCSR中控制I2C的寄存器共有6个。

2.1 I2CADR 地址寄存器

CPU也可以是I2C的Slave,CPU的I2C地址有 I2CADR指定

2.2 I2CFDR 频率设置寄存器

The serial bit clock frequency of SCL is equal to the CCB clock divided by the divider.

用来设置I2C总线频率

2.3 I2CCR 控制寄存器

MEN: Module Enable. 置1时,I2C模块使能

MIEN:Module Interrupt Enable. 置1时,I2C中断使能。 MSTA:Master/slave mode. 1 Master mode,0 Slave mode. 当1->0时,CPU发起STOP信号 当0->1时,CPU发起START信号

MTX:Transmit/receive mode select.0 Receive mode,1 Transmit mode TXAK:Transfer acknowledge. 置1时,CPU在9th clock发送ACK拉低SDA RSTA:Repeat START. 置1时,CPU发送REPEAT START

BCST:置1,CPU接收广播信息(信息的slave addr为7个0)

2.4 I2CSR 状态寄存器

MCF:0 Byte transfer is in process 1 Byte transfer is completed

MAAS:当CPU作为Slave时,若I2CDR与会话中Slaveaddr匹配,此bit被置1

MBB:0 I2C bus idle 1 I2C bus busy

MAL:若置1,表示仲裁失败

BCSTM:若置1,表示接收到广播信息

SRW:When MAAS is set, SRW indicates the value of the R/W command bit of the calling address, which is sent from the master. 0 Slave receive, master writing to slave 1 Slave transmit, master reading from slave

MIF:Module interrupt. The MIF bit is set when an interrupt is pending, causing a processor interrupt request(provided I2CCR[MIEN] is set)

RXAK:若置1,表示收到了ACK

2.5 I2CDR 数据寄存器

这个寄存器储存CPU将要传输的数据。

3. PPC-Linux中I2C的实现

内核代码(linux-2.6.24)中,通过I2C总线存取寄存器的函数都在文件drivers/i2c/busses/i2c-mpc.c中 最重要的函数是mpc_xfer.

1.

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.

{

struct i2c_msg *pmsg; int i; int ret = 0;

unsigned long orig_jiffies = jiffies;

struct mpc_i2c *i2c = i2c_get_adapdata(adap);

mpc_i2c_start(i2c); // 设置I2CCR[MEN], 使能I2C module

/* Allow bus up to 1s to become not busy */ //一直读I2CSR[MBB],等待I2C总线空闲下来

while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { if (signal_pending(current)) { pr_debug(\); writeccr(i2c, 0); return -EINTR;

static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msg

s, int num)

18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42.

}

if (time_after(jiffies, orig_jiffies + HZ)) { pr_debug(\);

if (readb(i2c->base + MPC_I2C_SR) == (CSR_MCF | CSR_MBB | CSR_RXAK)) mpc_i2c_fixup(i2c); return -EIO; }

schedule(); }

for (i = 0; ret >= 0 && i < num; i++) { pmsg = &msgs[i];

pr_debug(\, pmsg->flags & I2C_M_RD ? \ : \, pmsg->len, pmsg->addr, i + 1, num); //根据消息里的flag进行读操作或写操作 if (pmsg->flags & I2C_M_RD)

ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->le else

ret = mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->le }

mpc_i2c_stop(i2c); //保证为I2CCSR[MSTA]为0,保证能触发STOP return (ret < 0) ? ret : num; }

n, i);

n, i);

1.

2. 3. 4. 5. 6. 7. 8. 9. 10. 11.

T信号 12.

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); const u8 * data, int length, int restart) {

int i;

unsigned timeout = i2c->adap.timeout; u32 flags = restart ? CCR_RSTA : 0;

/* Start with MEN */ //以防万一,保证I2C模块使能起来 if (!restart)

writeccr(i2c, CCR_MEN);

/* Start as master */ //写了I2CCR[CCR_MSTA],触发CPU发起STARstatic int mpc_write(struct mpc_i2c *i2c, int target,

13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28.

/* Write target byte */ //CPU发送一个字节,slave I2C addr和0 (写 writeb((target << 1), i2c->base + MPC_I2C_DR);

if (i2c_wait(i2c, timeout, 1) < 0) //等待slave 发ACK return -1;

for (i = 0; i < length; i++) { /* Write data byte */

writeb(data[i], i2c->base + MPC_I2C_DR); //CPU接着发数据,包括r

if (i2c_wait(i2c, timeout, 1) < 0) //等待slave 发ACK return -1; }

return 0; }

操作bit)

eg addr和data

1.

ng) 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.

{

unsigned long orig_jiffies = jiffies; u32 x;

int result = 0;

if (i2c->irq == 0)

{ //循环读I2CSR,直到I2CSR[MIF]置1

while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { schedule();

if (time_after(jiffies, orig_jiffies + timeout)) { pr_debug(\); writeccr(i2c, 0); result = -EIO; break; } }

x = readb(i2c->base + MPC_I2C_SR); writeb(0, i2c->base + MPC_I2C_SR); } else {

/* Interrupt mode */

result = wait_event_interruptible_timeout(i2c->queue, static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writi

23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58.

(i2c->interrupt & CSR_MIF), timeout * HZ);

if (unlikely(result < 0)) {

pr_debug(\); writeccr(i2c, 0);

} else if (unlikely(!(i2c->interrupt & CSR_MIF))) { pr_debug(\); writeccr(i2c, 0); result = -ETIMEDOUT; }

x = i2c->interrupt; i2c->interrupt = 0; }

if (result < 0) return result;

if (!(x & CSR_MCF)) {

pr_debug(\); return -EIO; }

if (x & CSR_MAL) { //仲裁失败 pr_debug(\); return -EIO; }

if (writing && (x & CSR_RXAK)) {//写后没收到ACK pr_debug(\); /* generate stop */ writeccr(i2c, CCR_MEN); return -EIO; }

return 0; }

static int mpc_read(struct mpc_i2c *i2c, int target,

1.

2. 3. 4. 5. 6.

u8 * data, int length, int restart) {

unsigned timeout = i2c->adap.timeout; int i;

u32 flags = restart ? CCR_RSTA : 0;

7. 8. 9. 10. 11. 12.

13. 14. 15. 16.

/ 17. 18.

/* Start with MEN */ //以防万一,保证I2C模块使能 if (!restart)

writeccr(i2c, CCR_MEN); /* Switch to read - restart */

//注意这里,再次把CCR_MSTA置1,再触发 START

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);

/* Write target address byte - this time with the read flag set *

//CPU发送slave I2C addr和读操作1

writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);

//等待Slave发ACK 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28.

if (i2c_wait(i2c, timeout, 1) < 0) return -1;

if (length) {

if (length == 1)

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); else //为什么不置 TXAK

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); /* Dummy read */

readb(i2c->base + MPC_I2C_DR); }

for (i = 0; i < length; i++) {

if (i2c_wait(i2c, timeout, 0) < 0) return -1;

/* Generate txack on next to last byte */

//注意这里TXAK置1,表示CPU每收到1byte数据后,会发送ACK if (i == length - 2)

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);

/* Generate stop on last byte */

//注意这里CCR_MSTA [1->0] CPU会触发STOP if (i == length - 1)

writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);

data[i] = readb(i2c->base + MPC_I2C_DR); }

29. 30. 31.

return length; }