本文對如何使用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");
}
測試結果: