logo资料库

Nand Flash读写操作 收藏版.doc

第1页 / 共6页
第2页 / 共6页
第3页 / 共6页
第4页 / 共6页
第5页 / 共6页
第6页 / 共6页
资料共6页,全文预览结束
NandFlash 的读写操作 作者:蔡于清 www.another-prj.com (约有修改) 正如硬盘的盘片被分为磁道,每个磁道又分为若干扇区,一块 nand flash 也分为若干 block, 每个 block 分为如干 page。一般而言,block、page 之间的关系随着芯片的不同而不同,典 型的分配是这样的: 1block = 32page 1 page = 512bytes(datafield) + 16bytes(oob) 需要注意的是,对于 flash 的读写都是以一个 page 开始的,但是在读写之前必须进行 flash 的擦写,而擦写则是以一个 block 为单位的。同时必须提醒的是,512bytes 理论上被分为 1st half 和 2sd half,每个 half 各占 256 个字节。 我们讨论的 K9F1208U0B 总共有 4096 个 Blocks,故我们可以知道这块 flash 的容量为 4096 *(32 *528)= 69206016 Bytes = 66 MB 但事实上每个 Page 上的最后 16Bytes 是用于存贮检验码和其他信息用的,并不能存放实 际的数据,所以实际上我们可以操作的芯片容量为 4096 *(32 *512) = 67108864 Bytes = 64 MB 由上图所示,1 个 Page 总共由 528 Bytes 组成,这 528 个字节按顺序由上而下以列为单 位进行排列(1 列代表一个 Byte。第 0 行为第 0 Byte ,第 1 行为第 1 Byte,以此类推,每
个行又由 8 个位组成,每个位表示 1 个 Byte 里面的 1bit)。这 528Bytes 按功能分为两大部分, 分别是 Data Field 和 Spare Field,其中 Spare Field 占 528Bytes 里的 16Bytes,这 16Bytes 是用 于在读写操作的时候存放校验码用的,一般不用做普通数据的存储区,除去这 16Bytes,剩下 的 512Bytes 便是我们用于存放数据用的 Data Field,所以一个 Page 上虽然有 528 个 Bytes, 但我们只按 512Bytes 进行容量的计算。 读命令有两个,分别是 Read1,Read2 其中 Read1 用于读取 Data Field 的数据,而 Read2 则 是用于读取 Spare Field 的数据。对于 Nand Flash 来说,读操作的最小操作单位为 Page,也 就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本 Page 的最 后一个 Byte 为止(可以包括 Spare Field) Nand Flash 的寻址 Nand Flash 的地址寄存器把一个完整的 Nand Flash 地址分解成 Column Address 与 Page Address.进行寻址。 Column Address: 列地址。Column Address 其实就是指定 Page 上的某个 Byte,指定这个 Byte 其实也就是指定此页的读写起始地址。 Page Address:页地址。由于页地址总是以 512Bytes 对齐的,所以它的低 9 位总是 0。确 定读写操作是在 Flash 上的哪个页进行的。 Read1 命令 当我们得到一个 Nand Flash 地址 src_addr 时我们可以这样分解出 Column Address 和 Page Address column_addr = src_addr%512; page_address = (src_addr>>9); 也可以这么认为,一个 Nand Flash 地址的 A0~A7 是它的 column_addr,A9~A25 是它的 Page Address。(注意地址位 A8 并没有出现,也就是 A8 被忽略,在下面你将了解到这是什 么原因) // column address // page address Read1 命令的操作分为 4 个 Cycle,发送完读命令 00h 或 01h(00h 与 01h 的区别请见下 文描述)之后将分4 个 Cycle 发送参数,1st.Cycle 是 发送Column Address。2nd.Cycle ,3rd.Cycle 和 4th.Cycle 则是指定 Page Address(每次向地址寄存器发送的数据只能是 8 位,所以 17 位 的 Page Address 必须分成 3 次进行发送 Read1 的命令里面出现了两个命令选项,分别是 00h 和 01h。这里出现了两个读命是否令 你意识到什么呢?是的,00h 是用于读写 1st half 的命令,而 01h 是用于读取 2nd half 的命令。 现在我可以结合上图给你说明为什么 K9F1208U0B 的 DataField 被分为 2 个 half 了。 如上文我所提及的,Read1 的 1st.Cycle 是发送 Column Address,假设我现在指定的 Column Address 是 0,那么读操作将从此页的第 0 号 Byte 开始一直读取到此页的最后一个 Byte(包 括 Spare Field),如果我指定的 Column Address 是 127,情况也与前面一样,但不知道你发 现没有,用于传递 Column Address 的数据线有 8 条(I/O0~I/O7,对应 A0~A7,这也是 A8 为什么不出现在我们传递的地址位中),也就是说我们能够指定的 Column Address 范围为 0~255,但不要忘了,1 个 Page 的 DataField 是由 512 个 Byte 组成的,假设现在我要指定读 命令从第 256 个字节处 开始读取此页,那将会发生什么情景?我必须把 Column Address 设 置为 256,但 Column Address 最大只能是 255,这就造成数据溢出。。。正是因为这个原因我 们才把 Data Field 分为两个半区,当要读取的起始地址(Column Address)在 0~255 内时我 们用 00h 命令,当读取的起始地址是在 256~511 时,则使用 01h 命令.假设现在我要指定从
第 256 个 byte 开 始读取此页,那么我将这样发送命令串 column_addr=256; NF_CMD=0x01; NF_ADDR=column_addr&0xff; NF_ADDR=page_address&0xff; NF_ADDR=(page_address>>8)&0xff; NF_ADDR=(page_address>>16)&0xff; 其中 NF_CMD 和 NF_ADDR 分别是 NandFlash 的命令寄存器和地址寄存器的地址解引用, 从 2nd half 开始读取 1st Cycle 2nd.Cycle 3rd.Cycle 4th.Cycle 我一般这样定义它们, (*(volatile unsigned char *)0x4e000004) #define rNFCMD #define rNFADDR (*(volatile unsigned char *)0x4e000008) 事实上,当 NF_CMD=0x01 时,地址寄存器中的第 8 位(A8)将被设置为 1(如上文分 析,A8 位不在我们传递的地址中,这个位其实就是硬件电路根据 01h 或是 00h 这两个命令 来置高位或是置低位),这样我们传递 column_addr 的值 256 随然由于数据溢出变为 1,但 A8 位已经由于 NF_CMD =0x01 的关系被置为 1 了,所以我们传到地址寄存器里的值变成了 //NADD Flash command //NAND Flash address A0 1 A1 0 A2 0 A3 A4 0 0 A5 A6 0 0 A7 0 A8 1 这 8 个位所表示的正好是 256,这样读操作将从此页的第 256 号 byte(2nd half 的第 0 号 byte)开始读取数据。 nand_flash.c 中包含 3 个函数 void nf_reset(void); void nf_init(void); void nf_read(unsigned int src_addr,unsigned nf_reset()将被 nf_init()调用。nf_init()是 nand_flash 的初始化函数,在对 nand flash 进行任 char *desc_addr,int size); 何操作之前,nf_init()必须被调用。 nf_read(unsigned int src_addr,unsigned char *desc_addr,int size);为读函数,src_addr 是 nand flash 上的地址,desc_addr 是内存地址,size 是读取文件的长度。 在 nf_reset 和 nf_read 函数中存在两个宏 NF_nFCE_L(); NF_nFCE_H(); 你可以看到当每次对 Nand Flash 进行操作之前 NF_nFCE_L()必定被调用,操作结束之时 NF_nFCE_H()必定被调用。这两个宏用于启动和关闭 Flash 芯片的工作(片选/取消片选)。 至于 nf_reset()中的 rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1< <0); 这一行代码是对 NandFlash 的控制寄存器进行初始化配置,rNFCONF 是 Nand Flash 的配置 寄存器,各个位的具体功能请参阅 s3c2410 数据手册。 现在举一个例子,假设我要从 Nand Flash 中的第 5000 字节处开始读取 1024 个字节到内 存的 0x30000000 处,我们这样调用 read 函数 nf_read(5000, 0x30000000,1024); 我们来分析 5000 这个 src_addr. 根据 column_addr=src_addr%512;
page_address=(src_addr>>9); 我们可得出 column_addr=5000%512=392 page_address=(5000>>9)=9 于是我们可以知道 5000 这个地址是在第 9 页的第 392 个字节处,于是我们的 nf_read 函数 将这样发送命令和参数 column_addr=5000%512; >page_address=(5000>>9); NF_CMD=0x01; NF_ADDR= column_addr &0xff; NF_ADDR=page_address&0xff; NF_ADDR=(page_address>>8)&0xff; NF_ADDR=(page_address>>16)&0xff; 向 NandFlash 的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从 3rd.Cycle 4th.Cycle 从 2nd half 开始读取 1st Cycle 2nd.Cycle rNFDATA 寄存器(NandFlash 数据寄存器)读取数据了. 我用下面的代码进行数据的读取. for(i=column_addr;i<512;i++) { *buf++=NF_RDDATA(); } 每当读取完一个 Page 之后,数据指针会落在下一个 Page 的 0 号 Column(0 号 Byte). 源代码: /* www.another-prj.com author: caiyuqing 本代码只属于交流学习,不得用于商业开发 */ #include "s3c2410.h" #include "nand_flash.h" static unsigned char seBuf[16]={0xff}; //-------------------------------------------------------------------------------------- unsigned short nf_checkId(void) { int i; unsigned short id; NF_nFCE_L(); NF_CMD(0x90); NF_ADDR(0x0); for(i=0;i<10;i++); //chip enable //Read ID //wait tWB(100ns)
id=NF_RDDATA()<<8; id|=NF_RDDATA(); // Maker code(K9S1208V:0xec) // Devide code(K9S1208V:0x76) NF_nFCE_H(); return id; //chip enable } //-------------------------------------------------------------------------------------- static void nf_reset(void) { int i; NF_nFCE_L(); NF_CMD(0xFF); for(i=0;i<10;i++); NF_WAITRB(); NF_nFCE_H(); //chip enable //reset command //tWB = 100ns. //wait 200~500us; //chip disable } //-------------------------------------------------------------------------------------- void nf_init(void) { rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1< <0); // 1 // En } //-------------------------------------------------------------------------------------- r xxx, tWRPH1 nFCE=H tACLS nf_reset(); 1 ECCR xxx tWRPH0 1 r 1 r 1 r xxx void nf_read(unsigned int src_addr,unsigned { char *desc_addr,int size) int i; unsigned int column_addr = src_addr % 512; unsigned int page_address = (src_addr >> 9); unsigned char *buf = desc_addr; while((unsigned int)buf < (unsigned int)(desc_addr) + size) { // column address // page addrress NF_nFCE_L(); // enable chip /*NF_ADDR 和 NF_CMD 为 nand_flash 的地址和命令寄存器的解引用*/ if(column_addr > 255) NF_CMD(0x01); // Read2 command. // 2end halft cmd 0x01: Read command(start from 2end half page) else NF_CMD(0x00); // 1st halft?
NF_ADDR(column_addr & 0xff); NF_ADDR(page_address & 0xff); NF_ADDR((page_address >> 8) & 0xff); NF_ADDR((page_address >> 16) & 0xff); for(i = 0; i < 10; i++); NF_WAITRB(); // Column Address // Page Address // ... // .. // wait tWB(100ns)/////?????? // Wait tR(max 12us) // Read from main area for(i = column_addr; i < 512; i++) { *buf++= NF_RDDATA(); } NF_nFCE_H(); column_addr = 0; page_address++; // disable chip } return ; } 本文来自 CSDN 博客,转载请标明出处: http://blog.csdn.net/abc19842008/archive/2008/01/22/2059480.aspx
分享到:
收藏