S5PV210开发系列七_Nand驱动实现

S5PV210开发系列七

Nand驱动实现

象棋小子    1048272975

Nand flash具有大容量、改写速度快、接口简单等优点,适用于大量数据的存储,为固态大容量存储提供了廉价有效的解决方案。各种电子产品中如手机存储器、sd卡、u盘等均采用Nand flash存储,笔者此处就Nand驱动实现作一个简单的介绍。

1.  Nand flash概述

东芝公司在1989年最先发表Nand flash结构,强调降低每比特的成本,更高的性能,并且像磁盘一样可以通过接口轻松升级。随着Nand技术的发展,根据Nand颗粒存储单元,可以分为SLC(Single Level Cell)、MLC(Multi LevelCell)、TLC(Trinary Level Cell)。SLC为1bit/cell,即1个储存单元存放1bit数据,其特点是成本高、容量小、速度快、寿命长(约10万次擦写)。MLC为2bit/cell,即1个储存单元存放2bit数据,相比SLC来说,其价格一般,容量可以更大,速度一般、寿命一般(约1万次擦写)。TLC为3bit/cell,即1个储存单元存放3bit数据,因此相同容量下其成本最低,容量相比最大,但速度最慢,寿命也最短(约1000次擦写)。目前市面上基于Nand
flash的存储器,如sd卡、u盘等大多为MLC型,但对于高容量Nand存储器,如64G以上sd卡等,很有可能为TLC型Nand flash。

2. Nand驱动实现

Nand flash由Block(块)构成,Block的基本单元为Page(页),每个页又分为Data area(数据存储区域)以及Spare area(备用区域)。由于Nand flash在出厂以及在使用过程中会出现坏块,以及会出现位反转的问题,为了数据的可靠性,通常需要采用ecc(Error Correcting Code)算法,一般对于SLC Nand,采用1bit ecc即可,对于MLC Nand,需采用4bit以上ecc,而Spare area即用来保存坏块标记、ecc数据等额外信息。通常Nand
flash的读取和编程以页为基础,而擦除却是基于块的。Nand flash编程只能把1编程成0,而不能把0编程成1,因此在页编程时,必须已先擦除。

笔者采用Nand flash为Samsung的K9F4G08U0E,一页有(2048+64)Byte,一个Block有64页,容量大小为(512M+16M)Byte,是一款8位宽的SLC Nand flash。

Nand flash驱动一般应实现Nand初始化、页读、页编程、坏块标记、坏块检查、块擦除这几个接口实现。

2.1. Nand初始化

Nand初始化主要是对Nand引脚功能初始化、根据具体的Nand flash,设置最佳的Nand访问时序。

void Nand_Init(void)

{

// 配置nand控制引脚

MP01CON_REG =(MP01CON_REG & ~(0xf<<8)) | (0x3<<8); // NFCS0

MP01PUD_REG&= ~(3<<4); // pull-up/down disable

MP03CON_REG =0x22222222;

MP03PUD_REG =0;

NFCONF_REG =(2<<12)|(2<<8)|(1<<4)|(0<<3)|(0<<2)|(1<<1);

NFCONT_REG =(1<<23)|(1<<22)|(1<<5)|(1<<4)|(1<<2)|(1<<1)|(1<<0);

Nand_Reset();

}

2.2. Nand页读

K9F4G08U0E一页分为2k的data area与64字节的spare area,data area用来存储正常数据,sparearea用来存储附加数据(如ecc数据)。对于flash存储器,是会出现位反转或坏块的问题,写入flash的数据或从flash读出的数据可能是有错的,因此必须采用ecc算法,确保写入的数据与读出的数据是一致的。对于SLC Nand Flash,只需1位ecc即可,每512 byte产生4 byte的ecc数据,最多可纠错1位,spare area 0~1字节存放坏块标记,2~17字节存放该页ecc数据,18~19字节存放spare
area产生的ecc数据,剩余spare area存放接口需要写入的其它spare area数据。

