Cortex-M3 (NXP LPC1788)之EEPROM儲存器
技術標籤:LPC1788
EEPROM是一種非易失性儲存器,主要用於儲存相對少量的資料,如儲存一些系統的配置資訊。通過系統的EEPROM控制模組可以輕鬆的進行EERPOM的儲存控制。
要正確使用EEPROM需要配置掉電暫存器EEPWRDWN確定EEPROM的工作模式,配置EEPROM時鐘分頻器暫存器,使EEPROM工作在375KHZ。下面對EEPROM的讀和寫資料進行介紹。
EEPROM儲存器的訪問有三種操作方式:讀、寫、擦除/程式設計。對EEPROM中寫資料分成兩個單獨的操作:寫和擦除/程式設計。第一步寫操作並不是真正把資料寫入EEPROM的儲存介質中,而只是更新被稱作“頁暫存器”的臨時資料暫存器。只有執行下一步”擦除/程式設計“操作才會真正地更新非易失儲存器。LPC1788共有4K的片內EEPROM,其中EEPROM的每一頁等於頁暫存器大小為64Byte,總共有64頁。大小正好是64*64=4096Byte=4K。首先我們指定的位數,如8、16或者32位,將資料寫入頁暫存器,頁內的地址偏移由EEPROM地址暫存器的第6位決定。如果需要往頁暫存器寫入一串資料,寫完第一個資料後,頁內的偏移地址會自動增加,我們只需把要操作的下一個資料填入EEPROM寫資料暫存器即可。當頁暫存器的64個位元組被寫滿,必須進行程式設計操作,將資料寫入到EEPROM的儲存介質。然後更新頁暫存器的偏移地址。
對於EEPROM的讀操作,首先要向地址暫存器寫入一個12位的地址,高6位為頁的偏移,即我們需要讀哪個頁暫存器。低6位為頁內的偏移,即我們需要讀當前頁的哪個位元組。讀操作也可以自動的對地址暫存器做後遞增,這樣就可以對EEPROM儲存器進行連續的操作而無需每次讀資料都寫入一個新地址。
下面的程式將資訊寫入EEPROM後讀出通過串列埠列印顯示:
#include "LPC1788_REG.h"
#include "uart.h"
#define rEECMD (*(volatile unsigned*)(0x00200080))
#define rEEADDR (*(volatile unsigned*)(0x00200084))
#define rEEWDATA (*(volatile unsigned*)(0x00200088))
#define rEERDATA (*(volatile unsigned*)(0x0020008C))
#define rEEWSTATE (*(volatile unsigned*)(0x00200090))
#define rEECLKDIV (*(volatile unsigned*)(0x00200094))
#define rEEPWRDWN (*(volatile unsigned*)(0x00200098))
#define rEEINTEN (*(volatile unsigned*)(0x00200FE4))
#define rEEINTCLR (*(volatile unsigned*)(0x00200FD8))
#define rEEINTSET (*(volatile unsigned*)(0x00200FDC))
#define rEEINTSTAT (*(volatile unsigned*)(0x00200FE0))
#define rEEINTSTATCLR (*(volatile unsigned*)(0x00200FE8))
#define rEEINTSTATSET (*(volatile unsigned*)(0x00200FEC))
#define EEPROM_PAGE_SIZE 64
void Init_EEPROM(void);
void Write_EEPROM(unsigned char page_num, unsigned char page_offset, char* data, unsigned int count);
void Read_EEPROM(unsigned char page_num, unsigned char page_offset, char* data, unsigned int count);
char read_buffer[];
char write_buffer[] = {"\n\r\
\t - Name: Nuncle.lee \n\r\
\t - QQ: 23610603 \n\r\
\t - Email: [email protected]\n\r"};
int main(void)
{
Init_Uart2();
Init_EEPROM();
Write_EEPROM(0, 0, write_buffer, sizeof(write_buffer));
Write_EEPROM(sizeof(write_buffer)/64, sizeof(write_buffer)%64, '\0', 1);
Read_EEPROM(0, 0, read_buffer, sizeof(write_buffer)+1);
Uart2SendS(read_buffer);
return 0;
}
void Init_EEPROM(void)
{
unsigned int val=0;
rEEPWRDWN = 0; //不處於掉電模式
rEECLKDIV = (CCLK/375000)-1; //設定一個375KHZ的EEPROM時鐘
val = ((((CCLK / 1000000) * 15) / 1000) + 1); //配置等待狀態時間
val |= (((((CCLK / 1000000) * 55) / 1000) + 1) << 8);
val |= (((((CCLK / 1000000) * 35) / 1000) + 1) << 16);
rEEWSTATE = val;
}
void Write_EEPROM(unsigned char page_num, unsigned char page_offset, char* data, unsigned int count)
{
unsigned int i;
rEEADDR = (page_offset&0x3f); //確定開始寫的頁內偏移地址
for(i=0; i<count; i++)
{
rEECMD = 0x3; //8位寫操作
rEEWDATA = *(data+i);
while(!(rEEINTSTAT & (0x1<<26))); //等待寫操作完成
page_offset++;
if(page_offset >= EEPROM_PAGE_SIZE) //如果當前頁寫滿了64Byte 則啟動 程式設計操作
{
rEEINTSTATCLR = 0x1<<28;
rEEADDR = (page_num&0x3F)<<6;
rEECMD = 0x6; //擦除程式設計
while(!(rEEINTSTAT & (0x1<<28))); //等待程式設計完成
page_offset = 0;
page_num++; //寫滿一頁,準備寫下一頁
rEEADDR = 0; //寫滿一頁,從頁的開始寫
}
else if(i==count-1) //如果要寫入EPPROM的資料寫完成,啟動程式設計操作
{
rEEINTSTATCLR = 0x1<<28;
rEEADDR = (page_num&0x3F)<<6;
rEECMD = 0x6; //擦除程式設計
while(!(rEEINTSTAT & (0x1<<28)));
}
}
}
void Read_EEPROM(unsigned char page_num, unsigned char page_offset, char* data, unsigned int count)
{
unsigned int i;
rEEADDR = (page_num&0x3F)<<6|page_offset&0x3F; //確定讀資料的起始位置,包括頁偏移和頁內的偏移
rEECMD = 0x1<<3; //8位讀,地址自動遞增模式
for(i=0; i<count; i++)
{
while(!(rEEINTSTAT & (0x1<<26))); //等待上一次讀資料完成
*(data+i) = rEERDATA;
page_offset++;
if(page_offset >= EEPROM_PAGE_SIZE) //如果當前頁64Byte讀完,準備讀下一頁
{
page_offset = 0;
page_num++;
rEEADDR = (page_num&0x3F)<<6|page_offset&0x3F;
rEECMD = 0x1<<3;
}
}
}
程式的執行結果如下圖
程式中,sizeof關鍵字計算出字元陣列的大小,並不包含字串的結束標誌‘\0’。因此還需在sizeof(write_buffer)的位置寫入字串的結束標誌。讀取時讀sizeof(write_buffer)+1個位元組,才能正確的通過串列埠列印資料。當然,也可以自己指定串列埠列印字元的個數,而不用字串結束標誌去判斷。