STM32基礎(chǔ):IIC總線操作EEPROM存儲(chǔ)模塊AT24C02
參考文檔:AT24C02數(shù)據(jù)手冊
STM32基礎(chǔ):IIC概述與軟件模擬IIC一文中,詳細(xì)介紹了使用STM32的GPIO口模擬IIC總線的方法,如果讀者對IIC總線還不了解,請先閱讀此文。
本文是IIC總線的實(shí)際應(yīng)用,將帶領(lǐng)讀者一步一步閱讀AT24C02數(shù)據(jù)手冊,看時(shí)序圖了解如何使用IIC接口EEPROM存儲(chǔ)模塊AT24C02,并編寫代碼使用STM32驅(qū)動(dòng)這個(gè)模塊。
1 AT24C02概述
1.1 基本描述
《AT24C02數(shù)據(jù)手冊》P1
從上文可以知道,AT24C02提供2048位串行電可擦除和可編程只讀存儲(chǔ)器(EEPROM),組織256字節(jié)。該器件針對許多汽車應(yīng)用進(jìn)行了優(yōu)化,在這些應(yīng)用中,低功耗和低電壓操作是必不可少的。
AT24C02具有節(jié)省空間的8引腳JEDEC SOIC和8引腳TSSOP封裝,可通過 雙線串行接口(即IIC) 訪問。此外,整個(gè)系列還提供2.7V (2.7V至5.5V)版本。
1.2 重要特點(diǎn)
AT24C02有如下特點(diǎn),重點(diǎn)用紅色標(biāo)出:
《AT24C02數(shù)據(jù)手冊》P1
- 存儲(chǔ)器內(nèi)部按組織256字節(jié) × 8位 (2K)組織
- 雙線串行接口(IIC)
- 兼容400kHz通信速率
- 具有硬件數(shù)據(jù)保護(hù)的寫保護(hù)引腳
- 8字節(jié)/頁寫模式
- 允許部分頁寫入
- 高可靠性:100萬次寫周期,數(shù)據(jù)保留:100年
1.3 引腳定義
《AT24C02數(shù)據(jù)手冊》P1
《AT24C02數(shù)據(jù)手冊》P2
《AT24C02數(shù)據(jù)手冊》P3
DA、SCL為IIC總線使用引腳不再贅述。從上文可以知道,A2,A1和A0引腳用于AT24C02的設(shè)備地址輸入。WP為寫保護(hù)引腳,提供硬件數(shù)據(jù)保護(hù)。寫保護(hù)引腳在連接到地(GND)時(shí)允許正常的讀寫操作。當(dāng)寫保護(hù)引腳接在VCC上時(shí),寫保護(hù)功能開啟,操作如下表所示。
《AT24C02數(shù)據(jù)手冊》P3
在開發(fā)板的原理圖上可以看到,設(shè)備地址輸入A2、A1、A0都為0,WP已近接在GND上關(guān)閉了寫保護(hù),我們可以正常讀寫。
1.4 存儲(chǔ)空間
《AT24C02數(shù)據(jù)手冊》P4
AT24C02,2K,串行EEPROM內(nèi)部組織為32頁,每頁8字節(jié),2K需要一個(gè)8位的字地址進(jìn)行隨機(jī)字尋址。
1.5 設(shè)備地址
《AT24C02數(shù)據(jù)手冊》P8
2K EEPROM設(shè)備都需要一個(gè) 8位設(shè)備地址字 , 包含一個(gè)啟動(dòng)條件 ,以使芯片能夠進(jìn)行讀或?qū)懖僮鳌?/p>
設(shè)備地址字前4位最高有效位為1010。這對所有串行EEPROM設(shè)備都是通用的。接下來的3位是1K/2K EEPROM的A2、A1和A0設(shè)備地址位。設(shè)備地址的第8位是讀寫操作選擇位。如果該位高,則進(jìn)行讀操作;如果該位低,則進(jìn)行寫操作。
綜上,如果對AT24C02進(jìn)行讀操作,則設(shè)備地址為10100001B=A1H;如果對AT24C02進(jìn)行寫操作,則設(shè)備地址為10100000B=A0H.
《AT24C02數(shù)據(jù)手冊》P9
2 AT24C01編程
2.1 寫操作:字編程和頁編程
《AT24C02數(shù)據(jù)手冊》P8
寫數(shù)據(jù)有字編程和頁編程兩種方式:
- 字編程:一次寫入一個(gè)字節(jié)。
- 頁編程:1K/2K EEPROM能夠一次寫入一個(gè)8字節(jié)的頁。
如果打算寫入數(shù)據(jù),則需要知道數(shù)據(jù)的存儲(chǔ)地址。因?yàn)锳T24C02的存儲(chǔ)空間為2K(2^11),故尋址空間為02^11-1,即000H7FFH。每頁8字節(jié),故第1頁地址000H,第2頁地址008H,第3頁地址010H,……,第256頁地址7F8H。
下圖描述了字編程寫數(shù)據(jù)的過程,根據(jù)圖中的分析和前文的講述,可以寫出字編程函數(shù):
《AT24C02數(shù)據(jù)手冊》P10
/**
* @brief AT24C02初始化
*
*/
void AT24C02_Init(void)
{
IIC_Init(); // 初始化IIC總線
}
/**
* @brief AT24C02字節(jié)寫入
*
* @param Address: 字節(jié)地址
* @param Data: 待寫入的數(shù)據(jù)
*/
void AT24C02_ByteWrite(uint8_t Address, uint8_t Data)
{
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,寫操作
IIC_SendBytes(0xA0);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 發(fā)送字地址
IIC_SendBytes(Address);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the word address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the word address: OKn");
}
// 發(fā)送待寫入的數(shù)據(jù)
IIC_SendBytes(Data);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the data: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the data: OKn");
}
IIC_StopSignal(); // 發(fā)送停止信號
}
下圖描述了頁編程寫數(shù)據(jù)的過程,根據(jù)圖中的分析和前文的講述,可以寫出頁編程函數(shù):
《AT24C02數(shù)據(jù)手冊》P10
/**
* @brief AT24C02頁編程(8字節(jié)/頁)
*
* @param Address: 頁地址
* @param buf: 待寫入的數(shù)據(jù)
* @param DataLen: 待寫入的數(shù)據(jù)長度
*/
void AT24C02_PageWrite(uint32_t Address, uint8_t *buf, uint8_t DataLen)
{
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,寫操作
IIC_SendBytes(0xA0);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 發(fā)送頁地址
IIC_SendBytes(Address);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the page address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the page address: OKn");
}
// 循環(huán)發(fā)送數(shù)據(jù)
while (DataLen--)
{
IIC_SendBytes(*buf++); // 發(fā)送數(shù)據(jù)
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the data: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the data: OKn");
}
}
IIC_StopSignal(); // 發(fā)送停止信號
}
2.2 讀操作:當(dāng)前地址讀
《AT24C02數(shù)據(jù)手冊》P19
- 當(dāng)前地址讀:內(nèi)部地址計(jì)數(shù)器保存著上次訪問時(shí)最后一個(gè)地址加1的值 。(使用”當(dāng)前地址讀“讀出的數(shù)據(jù),其地址為上次訪問的最后一個(gè)地址加1。)
- 只要芯片有電,該地址就一直保存,當(dāng)讀到最后頁的最后字節(jié),地址會(huì)回轉(zhuǎn)到0:
- 當(dāng)寫到某頁尾的最后一個(gè)字節(jié),地址會(huì)回轉(zhuǎn)到該頁的首字節(jié)。
- 接收器件地址(讀/寫選擇位為"1")、EEPROM應(yīng)答ACK后,當(dāng)前地址的數(shù)據(jù)就隨時(shí)鐘送出。主器件無需應(yīng)答"0",但需發(fā)送停止條件。
《AT24C02數(shù)據(jù)手冊》P10
根據(jù)此圖和前文的講述,編寫當(dāng)前地址讀函數(shù):
/**
* @brief AT24C02當(dāng)前地址讀
*
* @return uint8_t 當(dāng)前地址的數(shù)據(jù)
*/
uint8_t AT24C02_CurrentAddressRead(void)
{
uint8_t Data;
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,讀操作
IIC_SendBytes(0xA1);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 讀取1字節(jié)數(shù)據(jù)
Data = IIC_ReadBytes();
// 發(fā)送應(yīng)答信號
IIC_MasterACK(1); // 不應(yīng)答
IIC_StopSignal(); // 發(fā)送停止信號
return Data; // 返回接收到的數(shù)據(jù)
}
下面我們測試前面寫的四個(gè)函數(shù)(AT24C02_Init
,AT24C02_ByteWrite
,AT24C02_PageWrite
,AT24C02_CurrentAddressRead
),測試過程為:
- 使用
AT24C02_Init
初始化AT24C02 - 使用
AT24C02_PageWrite
向0x00寫入“0123456” - 使用
AT24C02_ByteWrite
向0x07寫入“7” - 使用
AT24C02_CurrentAddressRead
進(jìn)行“當(dāng)前地址讀”
(0x00-0x07已經(jīng)被寫入“01234567”,即第一頁寫滿了,此時(shí)地址會(huì)回到該頁首字節(jié),使用“當(dāng)前地址讀”讀出的應(yīng)該是該頁第一個(gè)字節(jié)“0”)
如果輸出的提示信息全部正常且返回值為“0”則說明這四個(gè)函數(shù)編寫正確。測試效果如下圖,測試成功。
uint8_t data;
AT24C02_Init()
AT24C02_PageWrite(0x00, "0123456", 6);
HAL_Delay(100);
AT24C02_ByteWrite(0x07, '7');
HAL_Delay(100);
data = AT24C02_CurrentAddressRead();
printf("AT24C02_CurrentAddressRead: %c", data);
2.3 讀操作:隨機(jī)讀
《AT24C02數(shù)據(jù)手冊》P9
隨機(jī)讀需先寫一個(gè)目標(biāo)字地址 ,一旦EEPROM接收器設(shè)備地址(讀/寫選擇位為"0")和字地址并應(yīng)答了ACK,主機(jī)就產(chǎn)生一個(gè)重復(fù)的起始條件。然后,主器件發(fā)送設(shè)備地址(讀/寫選擇位為"1"),EEPROM應(yīng)答ACK,并隨時(shí)鐘送出數(shù)據(jù)。主器件無需應(yīng)答"0",但需發(fā)送停止條件。
《AT24C02數(shù)據(jù)手冊》P11
根據(jù)此圖和前文的講述,編寫隨機(jī)讀函數(shù):
/**
* @brief AT24C02隨機(jī)讀1字節(jié)數(shù)據(jù)
*
* @param Address: 字地址
* @return uint8_t 讀取到的數(shù)據(jù)
*/
uint8_t AT24C02_RandomRead(uint8_t Address)
{
uint8_t Data;
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,寫操作
IIC_SendBytes(0xA0);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 發(fā)送字?jǐn)?shù)據(jù)的地址
IIC_SendBytes(Address);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the word address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the word address: OKn");
}
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,讀操作
IIC_SendBytes(0xA1);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 讀取1字節(jié)數(shù)據(jù)
Data = IIC_ReadBytes();
// 發(fā)送應(yīng)答信號
IIC_MasterACK(1); // 不應(yīng)答
IIC_StopSignal(); // 發(fā)送停止信號
return Data; // 返回接收到的數(shù)據(jù)
}
下面我們測試AT24C02_RandomRead
函數(shù),測試過程為:
- 使用
AT24C02_Init
初始化AT24C02 - 使用
AT24C02_PageWrite
向0x00寫入“01234567” - 使用
AT24C02_RandomRead
讀出0x07處的數(shù)據(jù)
uint8_t data;
AT24C02_Init();
AT24C02_PageWrite(0x00, "01234567", 7);
HAL_Delay(100);
data = AT24C02_RandomRead(0x07);
printf("AT24C02_RandomRead: %c", data);
如果輸出的提示信息全部正常且返回值為“7”則說明函數(shù)編寫正確。測試效果如下圖,測試成功。
如果想要“隨機(jī)讀”函數(shù)可以一次讀取多個(gè)字節(jié),可以修改函數(shù)為如下形式:
/**
* @brief AT24C02隨機(jī)讀n個(gè)字節(jié)
*
* @param Address: 字地址
* @param RecvBuf: 接收緩沖區(qū)
* @param DataLen: 接收數(shù)據(jù)長度
*/
void AT24C02_RandomRead(uint8_t Address, uint8_t *RecvBuf, uint8_t DataLen)
{
uint8_t Data;
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,寫操作
IIC_SendBytes(0xA0);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 發(fā)送字?jǐn)?shù)據(jù)的地址
IIC_SendBytes(Address);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the word address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the word address: OKn");
}
IIC_StartSignal(); // 發(fā)送開始信號
// 發(fā)送設(shè)備地址,讀操作
IIC_SendBytes(0xA1);
if (IIC_WaitACK() == 1) // AT24C02沒應(yīng)答
{
printf("[AT24C02] Answered the device address: Errorn");
IIC_StopSignal(); // 發(fā)送停止信號
}
else
{
printf("[AT24C02] Answered the device address: OKn");
}
// 讀取n字節(jié)數(shù)據(jù)
DataLen -= 1;
while (DataLen--) // 讀取n-1字節(jié)數(shù)據(jù),最后1字節(jié)數(shù)據(jù)單獨(dú)讀取
{
*RecvBuf++ = IIC_ReadBytes();
// 發(fā)送應(yīng)答信號
IIC_MasterACK(0); // 應(yīng)答
}
*RecvBuf++ = IIC_ReadBytes(); // 讀取最后1字節(jié)數(shù)據(jù)
// 發(fā)送應(yīng)答信號
IIC_MasterACK(1); // 不應(yīng)答
IIC_StopSignal(); // 發(fā)送停止信號
return Data; // 返回接收到的數(shù)據(jù)
}
下面我們測試AT24C02_RandomRead
函數(shù),測試過程為:
- 使用
AT24C02_Init
初始化AT24C02 - 使用
AT24C02_PageWrite
向0x00寫入“01234567” - 使用
AT24C02_RandomRead
讀出0x00-0x07處的數(shù)據(jù)
uint8_t RecvBuf[10] = {0};
AT24C02_Init();
AT24C02_PageWrite(0x00, "01234567", 8);
HAL_Delay(100);
AT24C02_RandomRead(0x00, RecvBuf, 8);
printf("AT24C02_RandomRead: %s", RecvBuf);
如果輸出的提示信息全部正常且返回值為“01234567”則說明函數(shù)編寫正確。測試效果如下圖,測試成功。
2.4 讀操作:順序讀
《AT24C02數(shù)據(jù)手冊》P9
AT24C02還有一種讀操作,就是順序讀取。順序讀取由當(dāng)前地址讀取或隨機(jī)地址讀取啟動(dòng)。主機(jī)接收到一個(gè)數(shù)據(jù)字后,它以確認(rèn)響應(yīng)。只要 EEPROM 接收到應(yīng)答,將自動(dòng)增加字地址并繼續(xù)隨時(shí)鐘發(fā)送后面的數(shù)據(jù)。當(dāng)達(dá)到內(nèi)存地址限制時(shí),地址自動(dòng)回轉(zhuǎn)到0,認(rèn)可繼續(xù)順序讀取數(shù)據(jù)。主機(jī)不應(yīng)答“0”,而發(fā)送停止條件,即可結(jié)束順序讀操作。
《AT24C02數(shù)據(jù)手冊》P11
正所謂“紙上得來終覺淺,絕知此事要躬行”。在對順序讀寫略加分析后,順序讀取的代碼這里不再給出,讀者可以結(jié)合本文對前面幾種讀寫操作的講解和順序讀操作的流程自行完成。
-
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354286 -
EEPROM
+關(guān)注
關(guān)注
9文章
1008瀏覽量
81332 -
IIC總線
+關(guān)注
關(guān)注
1文章
66瀏覽量
20273 -
存儲(chǔ)模塊
+關(guān)注
關(guān)注
0文章
14瀏覽量
8867
發(fā)布評論請先 登錄
相關(guān)推薦
評論