int32_t Nand_ReadWithOob(uint32_t page, uint8_t *data,uint32_t data_len, uint8_t *oob, uint32_t oob_len)

{

uint8_t ecc[16];

uint32_t i;

uint8_t *pecc;

uint8_t *pBuffer;

uint32_t MECC, SECC;

uint16_t Col;

uint8_t Status;

if (!data && (data_len!=0)) {

return -1;

}

if (!oob && (oob_len!=0)) {

return -1;

}

// 保留oob前面20字节作为坏块标记以及数据ecc

if ((data_len>2048) || (oob_len>64-20)) {

return -2;

}

// ecc data 16字节开始于spare区的第2个字节偏移处

Col = 2048 + 2;

NF_INIT_SECC();

NF_SECC_UNLOCK(); // 解锁spare ECC

NF_CE_ENABLE();

NF_CLEAR_RB();

NF_CMD(NAND_CMD_READ0); // page read cycle 1

NF_ADDR(Col & 0xff); // column address

NF_ADDR(Col >> 8); // columu address

NF_ADDR(page & 0xff); // 传输3字节的页地址

NF_ADDR((page>>8) & 0xff);

NF_ADDR((page>>16) & 0xff);

NF_CMD(NAND_CMD_READSTART); // page read cycle 2

NF_WAIT_READY(); // 等待页读完成

for (i=0; i<16; i++) {

ecc[i] =NF_READ_BYTE();

}

NF_SECC_LOCK();

SECC = NF_READ_BYTE();

SECC |= NF_READ_BYTE() << 8;

for (i=0; i<oob_len; i++) {

oob[i] =NF_READ_BYTE();

}

NFSECCDATA0_REG=((SECC&0xff00)<<8)|(SECC&0xff);

NF_CE_DISABLE();

// Read ecc status

Status = NFESTAT0_REG;

if (((Status>>2) & 0x3) == 1) {

// ecc 1biterror, Correctable

ecc[(Status>>21)&0xf]^= (1 << ((Status>>18) & 0x7));

} else if (((Status>>2) & 0x3) > 1) {

Debug("ECCuncorrectable error detected\r\n");

return -3;

}

if (data_len == 0) {

return 0;

}

NF_CE_ENABLE(); // 使能片选

NF_CLEAR_RB(); // 清数据传输标志

NF_CMD(NAND_CMD_READ0); // page read cycle 1

NF_ADDR(0); // column address

NF_ADDR(0); // columu address

NF_ADDR(page & 0xff); // 写入3字节的页地址

NF_ADDR((page>>8) & 0xff);

NF_ADDR((page>>16) & 0xff);

NF_CMD(NAND_CMD_READSTART); // page read cycle 2

NF_WAIT_READY(); // 等待命令完成

// read main area

pecc = ecc;

while (data_len) {

pBuffer =data;

NF_INIT_MECC();// main区ECC清空

NF_MECC_UNLOCK();// main区ECC解锁,开始ECC计算

for (i=0;i<512; i++) {

*data++ =NF_READ_BYTE();

data_len--;

if(data_len == 0) {

break;

}

}

NF_MECC_LOCK();// 锁定main ECC

// SLC: Writeecc to compare

MECC =(pecc[1] << 16) | (pecc[0] << 0);

NFMECCDATA0_REG= MECC;

MECC =(pecc[3] << 16) | (pecc[2] << 0);

NFMECCDATA1_REG= MECC;

pecc += 4;

// Read eccstatus

Status =NFESTAT0_REG;

if ((Status& 0x3) == 1) {

// 1biterror, Correctable

pBuffer[(Status>>7)&0x7ff]^= (1 << ((Status>>4) & 0x7));

} else if((Status & 0x3) > 1) {

Debug("ECCuncorrectable error detected\r\n");

NF_CE_DISABLE();

return-4;

}

}

NF_CE_DISABLE();

return 0;

}

