本文对如何使用stm32cube生成I2C工程不作说明,仅对在对AT24Cxx系列的使用时作出易忽略的说明;
1、at24cxx页面结构:
从该图可以看出16K(bit)共有128个页,每页由16byte构成。16k = 128 * 16 * 8;
特别注意:除at24c01和at24c02的页由8个byte构成,其它的都是由16byte构成。(这关系到对芯片的连续读写)
2.at24cxx的设备地址:
A:作为设备地址的一部分;P:作为页地址的一部分;
对于大多数人来说都知道I2c设备具有一个设备地址,并且在一条总线上是唯一。若要在一个I2C总线上挂多个AT24CXX系列芯片,则需要A2,A1,A0作为设备地址的一部分,设备地址的最低位作为是读(1)写(0)。
对于在一条总线挂载的设备数:看有几个位用作设备地址。
例:AT24C04: 有两位用作设备地址(A2,A1),一位用作页地址(P0).则可挂载的设备数为2^2 = 4个。
AT24C16:没有用作设备地址的位,三位用作页地址(P0,P1,P2)。则仅可挂载设备数为2^0= 1个。
可能有人会考虑若A0,A1,A2用作地址了,那实际硬件接线该怎么接呢?
直接按照AT24C02的接就OK了.都接地。
3、页面连续读写:
注意:连续写并不是可以一直连续的写N个数据,而是写一页的数据(AT24C02只能连续写8byte的数据,其它的可以连续写16byte的数据。);
当从某个地址连续写多个数据时,要确定这个地址在某一页的偏移量,从而确定该页中最多还可以连续写多少byte数据。
以AT24C16为例;
比如在0x0025读写,则该位置的偏移量为:0x0025 & 0x000FF = 0x0005, 即该页还可以写 = 每页的字节数 - 偏移量 = 0x0F - 0x05 = 0x0A 即在该页还可以连续写10byte数据;
4、地址问题:
由于在传输过程中,地址数据是一个8位的地址,只能按该8位地址寻址的数据有2^8=256byte数据,
对于容量大于256byte容量的设备,我们还有设备ID中的页地址位可以使用。
例:以AT24C16为例 :
比如我们要访问(写)第0x0456的这一byte数据0x55:
(1)、开始信号:
(2)、设备号(高4位为:1101 p p p r/w), 此时设备号应设置为:0xA4;
(3)、地址:0x56;
(4)、数据:0x55;
(5)、结束;
5、时间控制:
每完成一次写操作后要进行一定的延时,让芯片去处理数据;从该图看,保险的时间为5ms.
例子:
#include "at24c16.h"
#include "i2c.h"
#define E2PROM_SIZE 0x0800 //2k byte 16bit
#define E2PROM_BASE_ID 0xA0
#define E2PROM_WRITE 0x00
#define E2PROM_READ 0x01
#define E2PROM_BASE_WID E2PROM_BASE_ID + E2PROM_WRITE
#define E2PROM_BASE_RID E2PROM_BASE_ID + E2PROM_READ
#define E2PROM_PAGE_MASK 0x000F
uint8_t writeAT24C16(uint16_t addr, uint8_t *data, uint16_t len)
{
uint8_t wNum = 0;
uint16_t lenLeft = len;
uint8_t deviceId ;
uint8_t *p = data;
/*is the address overfolw*/
if(addr + len >= E2PROM_SIZE)
return 1;
/*calculate the current write position to know how many word can write continully*/
wNum = 16 - addr & E2PROM_PAGE_MASK;
if(wNum == 0)
wNum = 16;
wNum = lenLeft>=wNum ? wNum : lenLeft;
/*transmit the date to e2prom*/
while(lenLeft)
{
/*calculate the device id*/
deviceId = (addr >> 8)<=0 ? E2PROM_BASE_WID : (E2PROM_BASE_WID | (uint8_t)((addr>>7)&0x0E));
if( HAL_I2C_Mem_Write(&hi2c1, deviceId, addr&0x00FF,
I2C_MEMADD_SIZE_8BIT, p, wNum, 0x20) != HAL_OK)
{
printf("I2S Write error!\r\n");
HAL_Delay(5);
continue;
}
addr += wNum;
lenLeft -= wNum;
p += wNum;
wNum = lenLeft > 16 ? 16 : lenLeft;
HAL_Delay(5);
}
return HAL_OK;
}
uint8_t readAT24C16(uint16_t addr, uint8_t *data, uint16_t len)
{
uint8_t rNum = 0;
uint16_t lenLeft = len;
uint8_t deviceId ;
uint8_t *p = data;
/*is the address overfolw*/
if(addr + len >= E2PROM_SIZE)
return 1;
/*calculate the current write position to know how many word can write continully*/
rNum = 16 - addr & E2PROM_PAGE_MASK;
if(rNum == 0)
rNum = 16;
rNum = lenLeft>=rNum ? rNum : lenLeft;
/*transmit the date to e2prom*/
while(lenLeft)
{
/*calculate the device id*/
deviceId = (addr >> 8)<=0 ? E2PROM_BASE_RID : (E2PROM_BASE_RID | (uint8_t)((addr>>7)&0x0E));
if( HAL_I2C_Mem_Read(&hi2c1, deviceId, addr&0x00FF,
I2C_MEMADD_SIZE_8BIT, p, rNum, 20) != HAL_OK)
{
printf("I2S Read error!\r\n");
continue;
}
addr += rNum;
lenLeft -= rNum;
p += rNum;
rNum = lenLeft > 16 ? 16 : lenLeft;
}
return HAL_OK;
}
void vE2romTest()
{
uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize];
uint16_t i;
printf("\r\n***************I2C Example*******************************\r\n");
for(i=0; i<256; i++)
WriteBuffer[i]=i; /* WriteBuffer init */
/* wrinte date to EEPROM */
if(!writeAT24C16(0x05,WriteBuffer,BufferSize))
printf("\r\n EEPROM 24C16 Write Test OK \r\n");
else
printf("\r\n EEPROM 24C16 Write Test False \r\n");
/* read date from EEPROM */
readAT24C16(0x05,ReadBuffer, BufferSize);
for(i=0; i<256; i++)
printf("0x%02X ",ReadBuffer[i]);
if(memcmp(WriteBuffer,ReadBuffer,BufferSize) == 0 ) /* check date */
printf("\r\n EEPROM 24C16 Read Test OK\r\n");
else
printf("\r\n EEPROM 24C16 Read Test False\r\n");
}
测试结果: