在我們的應(yīng)用開發(fā)過程中,經(jīng)常會(huì)使用到外部的EEPROM外部存儲(chǔ)器來保存一些參數(shù)和配置數(shù)據(jù)等。而比較常用的就是AT24Cxx系列產(chǎn)品,這一節(jié)我們來開發(fā)用于操作AT24Cxx系列產(chǎn)品的驅(qū)動(dòng)。
1 、功能概述
AT24Cxx系列EEPROM包括從1Kbit到2Mbit的各種容量。AT24Cxx系列產(chǎn)品采用I2C總線數(shù)據(jù)傳送協(xié)議。盡管容量跨度很大,但它們都擁有相同的封裝和引腳排布,具體的引腳分配如下:
由于A0、A1和A2可以組成000~111八種情況,即通過器件地址輸入端A0、A1和A2可以實(shí)現(xiàn)將最多8個(gè)器件連接到同一條總線上,通過不同的配置進(jìn)行器件的選擇。
對(duì)于AT24Cxx系列EEPROM不同的容量對(duì)地址的分配有較大差異,這涉及到設(shè)備地址和寄存器地址。從1K容量到2M容量寄存器地址分別采用7到18位來表示。16K及以下容量的EEPROM采用一個(gè)字節(jié)的寄存器地址配合設(shè)備地址段實(shí)現(xiàn)7到11位的寄存器地址尋址。而32k及以上的EEPROM采用兩個(gè)字節(jié)的寄存器地址配合設(shè)備地址段實(shí)現(xiàn)12到18位的寄存器地址尋址。具體的地址分配如下:
從上表我們很容易明白,設(shè)備地址的低3位的定義決定了在同一條I2C總線上,最多可以掛載多少個(gè)AT24Cxx設(shè)備。有3位用于設(shè)備地址則最多可掛載8個(gè)設(shè)備;有2位用于設(shè)備地址則最多可掛載4個(gè)設(shè)備;有1位用于設(shè)備地址則最多可掛載2個(gè)設(shè)備;有0位用于設(shè)備地址則最多可掛載1個(gè)設(shè)備。需要注意的是,不同定義的位的設(shè)備混用于同一總線時(shí),相同的定義位必須一樣,否則用作寄存器地址的位可能讓總線上的總線無法識(shí)別。
在一些AT24Cxx系列EEPROM型號(hào)中,帶有序列號(hào)的專用存儲(chǔ)單元。這些存儲(chǔ)單元不占用存儲(chǔ)器的存儲(chǔ)單元。序列號(hào)為128位,讀取序列號(hào)的設(shè)備地址以0xB0開頭,以區(qū)別于EEPROM存儲(chǔ)區(qū)域的讀取。
在一些AT24Cxx系列EEPROM型號(hào)中,除了帶有序列號(hào)的專用存儲(chǔ)單元外,還有帶有48位或者64位的MAC地址,固定在專用的存儲(chǔ)單元。這些單元不占用存儲(chǔ)器的存儲(chǔ)單元。讀取序列號(hào)和讀取MAC地址采用同樣的設(shè)備地址,均以0xB0開頭。有一些型號(hào)該區(qū)域并未用于MAC定制可用于用戶操作。
需要注意的是有些型號(hào)的AT24Cxx系列EEPROM存儲(chǔ)器的設(shè)備地址是固化的,需通過型號(hào)的后綴標(biāo)識(shí)來識(shí)別。
2 、驅(qū)動(dòng)設(shè)計(jì)與實(shí)現(xiàn)
我們已經(jīng)了解了AT24Cxx存儲(chǔ)器的基本功能及讀寫方式,接下來我們將開發(fā)操作AT24Cxx系列EEPROM存儲(chǔ)器的驅(qū)動(dòng)程序。
2.1 、對(duì)象定義
在使用一個(gè)對(duì)象之前我們需要獲得一個(gè)對(duì)象。同樣的我們想要AT24Cxx系列EEPROM存儲(chǔ)器就需要先定義AT24Cxx系列EEPROM存儲(chǔ)器的對(duì)象。
2.1.1 、對(duì)象的抽象
我們要得到AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象,需要先分析其基本特性。一般來說,一個(gè)對(duì)象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個(gè)方面思考一下AT24Cxx系列EEPROM存儲(chǔ)器的對(duì)象。
先來考慮屬性,作為屬性肯定是用于標(biāo)識(shí)或記錄對(duì)象特征的東西。我們來考慮AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象屬性。首先AT24Cxx系列EEPROM存儲(chǔ)器采用的是I2C接口,對(duì)于每一個(gè)I2C接口元件都有一個(gè)設(shè)備地址用于區(qū)別總線上的設(shè)備,所以我們將I2C設(shè)備地址作為對(duì)象的屬性用以區(qū)別總線設(shè)備。AT24Cxx系列EEPROM存儲(chǔ)器存在多個(gè)型號(hào)對(duì)應(yīng)不同的容量和特性,所以我們將其型號(hào)設(shè)置為對(duì)象的屬性以區(qū)別對(duì)象的類型。前面我們也說過,不同容量的AT24Cxx系列EEPROM存儲(chǔ)器由于尋址空間不同,所以寄存器地址長(zhǎng)度也是不同的,所以我們將其地址長(zhǎng)度作為屬性以區(qū)分處理。
接著我們還需要考慮AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象的操作問題。我們需要對(duì)AT24Cxx系列EEPROM存儲(chǔ)器進(jìn)行數(shù)據(jù)讀寫操作,無論讀寫其實(shí)都依賴于對(duì)I2C接口的操作,而這些操作基本都會(huì)依賴于具體的硬件平臺(tái),所以我們將讀寫操作作為對(duì)象的操作。
根據(jù)上述我們對(duì)AT24Cxx系列EEPROM存儲(chǔ)器的分析,我們可以定義AT24Cxx系列EEPROM存儲(chǔ)器的對(duì)象類型如下:
1 typedef struct At24cObject {
2 uint8_t devAddress; //設(shè)備地址
3 At24cModeType mode; //設(shè)備類型
4 At24cMemAddLengthType memAddLength; //寄存器地址長(zhǎng)度
5 void (*Read)(struct At24cObject *at,uint16_t regAddress,uint8_t *rData,uint16_t rSize); //讀數(shù)據(jù)操作指針
6 void (*Write)(struct At24cObject *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize); //寫數(shù)據(jù)操作指針
7 void (*Delayms)(volatile uint32_t nTime); //毫秒延時(shí)操作指針
8 }At24cObjectType;
2.1.2 、對(duì)象初始化
我們知道,一個(gè)對(duì)象僅作聲明是不能使用的,我們需要先對(duì)其進(jìn)行初始化,所以這里我們來考慮AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象的初始化函數(shù)。一般來說,初始化函數(shù)需要處理幾個(gè)方面的問題。一是檢查輸入?yún)?shù)是否合理;二是為對(duì)象的屬性賦初值;三是對(duì)對(duì)象作必要的初始化配置。據(jù)此我們?cè)O(shè)計(jì)AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象的初始化函數(shù)如下:
1 /* 初始化AT24CXX對(duì)象 */
2 void At24cxxInitialization(At24cObjectType *at, //AT24CXX對(duì)象實(shí)體
3 uint8_t devAddress, //AT24CXX設(shè)備地址
4 At24cModeType mode, //AT24CXX對(duì)象類型
5 At24cMemAddLengthType length, //寄存器地址長(zhǎng)度
6 At24cRead read, //讀AT24CXX對(duì)象操作指針
7 At24cWrite write, //寫AT24CXX對(duì)象操作指針
8 At24cDelayms delayms //延時(shí)操作指針
9 )
10 {
11 if((at==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL))
12 {
13 return;
14 }
15
16 if((devAddress&0xF0)==0xA0)
17 {
18 at->devAddress=devAddress;
19 }
20 else
21 {
22 at->devAddress=0x00;
23 }
24
25 at->mode=mode;
26 at->memAddLength=length;
27
28 at->Read=read;
29 at->Write=write;
30 at->Delayms=delayms;
31 }
2.2 、對(duì)象操作
我們已經(jīng)完成了AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象類型的定義和對(duì)象初始化函數(shù)的設(shè)計(jì)。但我們的主要目標(biāo)是獲取對(duì)象的信息,接下來我們還要實(shí)現(xiàn)面向AT24Cxx系列EEPROM存儲(chǔ)器的各類操作。
2.2.1 、寫單個(gè)字節(jié)
AT24Cxx系列EEPROM存儲(chǔ)器支持單字節(jié)寫數(shù)據(jù),收到正確的設(shè)備地址和字地址字節(jié)后,EEPROM將發(fā)送一個(gè)確認(rèn)。然后設(shè)備將準(zhǔn)備接收8位數(shù)據(jù)字。在接收到8位數(shù)據(jù)字之后,EEPROM將返回一個(gè)ACK。然后,尋址設(shè)備(如總線主機(jī))必須使用停止條件終止寫操作。此時(shí),EEPROM將進(jìn)入一個(gè)內(nèi)部自動(dòng)定時(shí)的寫周期,這個(gè)寫周期將在一定時(shí)間內(nèi)完成,而數(shù)據(jù)字將被編程到非易失性EEPROM中。在這個(gè)寫周期中,所有的輸入都是禁用的,EEPROM在寫完成之前不會(huì)響應(yīng)。
如果AT24Cxx對(duì)象是一個(gè)使用7位到11位地址表示寄存器地址的話,其數(shù)據(jù)格式如下圖所示:
如果AT24Cxx對(duì)象是一個(gè)使用12位到18位地址表示寄存器地址的話,其數(shù)據(jù)格式如下圖所示:
根據(jù)上述時(shí)序圖,我們可以編寫AT24Cxx系列EEPROM存儲(chǔ)器寫單個(gè)字節(jié)數(shù)據(jù)程序如下:
1 /*向AT24CXX寫入單個(gè)字節(jié)*/
2 void WriteByteToAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t data)
3 {
4 uint8_t temp;
5 uint16_t regAdd;
6
7 if(at->memAddLength==AT24C8BitMemAdd)
8 {
9 regAdd=(uint16_t)(regAddress&0xFF);
10 temp=(uint8_t)(regAddress>>8);
11 }
12 else
13 {
14 regAdd=(uint16_t)regAddress;
15 temp=(uint8_t)(regAddress>>16);
16 }
17 temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;
18 at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;
19
20 at->Write(at,regAdd,&data,1);
21 }
2.2.2 、寫多個(gè)字節(jié)
AT24Cxx系列EEPROM存儲(chǔ)器支持多字節(jié)的寫操作,但對(duì)于AT24Cxx對(duì)象來說最多只支持寫到發(fā)送地址所在的頁(yè)尾,所以資料中也稱其為頁(yè)寫。而對(duì)于不同型號(hào)的AT24Cxx系列EEPROM存儲(chǔ)器每頁(yè)所包含的字節(jié)數(shù)是不一樣的,從8個(gè)字節(jié)到256個(gè)字節(jié)不等,我們需要注意寫入的字節(jié)數(shù)。
整頁(yè)寫的初始化方式與單字節(jié)寫的初始化方式相同,但是總線主機(jī)在第一個(gè)數(shù)據(jù)字被鎖定后不會(huì)發(fā)送停止條件。相反,在EEPROM承認(rèn)接收到第一個(gè)數(shù)據(jù)字之后,總線主機(jī)可以傳輸最多到所在頁(yè)結(jié)尾的數(shù)據(jù)字。EEPROM將在接收到每個(gè)數(shù)據(jù)字后返回一個(gè)ACK。一旦所有要寫的數(shù)據(jù)都被發(fā)送到設(shè)備,總線主機(jī)必須發(fā)出一個(gè)停止條件此時(shí)內(nèi)部的自計(jì)時(shí)寫周期將開始。字地址的下四位在接收到每個(gè)數(shù)據(jù)字后進(jìn)行內(nèi)部遞增,高階位地址位元不會(huì)增加。整頁(yè)寫操作僅限于在單個(gè)物理頁(yè)中寫入字節(jié),而不管實(shí)際寫入的字節(jié)數(shù)。當(dāng)增加的字地址到達(dá)頁(yè)面邊界時(shí),地址計(jì)數(shù)器將滾動(dòng)到同一頁(yè)面的開頭。這是必須要注意的,一旦繼續(xù)寫數(shù)據(jù)可能會(huì)將頁(yè)面中先前加載的數(shù)據(jù)無意中更改。
如果AT24Cxx對(duì)象是一個(gè)使用7位到11位地址表示寄存器地址的話,其寫多個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
如果AT24Cxx對(duì)象是一個(gè)使用12位到18位地址表示寄存器地址的話,其寫多個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
根據(jù)上述時(shí)序圖,我們可以編寫AT24Cxx系列EEPROM存儲(chǔ)器寫多個(gè)字節(jié)數(shù)據(jù)程序如下:
1 /*向AT24CXX寫入多個(gè)字節(jié),從指定地址最多到所在頁(yè)的結(jié)尾*/
2 void WriteBytesToAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t *wData,uint16_t wSize)
3 {
4 uint16_t regAdd;
5 uint8_t size;
6 uint8_t temp;
7
8 if(at->memAddLength==AT24C8BitMemAdd)
9 {
10 regAdd=(uint16_t)(regAddress&0xFF);
11 temp=(uint8_t)(regAddress>>8);
12 }
13 else
14 {
15 regAdd=(uint16_t)regAddress;
16 temp=(uint8_t)(regAddress>>16);
17 }
18 temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;
19 at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;
20
21 if((wSize<=pageBytes[at->mode])&&(wSize<=(pageBytes[at->mode]-(regAddress®AddMask[at->mode]))))
22 {
23 size=wSize;
24 }
25 else
26 {
27 size=pageBytes[at->mode]-(regAddress®AddMask[at->mode]);
28 }
29
30 at->Write(at,regAdd,wData,size);
31 }
2.2.3 、讀單個(gè)字節(jié)
AT24Cxx系列EEPROM存儲(chǔ)器支持單字節(jié)的讀操作。這一方式其實(shí)有兩種模式,讀當(dāng)前位置和讀隨機(jī)位置。其實(shí)讀當(dāng)前位置是讀隨機(jī)位置特例,我們考慮一般性則只考慮隨機(jī)讀取就可以了。隨機(jī)讀的開始方式與字節(jié)寫操作加載新數(shù)據(jù)字地址的方式相同。這就是所謂的“偽寫”序列;但是,必須省略數(shù)據(jù)字節(jié)和字節(jié)寫的停止條件,以防止該部分進(jìn)入內(nèi)部寫循環(huán)。一旦設(shè)備地址和字地址被鎖定并被EEPROM確認(rèn),總線主機(jī)必須生成另一個(gè)啟動(dòng)條件??偩€主機(jī)現(xiàn)在通過發(fā)送一個(gè)啟動(dòng)條件來初始化一個(gè)讀取的當(dāng)前地址,接著是一個(gè)有效的設(shè)備地址字節(jié),其R/W位設(shè)置為邏輯“1”。之后EEPROM將對(duì)設(shè)備地址進(jìn)行ACK處理,并在SDA線路上連續(xù)地輸出數(shù)據(jù)字。如果總線主機(jī)在第9個(gè)時(shí)鐘周期內(nèi)沒有響應(yīng)ACK,則所有類型的讀操作都將終止。在NACK響應(yīng)之后,主進(jìn)程可以發(fā)送一個(gè)停止條件來完成協(xié)議。
如果AT24Cxx對(duì)象是一個(gè)使用7位到11位地址表示寄存器地址的話,其讀單個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
如果AT24Cxx對(duì)象是一個(gè)使用12位到18位地址表示寄存器地址的話,其讀單個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
根據(jù)上述時(shí)序圖,我們可以編寫AT24Cxx系列EEPROM存儲(chǔ)器隨機(jī)讀取一個(gè)字節(jié)數(shù)據(jù)程序如下:
1 /*從AT24CXX讀取單個(gè)字節(jié),從隨機(jī)地址讀取*/
2 uint8_t ReadByteFromAT24CXX(At24cObjectType *at,uint32_t regAddress)
3 {
4 uint8_t rData;
5 uint16_t regAdd;
6 uint8_t temp;
7
8 if(at->memAddLength==AT24C8BitMemAdd)
9 {
10 regAdd=(uint16_t)(regAddress&0xFF);
11 temp=(uint8_t)(regAddress>>8);
12 }
13 else
14 {
15 regAdd=(uint16_t)regAddress;
16 temp=(uint8_t)(regAddress>>16);
17 }
18 temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;
19 at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;
20
21 at->Read(at,regAdd,&rData,1);
22
23 return rData;
24 }
2.2.4 、讀多個(gè)字節(jié)
AT24Cxx系列EEPROM存儲(chǔ)器支持多字節(jié)的讀操作,類似于單字節(jié)讀取,可以從當(dāng)前位置開始讀也可以從指定地址開始讀。多字節(jié)讀取也稱為順序讀取由當(dāng)前地址讀取或隨機(jī)讀取啟動(dòng)??偩€主接收到一個(gè)數(shù)據(jù)字后,它以ACK應(yīng)答。只要EEPROM接收到ACK,它就會(huì)繼續(xù)增加字地址,并連續(xù)地計(jì)時(shí)輸出連續(xù)的數(shù)據(jù)字。當(dāng)達(dá)到最大內(nèi)存地址時(shí),數(shù)據(jù)字地址將滾動(dòng),順序讀取將從內(nèi)存數(shù)組的開頭開始。如果總線主機(jī)在第9個(gè)時(shí)鐘周期內(nèi)沒有響應(yīng)ACK,則所有類型的讀操作都將終止。在NACK響應(yīng)之后,主進(jìn)程可以發(fā)送一個(gè)停止條件來完成協(xié)議。
如果AT24Cxx對(duì)象是一個(gè)使用7位到11位地址表示寄存器地址的話,其讀多個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
如果AT24Cxx對(duì)象是一個(gè)使用12位到18位地址表示寄存器地址的話,其讀多個(gè)字節(jié)的數(shù)據(jù)格式如下圖所示:
根據(jù)上述時(shí)序圖,我們可以編寫AT24Cxx系列EEPROM存儲(chǔ)器順序讀取多個(gè)字節(jié)數(shù)據(jù)程序如下:
1 /*從AT24CXX讀取多個(gè)字節(jié),從指定地址最多到所在頁(yè)的結(jié)尾*/
2 void ReadBytesFromAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t *rData,uint16_t rSize)
3 {
4 uint16_t regAdd;
5 uint16_t size;
6 uint8_t temp;
7
8 if(at->memAddLength==AT24C8BitMemAdd)
9 {
10 regAdd=(uint16_t)(regAddress&0xFF);
11 temp=(uint8_t)(regAddress>>8);
12 }
13 else
14 {
15 regAdd=(uint16_t)regAddress;
16 temp=(uint8_t)(regAddress>>16);
17 }
18 temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;
19 at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;
20
21 if((rSize<=pageBytes[at->mode])&&(rSize<=(pageBytes[at->mode]-(regAddress®AddMask[at->mode]))))
22 {
23 size=rSize;
24 }
25 else
26 {
27 size=pageBytes[at->mode]-(regAddress®AddMask[at->mode]);
28 }
29
30 at->Read(at,regAdd,rData,size);
31 }
3 、驅(qū)動(dòng)的使用
在上一節(jié)我們?cè)O(shè)計(jì)并實(shí)現(xiàn)了AT24Cxx系列EEPROM存儲(chǔ)器的驅(qū)動(dòng)程序,而這一節(jié)我們將設(shè)計(jì)一個(gè)簡(jiǎn)單的應(yīng)用來驗(yàn)證這一驅(qū)動(dòng)程序。
3.1 、聲明并初始化對(duì)象
使用基于對(duì)象的操作我們需要先得到這個(gè)對(duì)象,所以我們先要使用前面定義的AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象類型聲明一個(gè)AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象變量,具體操作格式如下:
At24cObjectType at24c;
聲明了這個(gè)對(duì)象變量并不能立即使用,我們還需要使用驅(qū)動(dòng)中定義的初始化函數(shù)對(duì)這個(gè)變量進(jìn)行初始化。這個(gè)初始化函數(shù)所需要的輸入?yún)?shù)如下:
At24cObjectType *at,AT24CXX對(duì)象實(shí)體
uint8_t devAddress,AT24CXX設(shè)備地址
At24cModeType mode,AT24CXX對(duì)象類型
At24cMemAddLengthType length,寄存器地址長(zhǎng)度
At24cRead read,讀AT24CXX對(duì)象操作指針
At24cWrite write,寫AT24CXX對(duì)象操作指針
At24cDelayms delayms,延時(shí)操作指針
對(duì)于這些參數(shù),對(duì)象變量我們已經(jīng)定義了。對(duì)象類型與寄存器地址長(zhǎng)度為枚舉,根據(jù)實(shí)際情況選擇就好了。設(shè)備地址根據(jù)我們的實(shí)際使用情況設(shè)置就可以了。主要的是我們需要定義幾個(gè)函數(shù),并將函數(shù)指針作為參數(shù)。這幾個(gè)函數(shù)的類型如下:
1 /* 定義讀數(shù)據(jù)操作函數(shù)指針類型 */
2 typedef void (*At24cRead)(struct At24cObject *at,uint16_t regAddress,uint8_t *rData,uint16_t rSize);
3
4 /* 定義寫數(shù)據(jù)操作函數(shù)指針類型 */
5 typedef void (*At24cWrite)(struct At24cObject *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize);
6
7 /* 定義延時(shí)操作函數(shù)指針類型 */
8 typedef void (*At24cDelayms)(volatile uint32_t nTime);
對(duì)于這幾個(gè)函數(shù)我們根據(jù)樣式定義就可以了,具體的操作可能與使用的硬件平臺(tái)有關(guān)系。片選操作函數(shù)用于多設(shè)備需要軟件操作時(shí),如采用硬件片選可以傳入NULL即可。具體函數(shù)定義如下:
1 /*讀AT24C寄存器值*/
2 static void ReadDataFromAT24C(At24cObjectType *at24c,uint16_t regAddress,uint8_t *rData,uint16_t rSize)
3 {
4 uint16_t cSize;
5 uint8_t cmd[2];
6
7 if(at24c->memAddLength==AT24C8BitMemAdd)
8 {
9 cSize=1;
10 cmd[0]=(uint8_t)regAddress;
11 }
12 else
13 {
14 cSize=2;
15 cmd[0]=(uint8_t)(regAddress>>8);
16 cmd[1]=(uint8_t)regAddress;
17 }
18
19 HAL_I2C_Master_Transmit(&at24chi2c,at24c->devAddress,cmd,cSize,1000);
20
21 HAL_I2C_Master_Receive(&at24chi2c,at24c->devAddress+1,rData, rSize, 1000);
22 }
23
24 /*寫AT24C寄存器值*/
25 static void WriteDataToAT24C(At24cObjectType *at24c,uint16_t regAddress,uint8_t *wData,uint16_t wSize)
26 {
27 uint8_t tData[wSize+2];
28 uint16_t tSize;
29
30 if(at24c->memAddLength==AT24C8BitMemAdd)
31 {
32 tSize=wSize+1;
33 tData[0]=(uint8_t)regAddress;
34 }
35 else
36 {
37 tSize=wSize+2;
38 tData[0]=(uint8_t)(regAddress>>8);
39 tData[1]=(uint8_t)regAddress;
40 }
41
42 for(int i=0;i43 {
44 tData[i+2]=wData[i];
45 }
46
47 HAL_I2C_Master_Transmit(&at24chi2c,at24c->devAddress,wData,wSize,1000);
48 }
對(duì)于延時(shí)函數(shù)我們可以采用各種方法實(shí)現(xiàn)。我們采用的STM32平臺(tái)和HAL庫(kù)則可以直接使用HAL_Delay()函數(shù)。于是我們可以調(diào)用初始化函數(shù)如下:
1 At24cxxInitialization(&at24c, //AT24CXX對(duì)象實(shí)體
2 0xAE, //AT24CXX設(shè)備地址
3 AT24C01C, //AT24CXX對(duì)象類型
4 AT24C8BitMemAdd, //寄存器地址長(zhǎng)度
5 ReadDataFromAT24C, //讀AT24CXX對(duì)象操作指針
6 WriteDataToAT24C, //寫AT24CXX對(duì)象操作指針
7 HAL_Delay //延時(shí)操作指針
8 );
3.2 、基于對(duì)象進(jìn)行操作
我們定義了對(duì)象變量并使用初始化函數(shù)給其作了初始化。接著我們就來考慮操作這一對(duì)象獲取我們想要的數(shù)據(jù)。我們?cè)隍?qū)動(dòng)中已經(jīng)將獲取數(shù)據(jù)并轉(zhuǎn)換為轉(zhuǎn)換值的比例值,接下來我們使用這一驅(qū)動(dòng)開發(fā)我們的應(yīng)用實(shí)例。
1 /*AT24XXX數(shù)據(jù)操作*/
2 void AT24CReadWriteData(void)
3 {
4 uint16_t regAddress=0x02;
5 uint8_t readByte;
6 uint8_t writeByte=0x0A;
7 uint8_t rData[2];
8 uint16_t rSize=2;
9 uint8_t wData[]={0x5A,0xA5};
10 uint16_t wSize=2;
11
12 /*從AT24CXX讀取單個(gè)字節(jié),從隨機(jī)地址讀取*/
13 readByte=ReadByteFromAT24CXX(&at24c,regAddress);
14
15 /*向AT24CXX寫入單個(gè)字節(jié)*/
16 WriteByteToAT24CXX(&at24c,regAddress,writeByte);
17
18 /*從AT24CXX讀取多個(gè)字節(jié),從指定地址最多到所在頁(yè)的結(jié)尾*/
19 ReadBytesFromAT24CXX(&at24c,regAddress,rData,rSize);
20
21 /*向AT24CXX寫入多個(gè)字節(jié),從指定地址最多到所在頁(yè)的結(jié)尾*/
22 WriteBytesToAT24CXX(&at24c,regAddress,wData,wSize);
23 }
4 、應(yīng)用總結(jié)
這一篇中,我們?cè)O(shè)計(jì)了AT24Cxx系列EEPROM存儲(chǔ)器的讀寫驅(qū)動(dòng),而且設(shè)計(jì)了一個(gè)簡(jiǎn)單的應(yīng)用驗(yàn)證了驅(qū)動(dòng)程序,讀寫操作都能按預(yù)期要求完成,而且操作也很穩(wěn)定。
在使用驅(qū)動(dòng)時(shí)我們需要注意,因?yàn)椴煌萘康腁T24Cxx系列EEPROM存儲(chǔ)器的每一頁(yè)的字節(jié)數(shù)數(shù)不一樣的。在多字節(jié)讀寫時(shí),最多支持到所在頁(yè)尾。到頁(yè)尾后,EEPROM存儲(chǔ)器的內(nèi)部指針將回到頁(yè)首,此時(shí)執(zhí)行讀則得到的是錯(cuò)誤數(shù)據(jù),若執(zhí)行寫則會(huì)覆蓋原有數(shù)據(jù)造成錯(cuò)誤。所以在程序中若讀寫的范圍超越了一頁(yè)的范圍將會(huì)被舍棄。
在使用驅(qū)動(dòng)時(shí)還需注意,因?yàn)椴煌萘康腁T24Cxx系列EEPROM存儲(chǔ)器的尋址范圍是不一樣的,所以用于表示寄存器地址的寄存器地址位數(shù)有1個(gè)字節(jié)和2個(gè)字節(jié)的差別,為了便于區(qū)分需要在對(duì)AT24Cxx系列EEPROM存儲(chǔ)器對(duì)象進(jìn)行初始化時(shí)指定。
源碼下載:https://github.com/foxclever/ExPeriphDriver
評(píng)論
查看更多