日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

外设驱动库开发笔记23:AT24Cxx外部存储器驱动

發布時間:2024/7/23 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 外设驱动库开发笔记23:AT24Cxx外部存储器驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在我們的應用開發過程中,經常會使用到外部的EEPROM外部存儲器來保存一些參數和配置數據等。而比較常用的就是AT24Cxx系列產品,這一節我們來開發用于操作AT24Cxx系列產品的驅動。

1、功能概述

AT24Cxx系列EEPROM包括從1Kbit2Mbit的各種容量。AT24Cxx系列產品采用I2C總線數據傳送協議。盡管容量跨度很大,但它們都擁有相同的封裝和引腳排布,具體的引腳分配如下:

由于A0A1A2可以組成000~111八種情況,即通過器件地址輸入端A0A1A2可以實現將最多8個器件連接到同一條總線上,通過不同的配置進行器件的選擇。

對于AT24Cxx系列EEPROM不同的容量對地址的分配有較大差異,這涉及到設備地址和寄存器地址。從1K容量到2M容量寄存器地址分別采用718位來表示。16K及以下容量的EEPROM采用一個字節的寄存器地址配合設備地址段實現711位的寄存器地址尋址。而32k及以上的EEPROM采用兩個字節的寄存器地址配合設備地址段實現1218位的寄存器地址尋址。具體的地址分配如下:

從上表我們很容易明白,設備地址的低3位的定義決定了在同一條I2C總線上,最多可以掛載多少個AT24Cxx設備。有3位用于設備地址則最多可掛載8個設備;有2位用于設備地址則最多可掛載4個設備;有1位用于設備地址則最多可掛載2個設備;有0位用于設備地址則最多可掛載1個設備。需要注意的是,不同定義的位的設備混用于同一總線時,相同的定義位必須一樣,否則用作寄存器地址的位可能讓總線上的總線無法識別。

在一些AT24Cxx系列EEPROM型號中,帶有序列號的專用存儲單元。這些存儲單元不占用存儲器的存儲單元。序列號為128位,讀取序列號的設備地址以0xB0開頭,以區別于EEPROM存儲區域的讀取。

在一些AT24Cxx系列EEPROM型號中,除了帶有序列號的專用存儲單元外,還有帶有48位或者64位的MAC地址,固定在專用的存儲單元。這些單元不占用存儲器的存儲單元。讀取序列號和讀取MAC地址采用同樣的設備地址,均以0xB0開頭。有一些型號該區域并未用于MAC定制可用于用戶操作。

需要注意的是有些型號的AT24Cxx系列EEPROM存儲器的設備地址是固化的,需通過型號的后綴標識來識別。

2、驅動設計與實現

我們已經了解了AT24Cxx存儲器的基本功能及讀寫方式,接下來我們將開發操作AT24Cxx系列EEPROM存儲器的驅動程序。

2.1、對象定義

在使用一個對象之前我們需要獲得一個對象。同樣的我們想要AT24Cxx系列EEPROM存儲器就需要先定義AT24Cxx系列EEPROM存儲器的對象。

2.1.1、對象的抽象

我們要得到AT24Cxx系列EEPROM存儲器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下AT24Cxx系列EEPROM存儲器的對象。

先來考慮屬性,作為屬性肯定是用于標識或記錄對象特征的東西。我們來考慮AT24Cxx系列EEPROM存儲器對象屬性。首先AT24Cxx系列EEPROM存儲器采用的是I2C接口,對于每一個I2C接口元件都有一個設備地址用于區別總線上的設備,所以我們將I2C設備地址作為對象的屬性用以區別總線設備。AT24Cxx系列EEPROM存儲器存在多個型號對應不同的容量和特性,所以我們將其型號設置為對象的屬性以區別對象的類型。前面我們也說過,不同容量的AT24Cxx系列EEPROM存儲器由于尋址空間不同,所以寄存器地址長度也是不同的,所以我們將其地址長度作為屬性以區分處理。

