文章目錄
示例代碼下載
1.NOR與NAND的區別
Flash | NOR | NAND |
---|---|---|
接口 | RAM-Like,引腳多 | 引腳少,複用(地址數據共用) |
容量 | 小(1-32M) | 大(128M+) |
讀 | 簡單 | 複雜 |
寫 | 發出特定命令(慢) | 發出特定命令(快) |
價格 | 貴 | 較便宜 |
缺點 | 無位反轉、壞塊 | 位反轉、壞塊 |
一般存放 | bootloader(關鍵程序) | 大文件、視頻 |
xip | 可以 | 不可以 |
- xip(eXecute In Place),即芯片內執行,指應用程序可以直接在flash閃存內運行,不必再把代碼讀到系統RAM中。flash內執行是指nor flash 不需要初始化,可以直接在flash內執行代碼。但往往只執行部分代碼,比如初始化RAM.
2.命令表
- UBOOT下讀寫數據
2.1 讀數據
md.b
爲讀命令mw.w
爲寫命令
md.b 0
2.2 讀ID
- Nor手冊
- 往(555H)寫入AAH
- 往(2AAH)寫入55H
- 往(555H)寫入90H
- 讀(0)得到廠家(Manifacture)ID:C2H
- 讀(1)得到設備(Device)ID:22C4/2249
但是由於地址是錯開1位的(具體原因可查看第15章),則往CPU寫的地址需要addr<<1,即(地址*2)因此:
- UBOOT下
- 往(AAAH)寫入AAH
- 往(554H)寫入55H (這兩步爲解鎖命令)
- 往(AAAH)寫入90H(90H爲命令)
- 讀(0H)得到廠家(Manifacture)ID:C2H
- 讀(2H)得到設備(Device)ID:22C4/2249
- 退出讀ID狀態(即復位)
mw.w aaa aa
mw.w 554 55
mw.w aaa 90
md.w 0 1 #讀0地址1次
md.w 2 1
mw.w 0 F0
2.3 CFI模式
CFI(Common Flash Interface)
- Nor手冊
- 往(55H)寫入98H(進入CFI模式)
- 讀(27H)得到容量2^n的n
- 退出CFI模式(復位)
- UBOOT下
- 往(AAH)寫入98H(進入CFI模式)
- 讀(4EH)得到容量2^n的n
- 往(0H)寫入F0H
3.基本函數
3.1 寫函數
- Nor Flash 地址線21:即可訪問2M內存,
0x1FFFFF
,其範圍地址爲 0~0x1FFFFF
#define NOR_FLASH_BASE 0 /* Nor Flash基地址 nor-->cs0,base_addr = 0 */
/* Nor Flash寫入一個字
* 基地址:base,偏移地址:offset,寫入的值:value
* eg: 55 98
* 往(0 + (0x55)<<1 )寫入0x98
*/
void nor_write_word(unsigned int base,unsigned int offset,unsigned short value)
{
volatile unsigned short *p = (volatile unsigned short *) (base + offset<<1);
*p = value;
}
- 進行封裝
void nor_cmd(unsigned int offset,unsigned short cmd)
{
nor_write_word(NOR_FLASH_BASE, offset, cmd);
}
3.2 讀函數
/* Nor Flash讀取一個字
* 基地址:base,偏移地址:offset
* eg: 55 98
* 往(0 + (0x55)<<1 )寫入0x98
*/
unsigned int nor_read_word(unsigned int base,unsigned int offset)
{
volatile unsigned short *p = (volatile unsigned short *) (base + offset<<1);
return *p;
}
- 進行封裝
unsigned int nor_dat(unsigned int offset)
{
return nor_read_word(NOR_FLASH_BASE, offset);
}
4.識別NOR
4.1 讀取ID號
- 往(555H)寫入AAH
- 往(2AAH)寫入55H
- 往(555H)寫入90H
- 讀(0)得到廠家(Manifacture)ID:C2H
- 讀(1)得到設備(Device)ID:22C4/2249
/* 1.打印 Manifacture ID、Device ID */
nor_unlock(); //解鎖
nor_cmd(0x555,0x90);//讀命令
manifa_id = nor_dat(0x00); //讀廠家ID
device_id = nor_dat(0x01); //讀設備ID
nor_reset_mode(); //復位
4.2 進入CFI Mode
- 往(55H)寫98H
- CFI(Common Flash Interface)
/* 1.進入CFI Mode */
nor_cmd(0x55,0x98);
4.3 讀取容量
- 讀(27H)得到容量bytes
/* 2.打印容量 */
size = 1<<(nor_dat(0x27));
printf("nor size = 0x%x, %dM\r\n",size,size>>20);
4.4 讀取各個扇區
4.4.1 獲得region數量
regions = nor_dat(0x2C);
4.4.2 region詳細信息
erase block region :裏面含有1個或多個block,它們大小都一樣,一個nor含有1個或多個region,一個region含有1個或多個block(扇區)
- Erase block region information:
前2字節+1
:表示該region有多少個block後2字節*256
:表示該block的大小(bytes)
參考CFI標準:
- regions的基址從
0x2D
開始
region_info_base = 0x2D;
block_addr = 0;
for(i = 0;i < regions; i++){
/* 獲取block的數量和大小 */
blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);//低2字節,獲取block數量
block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));//高2字節,獲取block大小
region_info_base += 4;//region讀取的基址+4
/* 打印每個block的起始地址 */
for(j = 0;j < blocks; j++){
printf("0x%08x ",block_addr);
block_addr += block_size;
if( ((++cnt) % 5) == 0){ //每打印5個換行
printf("\r\n");
}
}
}
4.5 退出CFI Mode
nor_cmd(0x00,0xF0);//復位
5.寫數據
5.1 寫入
需要注意一個點,寫入的數據是16位的,也就是兩個字節,需要一次性寫入一個字節,所以在寫入之前需要對數據進行整合。
while(str[i] && str[i+1]){//兩個字符都不爲0時
data = str[i] + (str[i+1]<<8);
nor_unlock();//解鎖
nor_cmd(0x555,0xA0); //寫命令
nor_cmd(addr>>1,data);
/* 等待燒寫完成:讀數據Q6,無變化時表示完成*/
wait_ready(addr);
i += 2;
addr += 2;
}
5.2 判斷數據寫入完成
- 等待燒寫完成:讀數據Q6,無變化時表示完成
- 兩次讀取的結果不一致,說明數據還在變化,繼續等待
//等待讀取或擦除完畢
void wait_ready(unsigned int addr)
{
unsigned int pre_val;//上一次的值
unsigned int cur_val;
pre_val = nor_dat(addr>>1);
cur_val = nor_dat(addr>>1);
/* 兩次讀取的結果不一致,說明數據還在變化,繼續等待 */
while((cur_val & (1<<6) != (pre_val &(1<<6)))){//當前的Q6不等於上一次的Q6則等待
pre_val = cur_val;//更新上一次的值
val = nor_dat(addr>>1);//重新獲取
}
}
6.測試
6.1 讀取
6.2 寫入
6.3 擦除
7.問題
7.1 系統異常卡死
- 執行多次的菜單選擇,導致系統卡死
- 該爲定時器造成的異常錯誤,關閉定時器,則不會卡死
- 因爲當測試nor進入CFI模式時,如果發生了中斷,CPU必定讀NOR,那麼讀不到正確的指令,導致程序異常崩潰
7.2 nor數據錯誤
-
讀取設備ID時,讀到的是
0x002f
,0xea00
,改爲反彙編中text的[0]上的數據 -
編譯程序加上選項:指定ARM版本指令集
-march=armv4
或者指定芯片類型-mcpu=arm9tdmi
否則像如下的寫入操作會被分成兩個strb步驟(我們需要的是strh,一次性寫入兩個字節),最後導致讀取設備ID和廠家ID的時候出錯。
volatile unsigned short *p =value;
*p = value;
- 沒加編譯指定選項(分兩次存2個字節)
- 加了編譯指定芯片類型
-mcpu=arm9tdmi
(一次性存2個字節)
8.擦除扇區
void erase_nor_flash(void)
{
unsigned int addr;
/* 獲得地址*/
printf("Enter the address of sector to erase:");
addr = get_uint();
printf("Erase...\r\n");
nor_unlock();
nor_cmd(0x555,0x80); //擦除扇區命令
nor_unlock();
nor_cmd(addr>>1,0x30); //發出扇區地址
wait_ready(addr);
}