(1)我是在网上看到的一片关于SAMSUNG的K9F1208U0B的NandFlash读取操作的文章这个NAND是有4096个block每个block分32page每个page是512Byte大小(不包括SpareField)NandFlash的寻址:NandFlash的地址寄存器把一个完整的NandFlash地址分解成ColumnAddress与PageAddress进行寻址。ColumnAddress:列地址。ColumnAddress其实就是指定Page上的某个Byte,指定这个Byte其实也就是指定此页的读写起始地址。PageAddress:页地址。由于页地址总是以512Bytes对齐的,所以它的低9位总是0。确定读写操作是在Flash上的哪个页进行的。Read1命令:当我们得到一个NandFlash地址src_addr时,我们可以分解出ColumnAddress和PageAddress.我的板子上使用的是SAMSUNG的K9F1208U0B,下面我将对此型号的NandFlash读取操作做一个讲解。首先我们先从物理结构上来了解这颗芯片,结构图如下所示:正如硬盘的盘片被分为磁道,每个磁道又被分为若干扇区,一块NandFlash被分为若干Block,每个Block又被分为若干Page。由上图我们可以知道Flash中Byte(字节),Page(页),Block(块)3个单位之间的关系为1Page=512BytesDataField+16BytesSpareField,1Blcok=32Pages。我们讨论的K9F1208U0B总共有4096个Blocks,故我们可以知道这块Flash的容量为4096*(32*528)=69206016Bytes=66MB。但事实上每个Page上的最后16Bytes是用于存贮检验码用的,并不能存放实际的数据,所以实际上我们可以操作的芯片容量为4096*(32*512)=67108864Bytes=64MB。由上图所示,1个Page总共由528Bytes组成,这528个字节按顺序由上而下以列为单位进行排列(1列代表一个Byte。第0行为第0Byte,第1行为第1Byte,以此类推,每个行又由8个位组成,每个位表示1个Byte里面的1bit)。这528Bytes按功能分为两大部分,分别是DataField和SpareField,其中SpareField占528Bytes里的16Bytes,这16Bytes是用于在读写操作的时候存放校验码用的,一般不用做普通数据的存储区,除去这16Bytes,剩下的512Bytes便是我们用于存放数据用的DataField,所以一个Page上虽然有528个Bytes,但我们只按512Bytes进行容量的计算。DataField按位置关系又可分为两个部分,分别称为1sthalf与2ndhalf,每个half各占256个bytes。或许你会感到纳闷,为什么要把DataField分为两个部分?把他们看做一个整体进行操作不就好了吗?呵呵,凡事都有因果关系,这么分块自然有它的道理所在,但现在还不是告诉你答案的时候。我们还是先讨一下它的操作吧。对K9F1208U0B的操作是通过向NandFlash命令寄存器(对于s3c2410来说此寄存器为NFCMD,内存映射地址为0x4e000004)发送命令队列进行的,为什么说是命令队列?就是因为要完成某个操作的时候发送的不是一条命令,而是连续几条命令或是一条命令加几个参数。下面是K9F1208U0B的操作命令集:读命令有两个,分别是Read1,Read2。其中Read1用于读取DataField的数据,而Read2则是用于读取SpareField的数据。对于NandFlash来说,读操作的最小操作单位为Page,也就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本Page的最后一个Byte为止(可以包括SpareField)。NandFlash的寻址:NandFlash的地址寄存器把一个完整的NandFlash地址分解成ColumnAddress与PageAddress进行寻址。ColumnAddress:列地址。ColumnAddress其实就是指定Page上的某个Byte,指定这个Byte其实也就是指定此页的读写起始地址。PageAddress:页地址。由于页地址总是以512Bytes对齐的,所以它的低9位总是0。确定读写操作是在Flash上的哪个页进行的。Read1命令:当我们得到一个NandFlash地址src_addr时,我们可以这样分解出ColumnAddress和PageAddress。column_addr=src_addr%512;//columnaddresspage_address=(src_addr9);//pageaddress也可以这么认为,一个NandFlash地址的A0~A7是它的column_addr,A9~A25是它的PageAddress。(注意地址位A8并没有出现,也就是A8被忽略,在下面你将了解到这是什么原因)。Read1命令的操作分为4个Cycle,发送完读命令00h或01h(00h与01h的区别请见下文描述)之后将分4个Cycle发送参数,1st.Cycle是发送ColumnAddress。2nd.Cycle,3rd.Cycle和4th.Cycle则是指定PageAddress(每次向地址寄存器发送的数据只能是8位,所以17位PageAddress必须分成3次进行发送)。4个Cycle见下图所示:你是否还记得我上文提到过的DataField被分为1sthalf和2endhalf两个部分?而从上面的命令集我们看到Read1的命令里面出现了两个命令选项,分别是00h和01h。这里出现了两个读命是否令你意识到什么呢?是的,00h是用于读写1sthalf的命令,而01h是用于读取2ndhalf的命令。现在我可以结合上给你说明为什么K9F1208U0B的DataField被分为2个half了。如上文我所提及的,Read1的1st.Cycle是发送ColumnAddress,假设我现在指定的ColumnAddress是0,那么读操作将从此页的第0号Byte开始一直读取到此页的最后一个Byte(包括SpareField),如果我指定的ColumnAddress是127,情况也与前面一样,但不知道你发现没有,用于传递ColumnAddress的数据线有8条(I/O0~I/O7,对应A0~A7,这也是A8为什么不出现在我们传递的地址位中),也就是说我们能够指定的ColumnAddress范围为0~255,但不要忘了,1个Page的DataField是由512个Byte组成的,假设现在我要指定读命令从第256个字节处开始读取此页,那将会发生什么情景?我必须把ColumnAddress设置为256,但ColumnAddress最大只能是255,这就造成数据溢出。。。正是因为这个原因我们才把DataField分为两个半区,当要读取的起始地址(ColumnAddress)在0~255内时我们用00h命令,当读取的起始地址是在256~511时,则使用01h命令.假设现在我要指定从第256个byte开始读取此页,那么我将这样发送命令串column_addr=256;NF_CMD=0x01;//从2ndhalf开始读取NF_ADDR=column_addr&0xff;//1stCycleNF_ADDR=page_address&0xff;//2nd.CycleNF_ADDR=(page_address8)&0xff;//3rd.CycleNF_ADDR=(page_address16)&0xff;//4th.Cycle其中NF_CMD和NF_ADDR分别是NandFlash的命令寄存器和地址寄存器的地址解引用,我一般这样定义它们#definerNFCMD(*(volatileunsignedchar*)0x4e000004)//NANDFlashcommand#definerNFADDR(*(volatileunsignedchar*)0x4e000008)//NANDFlashaddress事实上,当NF_CMD=0x01时,地址寄存器中的第8位(A8)将被设置1(如上文分析,A8位不在我们传递的地址中,这个位其实就是硬件电路根据01h或是00h这两个命令来置高位或是置低位),这样我们传递column_addr的值256随然由于数据溢出变为1,但A8位已经由于NF_CMD=0x01的关系被置为1了,所以我们传到地址寄存器里的值变成了A0A1A2A3A4A5A6A7A8100000001这8个位所表示的正好是256,这样读操作将从此页的第256号byte(2ndhalf的第0号byte)开始读取数据。Read2:Read2则是指定读取SpareField的内容。其实Read1和Read2都是读命令,他们的区别相当于对一个读指针进行不同区域的定位。如图所示:nand_flash.c中包含3个函数voidnf_reset(void);voidnf_init(void);voidnf_read(unsignedintsrc_addr,unsignedchar*desc_addr,intsize);nf_reset()将被nf_init()调用。nf_init()是nand_flash的初始化函数,在对nandflash进行任何操作之前nf_init()必须被调用。nf_read(unsignedintsrc_addr,unsignedchar*desc_addr,intsize)为读函数。src_addr是nandflash上的地址,desc_addr是内存地址,size是读取文件的长度。在nf_reset和nf_read函数中存在两个宏NF_nFCE_L();NF_nFCE_H();你可以看到当每次对NandFlash进行操作之前NF_nFCE_L()必定被调用,操作结束之时NF_nFCE_H()必定被调用。这两个宏用于启动和关闭Flash芯片的工作(片选/取消片选)。至于nf_reset()中的rNFCONF=(115)|(114)|(113)|(112)|(111)|(TACLS8)|(TWRPH04)|(TWRPH10);这一行代码是对NandFlash的控制寄存器进行初始化配置,rNFCONF是NandFlash的配置寄存器,各个位的具体功能请参阅s3c2410数据手册。现在举一个例子,假设我要从NandFlash中的第5000字节处开始读取1024个字节到内存的0x30000000处,我们这样调用read函数nf_read(5000,0x30000000,1024);我们来分析5000这个src_addr。根据column_addr=src_addr%512;page_address=(src_addr9);我们可得出column_addr=5000%512=392page_address=(50009)=9于是我们可以知道5000这个地址是在第9页的第392个字节处,于是我们的nf_read函数将这样发送命令和参数column_addr=5000%512;page_address=(50009);NF_CMD=0x01;//从2ndhalf开始读取NF_ADDR=column_addr&0xff;//1stCycleNF_ADDR=page_address&0xff;//2nd.CycleNF_ADDR=(page_address8)&0xff;//3rd.CycleNF_ADDR=(page_address16)&0xff;//4th.Cycle向NandFlash的命令寄存器和地址寄存器发送完以上命令和参数之后,我们就可以从rNFDATA寄存器(NandFlash数据寄存器)读取数据了。我用下面的代码进行数据的读