【ARM裸板】Nor Flash基礎知識與編程示例

示例代碼下載

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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章