2.3. Nand页编程

当要写数据到一个页时,要先确保这个块已经被擦除。数据写完后,为确保写入与读取的数据一致,应同时写入数据的ecc值到spare area约定好的位置。

int32_t Nand_WriteWithOob(uint32_t page, const uint8_t*data, uint32_t data_len, const uint8_t *oob, uint32_t oob_len)

{

uint8_t ecc[16];

uint32_t i;

uint8_t *pecc;

uint32_t MECC, SECC;

uint32_t data_len_temp;

uint16_t Col;

uint8_t State;

if (!data && (data_len!=0)) {

return -1;

}

if (!oob && (oob_len!=0)) {

return -1;

}

// 保留oob前面20字节作为坏块标记以数据ecc

if ((data_len>2048) || (oob_len>64-20)) {

return -2;

}

NF_CE_ENABLE(); // 使能片选

NF_CLEAR_RB(); // 清数据传输标志

NF_CMD(NAND_CMD_SEQIN); // page program cycle 1

NF_ADDR(0); // column address

NF_ADDR(0); // columu address

NF_ADDR(page & 0xff); // 写入3字节页地址

NF_ADDR((page>>8) & 0xff);

NF_ADDR((page>>16) & 0xff);

pecc = ecc;

data_len_temp = data_len;

// write main area

while (data_len_temp) {

NF_INIT_MECC();// main区ECC清空

NF_MECC_UNLOCK();// main区ECC解锁,开始ECC计算

// Write bufferto main area

for (i=0;i<512; i++) {

NF_WRITE_BYTE(*data++);

data_len_temp--;

if(data_len_temp == 0) {

break;

}

}

NF_MECC_LOCK();// 锁定main ECC

MECC =NFMECC0_REG; // 4字节写main区数据的ECC

*pecc++ =MECC & 0xff;

*pecc++ =(MECC>>8) & 0xff;

*pecc++ =(MECC>>16) & 0xff;

*pecc++ =(MECC>>24) & 0xff;

}

if (data_len < 2048) {

// 调整到oob区

Col = 2048;

NF_CMD(NAND_CMD_RNDIN);// 页内随机写命令

NF_ADDR(Col& 0xff); // 2字节页内地址

NF_ADDR((Col>>8)& 0xff);

}

// Reserved for bad block (2 byte)

NF_WRITE_BYTE(0xff);

NF_WRITE_BYTE(0xff);

NF_INIT_SECC();

NF_SECC_UNLOCK(); // 解锁spare ECC

// main ecc 16 byte, start spare area offset 2

for (i=0; i<16; i++) {

NF_WRITE_BYTE(ecc[i]);

}

NF_SECC_LOCK(); // 锁定spare ECC

SECC = NFSECC_REG; // 2字节的spare写数据ECC

NF_WRITE_BYTE(SECC & 0xff); // 继续写入SECC

NF_WRITE_BYTE((SECC>>8) & 0xff);

for (i=0; i<oob_len; i++) {

NF_WRITE_BYTE(oob[i]);

}

NF_CMD(NAND_CMD_PAGEPROG); // page program cycle 2

NF_WAIT_READY(); // 等待写完

NF_CMD(NAND_CMD_STATUS); // 读取nand状态

do {

State =NF_READ_BYTE();

} while(!(State & (1<<6))); // 等待状态变成Ready

NF_CE_DISABLE();

// 是否写成功,第0位为0则pass,不然fail

if (State & (1<<0)) {

return -3; //写不成功

}

return 0;

}

2.4. Nand坏块检查

坏块标记约定用spare area第0~1字节来作标志,坏块这两字节标志为非0xff,好块为0xff。我们读取block中页0,spare  area第0~1字节的值即可判断这个block是否坏块。

int32_t Nand_IsBadBlock(uint32_t Block)

