简化为4个步骤:web
/* 识别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%x\r\n",id_info[0]); printf("Device Code: 0x%x\r\n",id_info[1]); printf("3rd cycle: 0x%x\n\r",id_info[2]); printf("Page size: %d KB\n\r",1 << (id_info[3] & 0x03));//页大小与id_info[3]最低2位有关 printf("Block size: %d KB\n\r",64 << ((id_info[3] >> 4) & 0x03));//块大小与id_info[3]第四、5位有关 printf("5th cycle: 0x%x\n\r",id_info[4]);
0x95
2KB
128KB
注意:若是此时烧写到Nand Flash,并从Nand Flash启动程序是不会成功的,由于这个bin文件大小已经超过了4K,且如今尚未实现nand flash的读函数。算法
目标:实现从NAND FLASH中启动,重定位全部数据至SDRAM,并实现读取芯片ID数据数组
“位反转”
,为了解决这个问题,nand中的OOB区,用于校验数据区的数据是否发生错误,当有错误时,能够恢复数据。(其自己不存储数据)Page1中的第0个byte
Nand Flash内部结构展开大体以下:
svg
将输入的地址addr转换为:函数
int col = addr % 2048;//列地址 addr &(2048-1); int page = addr / 2048;//行地址(至关于页地址)
/* 等待NAND就绪 */ void nand_wait_ready(void) { while(!(NFSTAT & 0x01));//当NFSATA[0] = 1时,表示就绪 }
/* 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 }
从Nand Flash启动,此时片内SRAM的地址对应的就是CPU的0地址,若是从Nand Flash启动,2440硬件会把nand Flash前4K的数据复制到片内SRAM,若是Nand Flash上的程序大于4K,那后续数据就会丢失,至关于只重定位了前4K的代码。测试
如何解决上述问题:ui
首先判断从NorFlash or NandFlash中启动spa
/* 检查是否从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); } }
读取0地址后160bytes的数据,若是跟.bin文件前160字节数据相同,则读取成功,不然读取失败code
/* 测试函数:读数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:\r\n"); printf(" 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n\r"); /* 长度固定为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("\n\r"); } }
由上测试可知读取成功
/* 检查参数合法性 */ if(addr & (0x1FFFF)){ //128K printf("Err addr! Please enter an integral multiple of 128K\r\n"); return -1; } if(len & (0x1FFFF)){ //128K printf("Err len! Please enter an integral multiple of 128K\r\n"); return -1; }
/* 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 128K\r\n"); return -1; } if(len & (0x1FFFF)){ //128K printf("Err len! Please enter an integral multiple of 128K\r\n"); 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; }
/* 擦除测试函数:固定擦除一个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 ]\r\n",whichblock); /* 提示擦除数据的范围 */ printf("***** Erase range : 0x%08x - 0x%08x\n\r",addr,(addr+(128*1024))); printf("***** erase ...\r\n"); if(erase_nand_data(addr,128*1024) == 0){//若是擦除成功 printf("***** Erase finished!\r\n"); } else{ printf("***** Erase fail!\r\n"); } }
/* 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(); }
此处注意:通常在烧写数据以前须要对数据进行擦除操做,除非本来的数据全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%08x\r\n",addr,(addr + len)); printf("***** writing ...\r\n"); write_nand_data(addr,str,strlen(str)+1);//strlen不包括结束符'\0',所以须要+1 printf("***** writing finished\r\n"); }
0x100000
地址写入“hello,world!”经过读取OOB区的第0个字节(即第
2048
个字节)来判断,若是不是0xFF,为坏块,不然不是
/* 判断是否为坏块 * ret:返回1为坏块,返回0不是 */ int isNandBadBlock(unsigned int addr) { unsigned int col = 2048;//读取OOB区第0个字节 unsigned int page = addr / 2048; unsigned char val = 0; /* 1. 选中 */ nand_enable_cs(); /* 2. 发出读命令00h */ nand_write_cmd(0x00); /* 3. 发出地址(分5步发出) */ nand_col(col); nand_page(page); /* 4. 发出读命令30h */ nand_write_cmd(0x30); /* 5. 判断状态,等待就绪 */ nand_wait_ready(); /* 6. 读数据 */ val = nand_read_data_byte(); /* 7. 取消选中 */ nand_disable_cs(); if(val != 0xFF){ return 1; /* 坏块 */ } else{ return 0; } }