EEPROM I2C操作说明.docx
《EEPROM I2C操作说明.docx》由会员分享,可在线阅读,更多相关《EEPROM I2C操作说明.docx(15页珍藏版)》请在冰豆网上搜索。
![EEPROM I2C操作说明.docx](https://file1.bdocx.com/fileroot1/2023-2/2/11701fe9-3369-40a9-a4a7-3cd1c993d753/11701fe9-3369-40a9-a4a7-3cd1c993d7531.gif)
EEPROMI2C操作说明
I2C协议
2条双向串行线,一条数据线SDA,一条时钟线SCL。
SDA传输数据是大端传输,每次传输8bit,即一字节。
支持多主控(multimastering),任何时间点只能有一个主控。
总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.
系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet。
1.1I2C位传输
数据传输:
SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit;
若SDA发生跳变,则用来表示一个会话的开始或结束(后面讲)
数据改变:
SCL为低电平时,SDA线才能改变传输的bit
1.2I2C开始和结束信号
开始信号:
SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:
SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
1.3I2C应答信号
Master每发送完8bit数据后等待Slave的ACK。
即在第9个clock,若从IC发ACK,SDA会被拉低。
若没有ACK,SDA会被置高,这会引起Master发生RESTART或STOP流程,如下所示:
1.4I2C写流程
写寄存器的标准流程为:
1. Master发起START
2. Master发送I2Caddr(7bit)和w操作0(1bit),等待ACK
3. Slave发送ACK
4. Master发送regaddr(8bit),等待ACK
5. Slave发送ACK
6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
7. Slave发送ACK
8. 第6步和第7步可以重复多次,即顺序写多个寄存器
9. Master发起STOP
写一个寄存器
写多个寄存器
1.5I2C读流程
读寄存器的标准流程为:
1. Master发送I2Caddr(7bit)和w操作1(1bit),等待ACK
2. Slave发送ACK
3. Master发送regaddr(8bit),等待ACK
4. Slave发送ACK
5. Master发起START
6. Master发送I2Caddr(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.3I2C设备并不只有一个设备地址
这一点往往被忽略,一般情况下认为在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.1I2CADR地址寄存器
CPU也可以是I2C的Slave,CPU的I2C地址有I2CADR指定
2.2I2CFDR频率设置寄存器
TheserialbitclockfrequencyofSCLisequaltotheCCBclockdividedbythedivider.
用来设置I2C总线频率
2.3I2CCR控制寄存器
MEN:
ModuleEnable. 置1时,I2C模块使能
MIEN:
ModuleInterruptEnable.置1时,I2C中断使能。
MSTA:
Master/slavemode.1Mastermode,0Slavemode.
当1->0时,CPU发起STOP信号
当0->1时,CPU发起START信号
MTX:
Transmit/receivemodeselect.0Receivemode,1Transmitmode
TXAK:
Transferacknowledge.置1时,CPU在9thclock发送ACK拉低SDA
RSTA:
RepeatSTART.置1时,CPU发送REPEATSTART
BCST:
置1,CPU接收广播信息(信息的slaveaddr为7个0)
2.4I2CSR状态寄存器
MCF:
0 Bytetransferisinprocess
1 Bytetransferiscompleted
MAAS:
当CPU作为Slave时,若I2CDR与会话中Slaveaddr匹配,此bit被置1
MBB:
0I2Cbusidle
1I2Cbusbusy
MAL:
若置1,表示仲裁失败
BCSTM:
若置1,表示接收到广播信息
SRW:
WhenMAASisset,SRWindicatesthevalueoftheR/Wcommandbitofthecallingaddress,whichissentfromthemaster.
0Slavereceive,masterwritingtoslave
1Slavetransmit,masterreadingfromslave
MIF:
Moduleinterrupt.TheMIFbitissetwhenaninterruptispending,causingaprocessorinterruptrequest(providedI2CCR[MIEN]isset)
RXAK:
若置1,表示收到了ACK
2.5I2CDR数据寄存器
这个寄存器储存CPU将要传输的数据。
3.PPC-Linux中I2C的实现
内核代码(linux-2.6.24)中,通过I2C总线存取寄存器的函数都在文件drivers/i2c/busses/i2c-mpc.c中
最重要的函数是mpc_xfer.
1.static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
2.{
3. struct i2c_msg *pmsg;
4. int i;
5. int ret = 0;
6. unsigned long orig_jiffies = jiffies;
7. struct mpc_i2c *i2c = i2c_get_adapdata(adap);
8.
9. mpc_i2c_start(i2c); //设置I2CCR[MEN],使能I2Cmodule
10.
11. /*Allowbusupto1stobecomenotbusy*/
12. //一直读I2CSR[MBB],等待I2C总线空闲下来
13. while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
14. if (signal_pending(current)) {
15. pr_debug("I2C:
Interrupted\n");
16. writeccr(i2c, 0);
17. return -EINTR;
18. }
19. if (time_after(jiffies, orig_jiffies + HZ)) {
20. pr_debug("I2C:
timeout\n");
21. if (readb(i2c->base + MPC_I2C_SR) ==
22. (CSR_MCF | CSR_MBB | CSR_RXAK))
23. mpc_i2c_fixup(i2c);
24. return -EIO;
25. }
26. schedule();
27. }
28.
29. for (i = 0; ret >= 0 && i < num; i++) {
30. pmsg = &msgs[i];
31. pr_debug("Doing%s%dbytesto0x%02x-%dof%dmessages\n",
32. pmsg->flags & I2C_M_RD ?
"read" :
"write",
33. pmsg->len, pmsg->addr, i + 1, num);
34. //根据消息里的flag进行读操作或写操作
35. if (pmsg->flags & I2C_M_RD)
36. ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
37. else
38. ret = mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
39. }
40. mpc_i2c_stop(i2c); //保证为I2CCSR[MSTA]为0,保证能触发STOP
41. return (ret < 0) ?
ret :
num;
42.}
1.static int mpc_write(struct mpc_i2c *i2c, int target,
2. const u8 * data, int length, int restart)
3.{
4. int i;
5. unsigned timeout = i2c->adap.timeout;
6. u32flags = restart ?
CCR_RSTA :
0;
7.
8. /*StartwithMEN*/ //以防万一,保证I2C模块使能起来
9. if (!
restart)
10. writeccr(i2c, CCR_MEN);
11. /*Startasmaster*/ //写了I2CCR[CCR_MSTA],触发CPU发起START信号
12. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
13. /*Writetargetbyte*/ //CPU发送一个字节,slaveI2Caddr和0(写操作bit)
14. writeb((target << 1), i2c->base + MPC_I2C_DR);
15.
16. if (i2c_wait(i2c, timeout, 1) < 0) //等待slave发ACK
17. return -1;
18.
19. for (i = 0; i < length; i++) {
20. /*Writedatabyte*/
21. writeb(data[i], i2c->base + MPC_I2C_DR); //CPU接着发数据,包括regaddr和data
22.
23. if (i2c_wait(i2c, timeout, 1) < 0) //等待slave发ACK
24. return -1;
25. }
26.
27. return 0;
28.}
1.static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
2.{
3. unsigned long orig_jiffies = jiffies;
4. u32x;
5. int result = 0;
6.
7. if (i2c->irq == 0)
8. { //循环读I2CSR,直到I2CSR[MIF]置1
9. while (!
(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
10. schedule();
11. if (time_after(jiffies, orig_jiffies + timeout)) {
12. pr_debug("I2C:
timeout\n");
13. writeccr(i2c, 0);
14. result = -EIO;
15. break;
16. }
17. }
18. x = readb(i2c->base + MPC_I2C_SR);
19. writeb(0, i2c->base + MPC_I2C_SR);
20. } else {
21. /*Interruptmode*/
22. result = wait_event_interruptible_timeout(i2c->queue,
23. (i2c->interrupt & CSR_MIF), timeout * HZ);
24.
25. if (unlikely(result < 0)) {
26. pr_debug("I2C:
waitinterrupted\n");
27. writeccr(i2c, 0);
28. } else if (unlikely(!
(i2c->interrupt & CSR_MIF))) {
29. pr_debug("I2C:
waittimeout\n");
30. writeccr(i2c, 0);
31. result = -ETIMEDOUT;
32. }
33.
34. x = i2c->interrupt;
35. i2c->interrupt = 0;
36. }
37.
38. if (result < 0)
39. return result;
40.
41. if (!
(x & CSR_MCF)) {
42. pr_debug("I2C:
unfinished\n");
43. return -EIO;
44. }
45.
46. if (x & CSR_MAL) { //仲裁失败
47. pr_debug("I2C:
MAL\n");
48. return -EIO;
49. }
50.
51. if (writing && (x & CSR_RXAK)) {//写后没收到ACK
52. pr_debug("I2C:
NoRXAK\n");
53. /*generatestop*/
54. writeccr(i2c, CCR_MEN);
55. return -EIO;
56. }
57. return 0;
58.}
1.static int mpc_read(struct mpc_i2c *i2c, int target,
2. u8 * data, int length, int restart)
3.{
4. unsigned timeout = i2c->adap.timeout;
5. int i;
6. u32flags = restart ?
CCR_RSTA :
0;
7.
8. /*StartwithMEN*/ //以防万一,保证I2C模块使能
9. if (!
restart)
10. writeccr(i2c, CCR_MEN);
11. /*Switchtoread-restart*/
12. //注意这里,再次把CCR_MSTA置1,再触发START
13. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
14.
15.
16. /*Writetargetaddressbyte-thistimewiththereadflagset*/
17. //CPU发送slaveI2Caddr和读操作1
18. writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
//等待Slave发ACK
1. if (i2c_wait(i2c, timeout, 1) < 0)
2. return -1;
3.
4. if (length) {
5. if (length == 1)
6. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
7. else //为什么不置TXAK
8. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
9. /*Dummyread*/
10. readb(i2c->base + MPC_I2C_DR);
11. }
12.
13. for (i = 0; i < length; i++) {
14. if (i2c_wait(i2c, timeout, 0) < 0)
15. return -1;
16.
17. /*Generatetxackonnexttolastbyte*/
18. //注意这里TXAK置1,表示CPU每收到1byte数据后,会发送ACK
19. if (i == length - 2)
20. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
21.
22. /*Generatestoponlastbyte*/
23. //注意这里CCR_MSTA[1->0]CPU会触发STOP
24. if (i == length - 1)
25. writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
26.
27. data[i] = readb(i2c-