{

uint8_toob[2];

// 每个block第一页spare区0, 1字节非0xff标记为好坏

Nand_Read(Block,0, 2048, 2, oob);

if((oob[0]==0xff) && (oob[1]==0xff)) {

return 0; // 好块

}

return 1; // 坏块

}

2.5. 坏块标记

如果页编程不成功或者擦除失败,检查出坏块,需要对相应的块在约定位置进行坏块标记,以免再对这个坏块进行读写。其代码实现如下:

int32_t Nand_MarkBadBlock(uint32_t Block)

{

// 每个block第一页spare区第0, 1字节标记非0xff坏块

uint8_t oob[2];

oob[0] = 0;

oob[1] = 0;

returnNand_Write(Block, 0, 2048, 2, oob);

}

2.6. 块区擦除

数据写入块区前,对应的块应已擦除好。

int32_t Nand_EraseBlock(uint32_t Block)

{

uint8_tState;

NF_CE_ENABLE();

NF_CLEAR_RB();

NF_CMD(NAND_CMD_ERASE1);// erase block command cycle 1

// write 3cycle block address[A28:A18]

NF_ADDR((Block<<6)& 0xff); // [A19:A18]

NF_ADDR((Block>>2)& 0xff); // [A27:A20]

NF_ADDR((Block>>10)& 0xff); // A28

NF_CMD(NAND_CMD_ERASE2);// erase block command cycle 2

NF_WAIT_READY();

NF_CMD(NAND_CMD_STATUS);

do {

State =NF_READ_BYTE();

}while(!(State & (1<<6))); // 等待状态变成Ready

NF_CE_DISABLE();

// 是否擦写成功,第0位为0则pass,不然fail

if (State& (1<<0)) {

return-1;

}

return 0; // 成功擦除

}

3. Nand启动

对于OneNand/Nand启动设备,BL0除了检验BL1的检验和之外,还会采用8bit ecc检验BL1代码的正确性,BL1在烧录进Nand设备时,还应生成相应的8/16位ECC数据,写入到spare area的指定位置,不然ecc失败也将无法从Nand设备启动。Nand启动需要8bit ecc处理,8bit ecc参考Bootloader中相应的NandBoot.c这个Nand驱动。

图3-1  Nand ECC检验

4. 附录

Nand.rar,Nand驱动实现。

http://pan.baidu.com/s/1gd6fEIv

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-06 12:02:14

S5PV210开发系列七_Nand驱动实现的相关文章

S5PV210开发系列五_sd卡驱动实现

S5PV210开发系列五 sd卡驱动实现 象棋小子    1048272975 SD卡(Secure Digital Memory Card)具有体积小.容量大.数据传输快.可插拔.安全性好等优点,被广泛应用于便携式设备上.例如作为数码相机的存储卡,作为手机.平板多媒体扩展卡用的TF卡(micro sd).笔者此处就S5PV210的 sd卡驱动实现作一个简单的介绍. 1. sd卡概述 sd卡技术是在MMC卡的基础上发展起来的,其尺寸与MMC卡一样,只是比MMC卡厚了0.7mm,因此sd设备可以识

S5PV210开发系列八_Yaffs的移植

S5PV210开发系列八 Yaffs的移植 象棋小子    1048272975 Nand作为市面上最基本的非易失性闪存技术之中的一个,应用在各种固态大容量存储解决方式中.因为Nand flash自身的特点,Nand存储器往往须要一款专用的Nand文件系统进行管理.开源的Yaffs文件系统因为其优异的性能,在Nand flash中受到广泛的应用,笔者此处就Yaffs的移植作一个简单的介绍. 1. Yaffs概述 Yaffs是由Aleph One公司所发展出来的Nand flash文件系统,专门为

S5PV210开发系列四_uCGUI的移植

