先来谈一下flash,flash是一种非易失存储器,一般flash存储设备分为Nandflash和Norflash。这两种flash各有优缺点。在读写速度上,norflash的读速度快一些,nandflash的写速度会快一些。Nandflash的容量一般都比Norflash大很多,而且相比价格比较便宜。但是Norflash支持XIP,而nandflash不支持,而且Nandflash可能有坏块。相关的比较,网上很多文章都有介绍,这里就说这么多了。
这里介绍nandflash驱动,在WinCE中,有专门针对flash存储设备驱动的支持,一般传统采用FAL+FMD的架构。在WinCE最新的版本中,也就是Windows CE6.0 R2中,还支持MDD+PDD的架构。在FAL+FMD架构中,FAL层由微软来实现,我们需要实现FMD层的相关接口函数。在MDD+PDD的架构中,MDD替换了原来架构中的FAL,而PDD相当于原来的FMD,只要实现PDD层就可以了。如果你的系统已经升级到WinCE6.0 R2,那么你应该可以在WINCE600PublicCOMMONOAKDRIVERS目录下面找到这两种架构驱动的源代码。 由于MDD+PDD的架构在WinCE6.0 R2中才有支持,本人也没有实现过。所以这里只介绍基于FAL+FMD架构下,nandflash驱动的开发,这也是目前大家都采用的开发flash驱动的架构。
如上面所说,我们需要实现FMD层的相关接口,下面来介绍一下各个接口函数:
1. PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut): 这个是Flash设备的初始化函数。在WinCE启动的时候,要加载Flash驱动时,首先调用这个函数对flash设备进行初始化。如果你的系统中有nandflash的controller,那么你需要在这里对你的nandflash controller进行初始化。如果没有的话,你需要针对你的硬件设计进行相关的片选,时序等进行配置。返回一个handle表示成功,这个handle将被FMD_Deinit(..)函数用到,如果返回NULL表示失败。
2. BOOL FMD_Deinit(PVOID hFMD): 这个函数在nandflash驱动卸载的时候被调用,参数就是FMD_Init函数返回的Handle.一般在这个函数里面,你可以释放一些用到的资源,然后关闭nandflash controller。
3. BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors): 这个函数用于读nandflash的一个扇区。对于nandflash来说,分大page和小page,大page是2048个bytes一页,小page是512个bytes一页。所以大page每个扇区有2048 bytes,小page每个扇区有512 bytes。
startSectorAddr: nandflash物理扇区的起始地址,对于nandflash来说,就是nandflash中从哪个page开始。
pSectorBuff:扇区数据buffer,从nandflash中读出的每一个扇区的数据都存放在这个buffer中。
pSectorInfoBuff:扇区信息buffer,一般每个扇区的信息会被保存在nandflash的带外数据中,针对小page,带外数据有16 bytes,大page有64 bytes。从nandflash的带外数据将该扇区的相关信息读出来,存放在这个buffer中。
dwNumSectors:读取多少个扇区,对于nandflash来说相当于读取多少个page。
4. BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors): 该函数用于写nandflash的一个扇区。参数和上面的FMD_ReadSector的参数意思一样,就不多说了。
5. BOOL FMD_EraseBlock(BLOCK_ID blockID): 该函数用于擦出nandflash的一个block,参数为要擦除nandflash的block地址,也就是第几个block。
6. DWORD FMD_GetBlockStatus(BLOCK_ID blockID): 该函数获得nandflash中某一个block的状态。参数为nandflash的block地址。由于nandflash中可能有坏块,所以针对nandflash,这个函数首先会检查当前块是否是坏块,这个一般通过读取当前block的第0个page和第1个page的带外数据。对于小page nandflash一般是读取第5个byte,对于大page nandflash一般读取第0个byte,如果不为0xff表示该块是坏块。当然,至于具体该读哪个byte,最好还是看一下所用nandflash的datasheet,确认一下,不同的厂家可能有所不同。如果发现该块是坏块,应该返回BLOCK_STATUS_BAD。如果不是坏块,需要读取这个块的起始扇区的扇区信息。如果读该扇区信息出错,应该返回BLOCK_STATUS_UNKNOWN,否则,判断独到的信息,返回相应结果。
7. BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus): 该函数设置nandflash某个block的状态,第一个参数是nandflash的block地址,第二个是要设置的状态。在这个函数中,首先检查dwStatus是不是BLOCK_STATUS_BAD,如果是就对nandflash作坏块标记,然后返回FALSE。如果不是,就将dwStatus写到该block的第0个page的扇区info中。这个函数和上面的函数正好是相反的。
8. BOOL FMD_GetInfo(PFlashInfo pFlashInfo): 该函数用于返回flash的信息。其中pFlashInfo是一个包含flash信息的结构。
pFlashInfo->flashType:flash的类型,对于nandflash来说,应该是NAND。
pFlashInfo->wDataBytesPerSector:一个扇区多少个bytes,对于大page是2048,对于小page是512。
pFlashInfo->dwNumBlocks:flash中总共有多少个block,查一下所用的nandflash的datasheet就知道了。
pFlashInfo->wSectorsPerBlock:每个block中包含多少个扇区。
pFlashInfo->dwBytesPerBlock:每个block中包含多少个bytes。
9. VOID FMD_PowerDown()和VOID FMD_PowerUp(): 这两个函数用于电源管理。FMD_PowerDown()用于关闭flash设备电源,FMD_PowerUp()用于恢复flash设备电源。根据你所用处理器和相关硬件环境,去实现这两个函数。不实现也不会影响nandflash的使用。
10. BOOL FMD_OEMIoControl(..): 就像很多的IOControl函数一样,根据不同的case,实现相应的功能。针对nandflash来说,这里面的case不一定都需要实现。事实上,如果什么都没有实现,也不影响nandflash的使用。在WinCE的文档中,定义了一些需要实现的case,你可以实现,也可以不去实现。
对于nandflash来说,实现上述函数就可以了。在nandflash出厂的时候,厂家已经对nandflash中的坏块进行了标记。所以第一次对nandflash操作的时候,不要随便擦除nandflash,因为这样可能会把坏块标记擦掉,这样你就判断不出哪个块是坏块了。
关于ECC校验,目前很多处理带有nandflash controller,而且nandflash controller带有硬件ECC功能。如果没有硬件ECC,也可以使用软件ECC,软ECC的代码可以在WINCE600PUBLICCOMMONOAKDRIVERSBLOCKMSFLASHFMDECC下找到。一般来说,ECC校验会对512个BYTE产生3个字节的校验码,也就是说对小PAGE来说,每个PAGE有3个字节的ecc校验码;对于大PAGE来说,有12个字节。这些校验码应该在写扇区数据的时候,被写在扇区的带外数据里面。当读扇区数据时,会先把数据读出来,然后根据这些数据计算ecc,再和读出来的ecc进行比较,如果一致,则表示正确。