1.讀芯片ID
1.1 讀芯片ID時序
簡化為4個步驟:
1.使能片選
2.寫命令0x90
3.寫地址0x00
4.讀ID信息
/* 識別NAND FLASH */
void scan_nand_flash(void)
{
int i;
//保存讀取ID信息的數組
unsigned char id_info[5] = {0};
nand_enable_cs();//使能CS
nand_write_cmd(0x90);
nand_write_addr(0x00);
for(i = 0;i < 5;i++){
id_info[i] = nand_read_data();
}
nand_disable_cs();//禁止CS
printf("Maker Code: 0x%xrn",id_info[0]);
printf("Device Code: 0x%xrn",id_info[1]);
printf("3rd cycle: 0x%xnr",id_info[2]);
printf("Page size: %d KBnr",1 << (id_info[3] & 0x03));//頁大小與id_info[3]最低2位有關
printf("Block size: %d KBnr",64 << ((id_info[3] >> 4) & 0x03));//塊大小與id_info[3]第4、5位有關
printf("5th cycle: 0x%xnr",id_info[4]);
1.2 由ID數據獲得芯片參數
ID信息的第4字節為0x95
頁大小與id_info[3]最低2位有關,可得頁大小為:2KB
塊大小與id_info[3]第4、5位有關,可得塊大小為:128KB
注意:如果此時燒寫到Nand Flash,并從Nand Flash啟動程序是不會成功的,因為這個bin文件大小已經超過了4K,且現在還沒有實現nand flash的讀函數。
2.讀數據
目標:實現從NAND FLASH中啟動,重定位所有數據至SDRAM,并實現讀取芯片ID數據
2.1 NAND內部結構分析
OOB:out of bank(在bank之外)
由上圖可得:
1Page = 2KB + 64B
1Block = 64 * Pages = 128KB + 4KB
1Device = 2048 * Blocks = 256MB + 8MB
OOB區的作用:因為nand的缺點是會發生“位反轉”,為了解決這個問題,nand中的OOB區,用于校驗數據區的數據是否發生錯誤,當有錯誤時,可以恢復數據。(其本身不存儲數據)
因為OOB中并不存放數據,只是用于校驗數據區是否發生錯誤,因此當CPU讀取Nand Flash第2048個數據,該數據為 Page1中的第0個byte
2.2 地址序列與時序
由地址序列可以看出:發出地址信號共需5個周期,前2個周期發出列地址(Column Address),后3個周期發出行地址(Row Address)
地址線序列有一些位是沒有用到的,其目的也是以后兼容更大芯片的nand falsh
Nand Flash內部結構展開大致如下:
2.3 讀數據流程
1.發出片選信號
2.發出0x00命令
3.發送5個周期的地址(兩個列地址,三個行地址(page))
4.再發送0x30命令
5.等待就緒
6.讀數據
7.禁止片選
2.4 轉換所讀地址的列與頁
將輸入的地址addr轉換為:
列地址(Col Address)
page是定位到哪一個頁,col變量定位的就是在這個頁的偏移量(在這個頁上的第幾列0~2047)
行地址(頁)
因為讀取數據的時候是一次性讀出一頁,因此當給出地址addr之后,每一頁的數據大小是2K,因此我們可以根據地址知道我們讀取的數據是哪一個頁
int col = addr % 2048;//列地址 addr &(2048-1);
int page = addr / 2048;//行地址(相當于頁地址)
2.5 NAND等待就緒
當NFSATA[0] = 0時,表示正忙
當NFSATA[0] = 1時,表示就緒
/* 等待NAND就緒 */
void nand_wait_ready(void)
{
while(!(NFSTAT & 0x01));//當NFSATA[0] = 1時,表示就緒
}
2.6 讀取數據函數
/* NAND FLASH讀取數據
* param:讀取的地址、存放的地址、讀取的長度
*/
void read_nand_data(unsigned int addr,unsigned char *buf,unsigned int len)
{
int i = 0;
/*page是定位到哪一個頁,col變量定位的就是在這個
*頁的偏移量(在這個頁上的第幾列0~2047)
*/
int col = addr % 2048;//列地址 addr &(2048-1);
/* 因為讀取數據的時候是一次性讀出一頁,因此當給出
* 地址addr之后,每一頁的數據大小是2K,因此我們可以
* 根據地址知道我們讀取的數據是哪一個頁
*/
int page = addr / 2048;//行地址(相當于頁地址)
nand_enable_cs();//1.使能CS
while(i < len){
nand_write_cmd(0x00); //2.發出0x00命令
/* col addr */
nand_write_addr(col & 0xFF);//3.發出地址
nand_write_addr((col >> 8) & 0xFF);
/* row/page addr */
nand_write_addr(page & 0xFF);
nand_write_addr((page >> 8) & 0xFF);
nand_write_addr((page >> 16) & 0xFF);
nand_write_cmd(0x30);//4.發出0x30命令
nand_wait_ready(); //5.等待就緒
/* for循環中有2個條件
* 1.當讀到頁尾,但還是沒有讀完,說明需要讀取下一頁
* 2.當已經讀取指定字節數,則不再讀取
*/
for(; (col < 2048) && (i < len); col++){//6.讀數據
buf[i++] = nand_read_data_byte();
}
if(i == len){
break;
}
col = 0;
page++;//指向下一頁
}
nand_disable_cs(); //7.禁止CS
}
2.7 NAND重定位
從Nand Flash啟動,此時片內SRAM的地址對應的就是CPU的0地址,如果從Nand Flash啟動,2440硬件會把nand Flash前4K的數據復制到片內SRAM,如果Nand Flash上的程序大于4K,那后續數據就會丟失,相當于只重定位了前4K的代碼。
如何解決上述問題:
1.前提:實現了NAND FLASH讀取數據函數
2.代碼燒寫到NAND FLASH,并從NAND中啟動
3.程序運行到重定位代碼的位置判斷一下,是從Nand Flash啟動還是NOR Flash啟動(通過往0地址寫數據,因為Nand是支持讀寫的,所以讀出的結果和寫的結果一樣,而NOR Flash不能像內存一樣讀寫,因此讀寫的內容是不一致的)
4.如果從NOR Flash啟動,直接使用簡單的重定位代碼就行,如果是Nand Flash啟動,那就是用Nnad Flash的讀函數進行代碼的重定位。
首先判斷從NorFlash or NandFlash中啟動
/* 檢查是否從NorFlash中啟動
* 方法:寫0x12345678到0地址,在讀取出來,如果得到0x12345678,表示0地址上的內容被修改,即為片內RAM,則為nand啟動
* 原因:原因:nor不能直接寫入,寫入需要發出一定格式的數據,才能寫入
* 返回0為nand啟動,返回1為nor啟動
*/
int isBootFromNorFlash(void)
{
volatile unsigned int *p = (volatile unsigned int *)0;
unsigned int val = *p;//暫存[0]上的數據
*p = 0xdeadc0de;//dead code任意值
if(0xdeadc0de == *p){
/* 寫成功,對應nand啟動 */
*p = val;//恢復原來的值
return 0;
}
else{
return 1;
}
}
重定位代碼
/*
* 將除bss段的全部數據拷貝到sdram中
* 傳遞形參,原地址src:_start 目標地址dest:__bss_start 長度len:__bss_start-_star
*/
void copy_to_sdram(void)
{
/* 要從lds文件中獲取__code_start、__bss_start
* 然后從0地址把數據復制到__code_start
*/
extern int __code_start,__bss_start; //聲明外部變量
volatile unsigned int *src = (volatile unsigned int *)0; //flash中0地址
volatile unsigned int *dest = (volatile unsigned int *)&__code_start; //目標地址:sdram中的0x30000000地址
volatile unsigned int *end = (volatile unsigned int *)&__bss_start; //結束地址:bss的起始地址
int len = (int)&__bss_start - (int)&__code_start;//獲取數據總長度
if(isBootFromNorFlash()){//如果從Nor中啟動
while(dest < end){
*dest++ = *src++; //拷貝
}
}
else{//從Nand中啟動,需要先初始化nand,然后重定位代碼
nand_init();
//從 src 復制到 des ,總共復制len字節,也就是重定位的代碼
read_nand_data((unsigned int)src,(unsigned char *)dest,len);
}
}
2.7 讀數據測試
讀取0地址后160bytes的數據,如果跟.bin文件前160字節數據相同,則讀取成功,否則讀取失敗
/* 測試函數:讀數nand上160bytes數據
*/
void read_nand_flash(void)
{
int i,j;
unsigned int addr,hex_addr;
unsigned char c,str[16],data[160];
volatile unsigned char *p;
/* 獲得地址 */
printf("*****Enter the address to read:");
addr = get_uint();
read_nand_data(addr,data,160);//獲取到地址上的數據
p = (volatile unsigned char *)data;//p指向data,用于打印data數據
hex_addr = addr;//起始地址
printf("Read Data:rn");
printf(" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0fnr");
/* 長度固定為160 bytes */
for(i = 0; i < 10; i++){
printf("0x%08x ",hex_addr);
//每行打印16個16進制數
for(j = 0; j < 16; j++){
c = *p++;//讀取16個
str[j] = c;//保存字符
//先打印數值
printf("%02x ",c);
}
printf(" | ");
//后打印字符
for(j = 0; j < 16; j++){
if(str[j] < 0x20 || str[j] > 0x7e){//不可視字符,打印‘.’
printf(".");
}
else{
printf("%c",str[j]);
}
}
hex_addr+=16;//換行+16
printf("nr");
}
}
由上測試可知讀取成功
3.擦除
3.1 擦除時序
1.發出片選信號
2.發出0x60命令
3.發送3個行地址(page)
4.再發送0xD0命令
5.等待就緒(等待擦除完成)
6.禁止片選
3.2 地址和參數合法性
由于按塊來擦除(128KB),因此地址和參數都必須是128K的倍數
/* 檢查參數合法性 */
if(addr & (0x1FFFF)){ //128K
printf("Err addr! Please enter an integral multiple of 128Krn");
return -1;
}
if(len & (0x1FFFF)){ //128K
printf("Err len! Please enter an integral multiple of 128Krn");
return -1;
}
3.3 擦除函數
/* NAND FLASH擦除數據
* param:擦除的起始地址、擦除的長度(byte)
* ret:失敗-1,成功0
*/
int erase_nand_data(unsigned int addr,unsigned int len)
{
int page = addr / 2048;//行地址
/* 檢查參數合法性 */
if(addr & (0x1FFFF)){ //128K
printf("Err addr! Please enter an integral multiple of 128Krn");
return -1;
}
if(len & (0x1FFFF)){ //128K
printf("Err len! Please enter an integral multiple of 128Krn");
return -1;
}
/* 1.使能CS */
nand_enable_cs();
while(1)
{
page = addr / 2048;//行地址
/* 2.發出0x60命令 */
nand_write_cmd(0x60);
/* 3.發出地址row/page addr */
nand_page(page);
/* 4.發出0xD0命令 */
nand_write_cmd(0xD0);
/* 5.等待就緒(等待擦除完成) */
nand_wait_ready();
len -= (128*1024);//長度減去一個block
if(0 == len){
break;
}
addr += (128*1024);//指向下一塊
}
/* 6.禁止CS */
nand_disable_cs();
return 0;
}
3.4 擦除數據測試
/* 擦除測試函數:固定擦除一個1block = 128K
*/
void erase_nand_flash(void)
{
int addr;
unsigned int whichblock;
/* 獲得第幾個Block */
printf("Enter the address of sector to erase: ");
addr = get_uint();
whichblock = addr / (128*1024);
printf("***** block number : [ %d ]rn",whichblock);
/* 提示擦除數據的范圍 */
printf("***** Erase range : 0x%08x - 0x%08xnr",addr,(addr+(128*1024)));
printf("***** erase ...rn");
if(erase_nand_data(addr,128*1024) == 0){//如果擦除成功
printf("***** Erase finished!rn");
}
else{
printf("***** Erase fail!rn");
}
}
4.寫數據
4.1 寫數據時序
1.發出片選信號
2.發出0x80命令
3.發送5個周期的地址(兩個列地址,三個行地址(page))
4.寫入數據
5.再發送0x10命令
6.等待就緒(等待擦除完成)
7.禁止片選
4.2 寫數據函數
/* NAND FLASH寫入數據
* param:寫入的地址、數據指針、寫入的長度
*/
void write_nand_data(unsigned int addr,unsigned char *buf,unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
/* 1.使能CS */
nand_enable_cs();
while(1){
/* 2.發出0x80命令 */
nand_write_cmd(0x80);
/* 3.發出地址 */
nand_col(col);
nand_page(page);
/* 4.寫入數據*/
for(; (col < 2048) && (i < len); ){
nand_write_data_byte(buf[i++]);
}
/* 5.發出0x10命令 */
nand_write_cmd(0x10);
/* 6.等待就緒(等待寫入完成) */
nand_wait_ready();
if(i == len){
break;
}
else{
col = 0;
page++;
}
}
/* 7.禁止CS */
nand_disable_cs();
}
4.3 寫數據測試
此處注意:一般在燒寫數據之前需要對數據進行擦除操作,除非原本的數據全f,否則都需要進行擦除,不然寫入的數據會有問題。
void write_nand_flash(void)
{
unsigned int addr;
unsigned char str[50];
unsigned int len;
/* 獲得第幾個Block */
printf("***** Enter addr to write: ");
addr = get_uint();
printf("***** Enter the string to write: ");
gets(str);
len = strlen(str) + 1;
printf("***** write range : 0x%08x - 0x%08xrn",addr,(addr + len));
printf("***** writing ...rn");
write_nand_data(addr,str,strlen(str)+1);//strlen不包括結束符' 主站蜘蛛池模板: 新平| 静宁县| 酉阳| 饶平县| 易门县| 翁牛特旗| 曲阳县| 临澧县| 酉阳| 司法| 天台县| 长沙市| 长武县| 秦皇岛市| 福安市| 普宁市| 五大连池市| 大渡口区| 秦皇岛市| 收藏| 乐安县| 南江县| 普安县| 新营市| 连平县| 临沧市| 济宁市| 淳化县| 西畴县| 枣庄市| 通河县| 天祝| 甘谷县| 中山市| 凌源市| 疏勒县| 宁南县| 敦煌市| 信阳市| 法库县| 德令哈市|