iic裸机程序运行机制
本文代码适用于AT24cxx平台E2PROM
使用I2C实现的AT24cxx系列的读写函数
读操作
1 2 3 4 5 6 7 8 9 10
| unsigned char at24cxx_read(unsigned char address) { unsigned char val; printf("at24cxx_read address = %d\r\n", address); i2c_write(0xA0, &address, 1); printf("at24cxx_read send address ok\r\n"); i2c_read(0xA0, (unsigned char *)&val, 1); printf("at24cxx_read get data ok\r\n"); return val; }
|

上图为读,根据要求,我们要发出device address,和word address。i2c_write(0xA0, &address, 1);
三个参数:
- 第一个参数是device address。
- 第二个参数为word address-存放的地址,在这里是先做数据存放的buf缓冲区,通常为参数传入。
- 第三个参数是数据长度
所以我们完成一个读的操作,需要先写入word地址,之后再进行读操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
void i2c_read(unsigned int slvAddr, unsigned char *buf, int len) { g_tS3C24xx_I2C.Mode = RDDATA; g_tS3C24xx_I2C.Pt = -1; g_tS3C24xx_I2C.pData = buf; g_tS3C24xx_I2C.DataCount = len; IICDS = slvAddr; IICSTAT = 0xb0; while (g_tS3C24xx_I2C.DataCount != 0); }
|
在这之后会触发i2c的中断服务程序
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 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
| void I2CIntHandle(void) { unsigned int iicSt,i;
SRCPND = BIT_IIC; INTPND = BIT_IIC; iicSt = IICSTAT;
if(iicSt & 0x8){ printf("Bus arbitration failed\n\r"); }
switch (g_tS3C24xx_I2C.Mode) { case RDDATA: { if (g_tS3C24xx_I2C.Pt == -1) { g_tS3C24xx_I2C.Pt = 0; if(g_tS3C24xx_I2C.DataCount == 1) IICCON = 0x2f; else IICCON = 0xaf; break; }
g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS; g_tS3C24xx_I2C.DataCount--; if (g_tS3C24xx_I2C.DataCount == 0) {
IICSTAT = 0x90; IICCON = 0xaf; Delay(10000); break; } else { if(g_tS3C24xx_I2C.DataCount == 1) IICCON = 0x2f; else IICCON = 0xaf; } break; } } }
|
我们先把关于读的部分去掉不看。根据上面设置的g_ts3c24xx_I2C.Mode。我们会进入case RDDATA中。
g_tS3C24xx_I2C.Pt == -1。是我们一开始设置的索引。因为第一个发出的是设备地址,没有数据,所以要单独处理。
g_tS3C24xx_I2C.DataCount == 1。若满足,则说明只接受一个数据。只接收一个数据的时候NO ACK(看图可知)
所以发出IICCON = 0x2f(具体相关含义查看芯片手册即可,不赘述!要是实在想不明白也不想去看芯片手册就把这个当成一个指令:恢复I2C传输,开始接收数据,接收到数据时不发出ACK
之后的break弹出了此次的操作。
因为没有发出stop的信号,在下一个数据到来的时候,会再次进入中断。
g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS; g_tS3C24xx_I2C.DataCount–; 。接收数据
g_tS3C24xx_I2C.DataCount == 0。说明数据已经全部读完。所以
1 2 3 4
| IICSTAT = 0x90; IICCON = 0xaf; Delay(10000); break;
|
发出P信号(终止信号)退出本次通讯。
写操作

写操作就相对简单一些,我们只需要直接将word address和data打包一股脑扔过去就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
void i2c_write(unsigned int slvAddr, unsigned char *buf, int len) { g_tS3C24xx_I2C.Mode = WRDATA; g_tS3C24xx_I2C.Pt = 0; g_tS3C24xx_I2C.pData = buf; g_tS3C24xx_I2C.DataCount = len; IICDS = slvAddr; IICSTAT = 0xf0; while (g_tS3C24xx_I2C.DataCount != -1); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| case WRDATA: { if((g_tS3C24xx_I2C.DataCount--) == 0) { IICSTAT = 0xd0; IICCON = 0xaf; Delay(10000); break; }
IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++]; for (i = 0; i < 10; i++);
IICCON = 0xaf; break; }
|
这里我们直接看case WRDATA就好了。
逻辑其实很清晰
裸板的代码就到此为止了。没有太分析i2c的硬件逻辑,毕竟还是写软件为主。未来不排除会专门讲讲i2c咕咕咕
下一步是关于linux中的i2c驱动。