S5PV210开发系列四 uCGUI的移植 象棋小子          1048272975 GUI(图形用户界面)极大地方便了非专业用户的使用,用户无需记忆大量的命令,取而代之的是可以通过窗口.菜单.按键等方式进行操作.在某些场合,设计一款人机界面丰富友好的嵌入式产品能赢得更多的用户.笔者此处就S5PV210基于uCGUI图形用户界面的使用作一个简单的介绍. 1. uCGUI移植概述 1.1. S5PV210 Bootloader 笔者的S5PV210的Bootloader设置最高的CPU主频

S5PV210开发系列三_简易Bootloader的实现

S5PV210开发系列三 简易Bootloader的实现 象棋小子          1048272975 Bootloader是嵌入式系统上电后第一段执行的代码.对于功能简单的处理器,可能并没有Bootloader的概念,但对于应用处理器,有不同的启动方式,不同的存储设备(Nand flash.sd/mmc.DDR2.SRAM等),不同的操作系统等,往往需要一个Bootloader先初始化CPU和相关的硬件,建立内存空间映射,把内核或应用程序加载到相应的内存执行位置,最后调用内核或应用程序,释

S5PV210开发系列六_Fatfs的移植

S5PV210开发系列六 Fatfs的移植 象棋小子    1048272975 对于固态存储器,其存储容量可以很大,往往需要一款文件系统对存储器用户数据进行组织文件的管理.它对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索.在嵌入式系统中,往往需要采用windows兼容的文件系统,像相机的照片.视频监控.语音产品等,很多都需要从windows计算机上提取资源或在windows计算机上进一步处理.Fatfs由于其开源免费,支持fat32,受到了广泛的应用,笔者此处就S5

S5PV210开发系列一_开发环境以及启动模式

S5PV210开发系列一 开发环境以及启动模式 象棋小子          1048272975 ARM核以其高性能.低功耗.低成本广泛应用在各个领域,包括ARM7.ARM9.ARM11.Cortex-M.Cortex-A等这几个系列.众多的半导体商如NXP.Freescale.Atmel.Samsung.TI等都设计了基于ARM核的自家通用处理器,ARM核从低成本控制处理器到高性能应用处理器,已经深入到我们生活的方方面面.笔者此处就Samsung的Cortex-A8处理器S5PV210作一个简

【Qt编程】基于Qt的词典开发系列&lt;七&gt;--调用网络API

前面文章中我们实现了本地的词库设计,可以完成本地的查词功能,那么这篇文章主要讲一讲如何通过调用网络的api来实现词典的网络查词功能. 词典API的选择 基本上市面上做词典软件的公司都有对应的api,比如说有道.金山等等,不过一般都需要api key,有点麻烦,于是乎,我就找了个简单好用的扇贝网API:http://www.shanbay.com/help/developer/api/ 奇怪的是,该api说要停用了,可以从我去年暑假完成该软件制作,到如今为止,还能正常使用. 扇贝词典api的使用

BizTalk开发系列(七) Hello World2

之前根据BizTalk的订阅原理,使用BizTalk管理控制台创建了第一个应用程序 Hello World.但是由于控制台的开发功能有限,绝大多数的BizTalk程序都是在集成开发环境Visual Studio下进行的.下面我们在Visual Studio下创建Hello World2程序.其功能是将源消息的两个字段组成第三个字段并保存到目标消息.[效果图如下] 此程序主要是为要熟悉BizTalk相关的开发组件包括:Schema, Mapping , Pipeline, Orchestratio

【DevOps】团队敏捷开发系列--开山篇

随着软件发布迭代的频率越来越高,传统的「瀑布型」(开发-测试-发布)模式已经不能满足快速交付的需求.2009 年左右 DevOps 应运而生,开发运维一体化,通过自动化工具与流程让整个软件开发构建.测试.发布更加快捷.频繁.高效和可靠. 本系列教程目录 本系列将详细讲解Devops落地细节.将构建整个持续集成与交付的一整套体系与流程.对于未来要开篇的系列博文列表如下: [DevOps]团队敏捷开发系列(一)--开山篇 [DevOps]团队敏捷开发系列(二)--版本控制之道Git [DevOps]