接著我們還需要考慮AT24Cxx系列EEPROM存儲器對象的操作問題。我們需要對AT24Cxx系列EEPROM存儲器進行數據讀寫操作,無論讀寫其實都依賴于對I2C接口的操作,而這些操作基本都會依賴于具體的硬件平臺,所以我們將讀寫操作作為對象的操作。

根據上述我們對AT24Cxx系列EEPROM存儲器的分析,我們可以定義AT24Cxx系列EEPROM存儲器的對象類型如下:

typedef struct At24cObject {uint8_t devAddress;????????? //設備地址At24cModeType mode;?????????? //設備類型At24cMemAddLengthType memAddLength;?????????? //寄存器地址長度void (*Read)(struct At24cObject *at,uint16_t regAddress,uint8_t *rData,uint16_t rSize);?????? //讀數據操作指針void (*Write)(struct At24cObject *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize);??? //寫數據操作指針void (*Delayms)(volatile uint32_t nTime);?????? //毫秒延時操作指針 }At24cObjectType;

2.1.2、對象初始化

我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮AT24Cxx系列EEPROM存儲器對象的初始化函數。一般來說,初始化函數需要處理幾個方面的問題。一是檢查輸入參數是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據此我們設計AT24Cxx系列EEPROM存儲器對象的初始化函數如下:

/* 初始化AT24CXX對象 */ void At24cxxInitialization(At24cObjectType *at,??? //AT24CXX對象實體uint8_t devAddress,?? //AT24CXX設備地址At24cModeType mode,??? //AT24CXX對象類型At24cMemAddLengthType length,????? //寄存器地址長度At24cRead read,??????? //讀AT24CXX對象操作指針At24cWrite write,????? //寫AT24CXX對象操作指針At24cDelays delayms?????????? //延時操作指針) {if((at==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL)){return;}if((devAddress&0xF0)==0xA0){at->devAddress=devAddress;}else{at->devAddress=0x00;}at->mode=mode;at->memAddLength=length;at->Read=read;at->Write=write;at->Delayms=delayms; }

2.2、對象操作

我們已經完成了AT24Cxx系列EEPROM存儲器對象類型的定義和對象初始化函數的設計。但我們的主要目標是獲取對象的信息,接下來我們還要實現面向AT24Cxx系列EEPROM存儲器的各類操作。

2.2.1、寫單個字節

AT24Cxx系列EEPROM存儲器支持單字節寫數據,收到正確的設備地址和字地址字節后,EEPROM將發送一個確認。然后設備將準備接收8位數據字。在接收到8位數據字之后,EEPROM將返回一個ACK。然后,尋址設備(如總線主機)必須使用停止條件終止寫操作。此時,EEPROM將進入一個內部自動定時的寫周期,這個寫周期將在一定時間內完成,而數據字將被編程到非易失性EEPROM中。在這個寫周期中,所有的輸入都是禁用的,EEPROM在寫完成之前不會響應。

如果AT24Cxx對象是一個使用7位到11位地址表示寄存器地址的話,其數據格式如下圖所示:

如果AT24Cxx對象是一個使用12位到18位地址表示寄存器地址的話,其數據格式如下圖所示:

根據上述時序圖,我們可以編寫AT24Cxx系列EEPROM存儲器寫單個字節數據程序如下:

/*向AT24CXX寫入單個字節*/ void WriteByteToAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t data) {uint8_t temp;uint16_t regAdd;if(at->memAddLength==AT24C8BitMemAdd){regAdd=(uint16_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{regAdd=(uint16_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;at->Write(at,regAdd,&data,1); }

2.2.2、寫多個字節

AT24Cxx系列EEPROM存儲器支持多字節的寫操作,但對于AT24Cxx對象來說最多只支持寫到發送地址所在的頁尾,所以資料中也稱其為頁寫。而對于不同型號的AT24Cxx系列EEPROM存儲器每頁所包含的字節數是不一樣的,從8個字節到256個字節不等,我們需要注意寫入的字節數。

整頁寫的初始化方式與單字節寫的初始化方式相同,但是總線主機在第一個數據字被鎖定后不會發送停止條件。相反,在EEPROM承認接收到第一個數據字之后,總線主機可以傳輸最多到所在頁結尾的數據字。EEPROM將在接收到每個數據字后返回一個ACK。一旦所有要寫的數據都被發送到設備,總線主機必須發出一個停止條件此時內部的自計時寫周期將開始。字地址的下四位在接收到每個數據字后進行內部遞增,高階位地址位元不會增加。整頁寫操作僅限于在單個物理頁中寫入字節,而不管實際寫入的字節數。當增加的字地址到達頁面邊界時,地址計數器將滾動到同一頁面的開頭。這是必須要注意的,一旦繼續寫數據可能會將頁面中先前加載的數據無意中更改。

如果AT24Cxx對象是一個使用7位到11位地址表示寄存器地址的話,其寫多個字節的數據格式如下圖所示:

如果AT24Cxx對象是一個使用12位到18位地址表示寄存器地址的話,其寫多個字節的數據格式如下圖所示:

根據上述時序圖,我們可以編寫AT24Cxx系列EEPROM存儲器寫多個字節數據程序如下:

/*向AT24CXX寫入多個字節,從指定地址最多到所在頁的結尾*/ void WriteBytesToAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t *wData,uint16_t wSize) {uint16_t regAdd;uint8_t size;uint8_t temp;if(at->memAddLength==AT24C8BitMemAdd){regAdd=(uint16_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{regAdd=(uint16_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;if((wSize<=pageBytes[at->mode])&&(wSize<=(pageBytes[at->mode]-(regAddress&regAddMask[at->mode])))){size=wSize;}else{size=pageBytes[at->mode]-(regAddress&regAddMask[at->mode]);}at->Write(at,regAdd,wData,size); }

2.2.3、讀單個字節

AT24Cxx系列EEPROM存儲器支持單字節的讀操作。這一方式其實有兩種模式,讀當前位置和讀隨機位置。其實讀當前位置是讀隨機位置特例,我們考慮一般性則只考慮隨機讀取就可以了。隨機讀的開始方式與字節寫操作加載新數據字地址的方式相同。這就是所謂的偽寫序列;但是,必須省略數據字節和字節寫的停止條件,以防止該部分進入內部寫循環。一旦設備地址和字地址被鎖定并被EEPROM確認,總線主機必須生成另一個啟動條件。總線主機現在通過發送一個啟動條件來初始化一個讀取的當前地址,接著是一個有效的設備地址字節,其R/W位設置為邏輯“1”。之后EEPROM將對設備地址進行ACK處理,并在SDA線路上連續地輸出數據字。如果總線主機在第9個時鐘周期內沒有響應ACK,則所有類型的讀操作都將終止。在NACK響應之后,主進程可以發送一個停止條件來完成協議。

如果AT24Cxx對象是一個使用7位到11位地址表示寄存器地址的話,其讀單個字節的數據格式如下圖所示:

如果AT24Cxx對象是一個使用12位到18位地址表示寄存器地址的話,其讀單個字節的數據格式如下圖所示:

根據上述時序圖,我們可以編寫AT24Cxx系列EEPROM存儲器隨機讀取一個字節數據程序如下:

/*從AT24CXX讀取單個字節,從隨機地址讀取*/ uint8_t ReadByteFromAT24CXX(At24cObjectType *at,uint32_t regAddress) {uint8_t rData;uint16_t regAdd;uint8_t temp;if(at->memAddLength==AT24C8BitMemAdd){regAdd=(uint16_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{regAdd=(uint16_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;at->Read(at,regAdd,&rData,1);return rData; }

2.2.4、讀多個字節

AT24Cxx系列EEPROM存儲器支持多字節的讀操作,類似于單字節讀取,可以從當前位置開始讀也可以從指定地址開始讀。多字節讀取也稱為順序讀取由當前地址讀取或隨機讀取啟動。總線主接收到一個數據字后,它以ACK應答。只要EEPROM接收到ACK,它就會繼續增加字地址,并連續地計時輸出連續的數據字。當達到最大內存地址時,數據字地址將滾動,順序讀取將從內存數組的開頭開始。如果總線主機在第9個時鐘周期內沒有響應ACK,則所有類型的讀操作都將終止。在NACK響應之后,主進程可以發送一個停止條件來完成協議。

如果AT24Cxx對象是一個使用7位到11位地址表示寄存器地址的話,其讀多個字節的數據格式如下圖所示:

如果AT24Cxx對象是一個使用12位到18位地址表示寄存器地址的話,其讀多個字節的數據格式如下圖所示:

根據上述時序圖,我們可以編寫AT24Cxx系列EEPROM存儲器順序讀取多個字節數據程序如下:

/*從AT24CXX讀取多個字節,從指定地址最多到所在頁的結尾*/ void ReadBytesFromAT24CXX(At24cObjectType *at,uint32_t regAddress,uint8_t *rData,uint16_t rSize) {uint16_t regAdd;uint16_t size;uint8_t temp;if(at->memAddLength==AT24C8BitMemAdd){regAdd=(uint16_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{regAdd=(uint16_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[at->mode]>>1)))<<1;at->devAddress=(at->devAddress & devAddMask[at->mode])|temp;if((rSize<=pageBytes[at->mode])&&(rSize<=(pageBytes[at->mode]-(regAddress&regAddMask[at->mode])))){size=rSize;}else{size=pageBytes[at->mode]-(regAddress&regAddMask[at->mode]);}at->Read(at,regAdd,rData,size); }

3、驅動的使用

在上一節我們設計并實現了AT24Cxx系列EEPROM存儲器的驅動程序,而這一節我們將設計一個簡單的應用來驗證這一驅動程序。

3.1、聲明并初始化對象

使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的AT24Cxx系列EEPROM存儲器對象類型聲明一個AT24Cxx系列EEPROM存儲器對象變量,具體操作格式如下:

At24cObjectType at24c;

聲明了這個對象變量并不能立即使用,我們還需要使用驅動中定義的初始化函數對這個變量進行初始化。這個初始化函數所需要的輸入參數如下:

At24cObjectType *atAT24CXX對象實體

uint8_t devAddressAT24CXX設備地址

At24cModeType modeAT24CXX對象類型

At24cMemAddLengthType length,寄存器地址長度

At24cRead read,讀AT24CXX對象操作指針

At24cWrite write,寫AT24CXX對象操作指針

At24cDelayms delayms,延時操作指針

對于這些參數,對象變量我們已經定義了。對象類型與寄存器地址長度為枚舉,根據實際情況選擇就好了。設備地址根據我們的實際使用情況設置就可以了。主要的是我們需要定義幾個函數,并將函數指針作為參數。這幾個函數的類型如下:

/* 定義讀數據操作函數指針類型 */ typedef void (*At24cRead)(struct At24cObject *at,uint16_t regAddress,uint8_t *rData,uint16_t rSize);??????/* 定義寫數據操作函數指針類型 */ typedef void (*At24cWrite)(struct At24cObject *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize);???/* 定義延時操作函數指針類型 */ typedef void (*At24cDelayms)(volatile uint32_t nTime);??????

對于這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。片選操作函數用于多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數定義如下:

/*讀AT24C寄存器值*/ static void ReadDataFromAT24C(At24cObjectType *at24c,uint16_t regAddress,uint8_t *rData,uint16_t rSize) {uint16_t cSize;uint8_t cmd[2];if(at24c->memAddLength==AT24C8BitMemAdd){cSize=1;cmd[0]=(uint8_t)regAddress;}else{cSize=2;cmd[0]=(uint8_t)(regAddress>>8);cmd[1]=(uint8_t)regAddress;}HAL_I2C_Master_Transmit(&at24chi2c,at24c->devAddress,cmd,cSize,1000);HAL_I2C_Master_Receive(&at24chi2c,at24c->devAddress+1,rData, rSize, 1000); }/*寫AT24C寄存器值*/ static void WriteDataToAT24C(At24cObjectType *at24c,uint16_t regAddress,uint8_t *wData,uint16_t wSize) {uint8_t tData[wSize+2];uint16_t tSize;if(at24c->memAddLength==AT24C8BitMemAdd){tSize=wSize+1;tData[0]=(uint8_t)regAddress;}else{tSize=wSize+2;tData[0]=(uint8_t)(regAddress>>8);tData[1]=(uint8_t)regAddress;}for(int i=0;i<wSize;i++){tData[i+2]=wData[i];}HAL_I2C_Master_Transmit(&at24chi2c,at24c->devAddress,wData,wSize,1000); }

對于延時函數我們可以采用各種方法實現。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數。于是我們可以調用初始化函數如下:

At24cxxInitialization(&at24c,?????? //AT24CXX對象實體0xAE,?????????? //AT24CXX設備地址AT24C01C,??????? //AT24CXX對象類型AT24C8BitMemAdd,?????? //寄存器地址長度ReadDataFromAT24C,??? //讀AT24CXX對象操作指針WriteDataToAT24C, //寫AT24CXX對象操作指針HAL_Delay //延時操作指針);

3.2、基于對象進行操作

我們定義了對象變量并使用初始化函數給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數據。我們在驅動中已經將獲取數據并轉換為轉換值的比例值,接下來我們使用這一驅動開發我們的應用實例。

/*AT24XXX數據操作*/ void AT24CReadWriteData(void) {uint16_t regAddress=0x02;uint8_t readByte;uint8_t writeByte=0x0A;uint8_t rData[2];uint16_t rSize=2;uint8_t wData[]={0x5A,0xA5};uint16_t wSize=2;/*從AT24CXX讀取單個字節,從隨機地址讀取*/readByte=ReadByteFromAT24CXX(&at24c,regAddress);/*向AT24CXX寫入單個字節*/WriteByteToAT24CXX(&at24c,regAddress,writeByte);/*從AT24CXX讀取多個字節,從指定地址最多到所在頁的結尾*/ReadBytesFromAT24CXX(&at24c,regAddress,rData,rSize);/*向AT24CXX寫入多個字節,從指定地址最多到所在頁的結尾*/WriteBytesToAT24CXX(&at24c,regAddress,wData,wSize); }

4、應用總結

這一篇中,我們設計了AT24Cxx系列EEPROM存儲器的讀寫驅動,而且設計了一個簡單的應用驗證了驅動程序,讀寫操作都能按預期要求完成,而且操作也很穩定。

在使用驅動時我們需要注意,因為不同容量的AT24Cxx系列EEPROM存儲器的每一頁的字節數數不一樣的。在多字節讀寫時,最多支持到所在頁尾。到頁尾后,EEPROM存儲器的內部指針將回到頁首,此時執行讀則得到的是錯誤數據,若執行寫則會覆蓋原有數據造成錯誤。所以在程序中若讀寫的范圍超越了一頁的范圍將會被舍棄。

在使用驅動時還需注意,因為不同容量的AT24Cxx系列EEPROM存儲器的尋址范圍是不一樣的,所以用于表示寄存器地址的寄存器地址位數有1個字節和2個字節的差別,為了便于區分需要在對AT24Cxx系列EEPROM存儲器對象進行初始化時指定。

源碼下載:https://github.com/foxclever/ExPeriphDriver

歡迎關注:

?

總結

以上是生活随笔為你收集整理的外设驱动库开发笔记23:AT24Cxx外部存储器驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。