NandFlash

  • 一、写作目的及参考来源说明
  • 二、NandFlash的简介
  • 三、引脚功能
  • 四、Array Organization(组织阵列)
    • 1 . 最小读写单元
    • 2 . 最小擦除单元
    • 3 . Nand Flash控制器与Nand Flash芯片
    • 3 . SLC和MLC
    • 4 . oob / Redundant Area / Spare Area
  • 五、基于 CPU NandFlash 控制器的操作
    • 1 . NAND FL ASH CONFIGURATION REGISTER 的时序时间计算
    • 2 . CONTROL REGISTER
    • 3 . 初始化2440 的 nandflash 控制器
    • 4 . 基于nandflash 控制器的操作
      • 使能NAND Flash
    • 5 . I/O 0~7 地址/命令/数据
    • 6 . 坏块检测
    • 7 . 数据读取
    • 8 . 数据写入
    • 9 . block擦除过程
    • 10 . 软件获取芯片ID
  • 六、NAND操作菜单汇总及串口输出测试
  • 七、代码汇总

一、写作目的及参考来源说明

NandFlash 尽管本身时序复杂,但是经过这么多年的发展,早已形成了一种专用的接口,作为普通使用者来说,我们无需直接去编写配置复杂的时序,只需按照CPU芯片手册和NAND手册的要求去配置SOC的nand控制器就可以了 ,即便是这样,有些流程和细节仍需我们注意,本篇以三星公司生产的 K9F2G08U0C为例,特此记录,但本人求忘记的时候“”有章可循“”,也希望能够帮助更多的人

本篇所写,如有错误,欢迎评论区批评指出

本篇参考了以下博客的内容和韦东山老师的讲解,特此感谢
NAND_FLASH(K9F1208U0C)驱动分析
Nand Flash基础知识与坏块管理机制的研究
物联网:关于Nand flash读写范围的问题

二、NandFlash的简介

Nand flash成本相对低,说白了就是便宜,缺点是使用中数据读写容易出错,所以一般都需要有对应的软件或者硬件的数据校验算法,统称为ECC。但优点是,相对来说容量比较大,现在常见的Nand Flash都是1GB,2GB,8GB,更大的128GB的都有了,相对来说,价格便宜,因此适合用来存储大量的数据。其在嵌入式系统中的作用,相当于PC上的硬盘,用于存储大量数据。
(图片来源于网络,如有侵权请告知)

三、引脚功能


注意:“#” 表示第低电平有效

标号 功能
I/O 0~7 命令/地址/数据 复用
CLE 命令锁存使能
ALE 地址锁存使能
CE# 片选(芯片使能)
RE# 读使能
WE# 写使能
WP# 写保护
R/B# 待续/忙状态
Vcc 电源
Vss
N.C 无连接

四、Array Organization(组织阵列)

1 . 最小读写单元

如图所示,nandflash 的最小的读写单元是一个page,一个 page 由 2K Bytes 大小的数据存储区和64Bytes的oob区组成,数据存储区用于存放我们要存储的数据,而剩下的oob区用于存放数据的校验值,所以我们一般说 Page Size都指的是2 KBytes 的数据存储区。

2 . 最小擦除单元

nandflash 的最小擦除单元是一个block,一个block由 64 个 page 组成,也就是 64 x 2 x 1024 + 64 x 64 = 128KBytes + 4KBytes
最终 2048 个 block 组成了一个device,即nandflash块设备
前面说了,nandflash 的缺点是使用中数据读写容易出错,所以对于每个page的数据区为2KB的nandflash,其坏块标记是一次标记一个block到该block的第一个page的oob区的第一个和第二个字节,读取校验的的时候需要block对齐

3 . Nand Flash控制器与Nand Flash芯片

摘自: Nand Flash基础知识与坏块管理机制的研究
我们写驱动,是写Nand Flash 控制器的驱动,而不是Nand Flash 芯片的驱动,因为独立的Nand Flash芯片,一般来说,是很少直接拿来用的,多数都是硬件上有对应的硬件的Nand Flash的控制器,去操作和控制Nand Flash,包括提供时钟信号,提供硬件ECC校验等等功能,我们所写的驱动软件,是去操作Nand Flash的控制器

然后由控制器去操作Nand Flash芯片,实现我们所要的功能。

由于Nand Flash读取和编程操作来说,一般最小单位是页,所以Nand Flash在硬件设计时候,就考虑到这一特性,对于每一片(Plane),都有一个对应的区域专门用于存放,将要写入到物理存储单元中去的或者刚从存储单元中读取出来的,一页的数据,这个数据缓存区,本质上就是一个缓存buffer,但是只是此处datasheet里面把其叫做页寄存器page register而已,实际将其理解为页缓存,更贴切原意。

而正是因为有些人不了解此内部结构,才容易产生之前遇到的某人的误解,以为内存里面的数据,通过Nand Flash的FIFO,写入到Nand Flash里面去,就以为立刻实现了实际数据写入到物理存储单元中了,而实际上只是写到了这个页缓存中,只有当你再发送了对应的编程第二阶段的确认命令,即0x10,之后,实际的编程动作才开始,才开始把页缓存中的数据,一点点写到物理存储单元中去。

3 . SLC和MLC

SLC 和MLC分别是是Single-Level Cell 单层单元和Multi-Level Cell多层单元的缩写,SLC的特点是成本高、容量小、速度快,而MLC的特点是容量大成本低,但是速度慢。MLC的每个单元是2bit的,相对SLC来说整整多了一倍。不过,由于每个MLC存储单元中存放的资料较多,结构相对复杂,出错的几率会增加,必须进行错误修正,这个动作导致其性能大幅落后于结构简单的SLC闪存。所以DIY固态U盘可以尽量选择SLC
那么软件如何识别系统上使用过的SLC还是MLC呢?

Nand Flash设计中,有个命令叫做Read ID,读取ID,读取好几个字节,一般最少是4个,新的芯片,支持5个甚至更多。以K9F2G08U0C为例,支持5cyc。


从这些字节中,可以解析出很多相关的信息,比如此Nand Flash内部是几个芯片(chip)所组成的,每个chip包含了几片(Plane),每一片中的页大小Page Size,Block Size 块大小,等等。
3rd ID Data 的 Cell Type 从2起步,表明 K9F2G08U0C 是 MLC

4 . oob / Redundant Area / Spare Area

每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而Linux系统中,一般叫做OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),所以设计了多余的区域,用于放置数据的校验值。

Oob的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了oob。

关于oob具体用途,总结起来有:

标记是否是坏快
存储ECC数据
存储一些和文件系统相关的数据。如jffs2就会用到这些空间存储一些特定信息,而yaffs2文件系统,会在oob中,存放很多和自己文件系统相关的信息。

五、基于 CPU NandFlash 控制器的操作


前面说了,nand已经发展为一种行业通用的接口,我们使用nand,只需要按照CPU芯片手册和NAND手册的要求去配置SOC的nand控制器就可以了。

1 . NAND FL ASH CONFIGURATION REGISTER 的时序时间计算

下面我们来看一下手册中的相关内容:
2440中nand时序:

然后我们在nand手册中找一个时序图,来计算一下CPU 的 nand 控制器的TACLS、TWRPH0、TWRPH1 的时间:

计算好时间之后我就可以写相应的2440 的nandflash控制器(NAND FL ASH CONFIGURATION REGISTER)了:

之前我们进行过CPU的时钟配置,HCLK = 100M 如下:

/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m *//* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ldr r0, =0x4C000000ldr r1, =0xFFFFFFFFstr r1, [r0]

所以,可以分别计算出TACLS、TWRPH0、TWRPH1的值 0,1,0,见上图 NAND FL ASH CONFIGURATION REGISTER中的红色字

2 . CONTROL REGISTER

3 . 初始化2440 的 nandflash 控制器

首先,在s3c2440_soc.h中地址有如下宏定义

/*NAND Flash*/#define     NFCONF                   __REG(0x4E000000)  //NAND flash configuration
#define     NFCONT                   __REG(0x4E000004)  //NAND flash control
#define     NFCMD                    __REG_BYTE(0x4E000008)  //NAND flash command
#define     NFADDR                   __REG_BYTE(0x4E00000C)  //NAND flash address
#define     NFDATA                   __REG_BYTE(0x4E000010)  //NAND flash data
#define     NFMECC0                  __REG(0x4E000014)  //NAND flash main area ECC0/1
#define     NFMECC1                  __REG(0x4E000018)  //NAND flash main area ECC2/3
#define     NFSECC                   __REG(0x4E00001C)  //NAND flash spare area ECC
#define     NFSTAT                   __REG_BYTE(0x4E000020)  //NAND flash operation status
#define     NFESTAT0                 __REG(0x4E000024)  //NAND flash ECC status for I/O[7:0]
#define     NFESTAT1                 __REG(0x4E000028)  //NAND flash ECC status for I/O[15:8]
#define     NFMECC0_STATUS           __REG(0x4E00002C)  //NAND flash main area ECC0 status
#define     NFMECC1_STATUS           __REG(0x4E000030)  //NAND flash main area ECC1 status
#define     NFSECC_STATUS            __REG(0x4E000034)  //NAND flash spare area ECC status
#define     NFSBLK                   __REG(0x4E000038)  //NAND flash start block address
#define     NFEBLK                   __REG(0x4E00003C)  //NAND flash end block address

代码配置如下:

#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0void nand_init(void)
{/*设置NAND FLASH 的时序 */NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);/* 设置NAND FLASH 控制器 */NFCONT = (1 << 4) | (1 << 1) | (1 << 0);
}

4 . 基于nandflash 控制器的操作

有了nandflash 控制器,我们的读写操作就变得相对简单了,只需要读写相应的CPU的nand相关的寄存器,2440就可以自动按照读写时序读取或发出数据。
下面以 2440 和 K9F2G08U0C 为例来进一步说明:

使能NAND Flash

前面的引脚 CE# 片选(芯片使能)低电平时有效,写MODE 位为1, 2440的nFCE引脚就会为低电平,则与其连接的 CE# 为低电平 ,使能nand flash 。
同理,禁止片选只要将MODE位写0就好了
代码配置如下:

void nand_delay(void)
{volatile unsigned int i;for (i = 0; i < 10; i++);
}
void nand_select(void)
{/*使能片选 bit 1 为 1 */NFCONT &= ~(1 << 1);nand_delay();
}
void nand_deselect(void)
{/*禁止片选 bit 1 为 1 */NFCONT |= (1 << 1);
}

5 . I/O 0~7 地址/命令/数据

我们只需要读或者写相应的2440寄存器,nandflash控制器就可以自动完成对nandflash的操作
来看一下2440中的这三个寄存器

所以代码编写如下:

//写命令
void nand_cmd(unsigned char cmd)
{NFCMD = cmd;nand_delay();
}
//写地址
void nand_addr_byte(unsigned char addr)
{NFADDR = addr;nand_delay();
}
//读数据
unsigned char nand_data(void)
{return NFDATA;
}
//写数据
void nand_w_data(unsigned char val)
{NFDATA = val;
}

我们还需要判断nandflash的状态,以判断操作是否完成:

void nand_wait_ready(void)
{while (!(NFSTAT & 1));
}

地址发送的形式:

进一步封装写地址的函数,封装成三类;如下:

void nand_addr(unsigned int addr)
{unsigned int col = addr % 2048;unsigned int page = addr / 2048;NFADDR = col & 0xff;nand_delay();NFADDR = (col >> 8) & 0xff;nand_delay();NFADDR = page & 0xff;nand_delay();NFADDR = (page >> 8) & 0xff;nand_delay();NFADDR = (page >> 16) & 0xff;nand_delay();
}void nand_page(unsigned int page)
{NFADDR = page & 0xff;nand_delay();NFADDR = (page >> 8) & 0xff;nand_delay();NFADDR = (page >> 16) & 0xff;nand_delay();
}void nand_col(unsigned int col)
{NFADDR = col & 0xff;nand_delay();NFADDR = (col >> 8) & 0xff;nand_delay();
}

6 . 坏块检测

对于每个page的数据区为2KB的nandflash,其坏块标记是一次标记一个block到该block的第一个page的oob区的第一个和第二个字节,0xff表示正常,其它值表示错误。读取校验的的时候需要block对齐,在每次读写数据或者擦除操作前都需要判断当前所在块的好坏

int nand_bad(unsigned int addr)
{unsigned int col = 2048;unsigned int page = addr / (2048 * 1024);unsigned char val;/* 1. 选中 */nand_select();/* 2. 发出读命令00h */nand_cmd(0x00);/* 3. 发出地址(分5步发出) */nand_col(col);nand_page(page);/* 4. 发出读命令30h */nand_cmd(0x30);/* 5. 判断状态 */nand_wait_ready();/* 6. 读数据 */val = nand_data();/* 7. 取消选中 */nand_deselect();if (val != 0xff)return 1; /* bad blcok */elsereturn 0;
}

7 . 数据读取

nandflsh 时序如下:

在每次读数据前都需要判断当前所在块的好坏:

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{/* 定位当前待读取的列地址 */unsigned int column = addr % 2048;unsigned int cnt = 0; //累计已读字节长度/*cnt没读够len时,每发出一遍命令后,column一次最多只能遍历一个page的data区 ,当超出一个page时需要再发一遍读命令*/while (cnt < len){if (nand_bad(addr)) /* 一个block只判断一次 */{addr += (128 * 1024); /* 跳过当前block */continue;            /* 之所以加continue,是因为跳过当前block的下一个block也需要读oob区来判断好坏,continue结束当前循环,进入下一次去判断 */}/* 使能片选 */nand_select();/*发出00命令*/nand_cmd(0x00);/* 发出地址(分5步发出) */nand_addr(addr);/*发出30命令*/nand_cmd(0x30);/* 等待就绪 */nand_wait_ready();/*在累计读取次数cnt不够目标次数len的前提下(while),column只要未超出单个page的data区就可以继续读 */for (column = 0; (column < 2048) && (cnt < len); column++){buf[cnt++] = nand_data();addr++; //addr累加}//跳出for后的addr只能是2048的倍数,在下一个nand_addr(addr);会自动回车换行指向下一个page的起始地址/*禁止片选*/nand_deselect();}
}

为了方便调用和信息交互,我们进一步对其进行封装:每次读取并打印64个字符

void do_read_nand_flash(void)
{unsigned int i = 0, j = 0;unsigned char read_buf[64] = {0};volatile unsigned char *p = (volatile unsigned char *)read_buf;unsigned int addr = 0;printf("Enter the start address to read:");addr = get_uint();nand_read(addr, read_buf, 64);for (i = 0; i < 4; i++){for (j = 0; j < 16; j++){printf("%02x ", *p++);}printf("   ;");for (p -= 16, j = 0; j < 16; j++, p++){/* 后打印字符 */if (*p < 0x20 || *p > 0x7e) /* 不可视字符 */{putchar('.');}elseputchar(*p);}printf("\n\r");}
}

8 . 数据写入

nandflsh 时序如下:

每次写前,需要判断当前要写入的快是否为好块

char nand_write(unsigned int addr, unsigned char *write_buf, unsigned int len)
{if (nand_bad(addr)) /* 一个block只判断一次 */{printf("this block is bad !\n\r");return -1;}unsigned int page = addr / 2048;unsigned int column = addr % 2048;unsigned int cnt_Byte = 0;while (cnt_Byte < len){nand_select();nand_cmd(0x80);/* 发出地址(分5步发出) */nand_addr(addr);/* 发出数据 */for (; (column < 2048) && (cnt_Byte < len);){nand_w_data(write_buf[cnt_Byte++]);}nand_cmd(0x10);nand_wait_ready();nand_deselect();cnt_Byte += 2048;if (cnt_Byte == len)break;else{column = 0; //类似于回车page++;     //类似于换行}}return 0;
}

为了方便调用和信息交互,我们进一步对其进行封装:最多一次支持写100个字符

void do_write_nand_flash(void)
{unsigned int addr;unsigned char str[100];/* 获得地址 */printf("Enter the address of sector to write: \n\r");addr = get_uint();printf("Please enter less than 100 characters to write: ");gets(str);nand_write(addr, str, strlen(str) + 1); //+1,保留'\0'
}

9 . block擦除过程

擦除过程是将0变成1的过程,即充电的过程(比如SLC中,当低于某个电压值表示0,高于这个电压值则表示1;而对于MLC来说可以有多个阈值,所以可以保存更多bit)。擦除过程是按块进行的,但启始地址是页地址,不过擦除过程在内部是有边界对齐的,也就是说当擦除启始地址不是块对齐时,只能擦除本块,而不能垮越到第二个块继续擦除,也就是无论我们给的地址是否页对齐,本块都将擦除,不会有任何保留。
需要注意的是:块擦除时每一页的oob区也同时被擦除掉了,所以一般擦除前先读取块的第一页的两个字节看是否为0xff,不是的话就不要擦除,0xff表示正常,其它值表示错误,否则将会擦掉所有坏块信息,尤其是出厂时写入的。

nandflsh 时序如下:

char nand_erase_block(unsigned int addr, unsigned int len)
{unsigned int page = 0;unsigned int cnt_Byte = 0;page = addr / 2048;if (nand_bad(addr)) /* 一个block只判断一次 */{printf("this block is bad !\n\r");return -1;}/* 如果page或者len不是block的整数倍,则提醒并返回 */if (page % 64 || len % (2 * 64 * 1024)){printf("nand_erase err, addr is not block align\n\r");return -1;}/* 即便是对齐也再强制对齐一遍 */else{page = (page >> 6) << 6; //保证起始擦除地址是64page即block对齐的(二的六次方)}while (cnt_Byte < len){nand_select();nand_cmd(0x60);nand_page(page);nand_cmd(0xd0);nand_wait_ready();nand_deselect();cnt_Byte += (64 * 2 * 1024);if (cnt_Byte == len) //如果查出的长度达到len,则停止下一次除break;else //否则,继续下一次的擦除page += 64;}return 0;
}

为了方便调用和信息交互,我们进一步对其进行封装:每次调用一次擦除一个block

void do_erase_nand_flash(void)
{unsigned int addr;/* 获得地址 */printf("Enter the address of block to erase: \n\r");addr = get_uint();if (nand_erase_block(addr, (64 * 2 * 1024)) == 0){printf("erase is ok\n\r");}elseprintf("erase is fail\n\r");//最小擦除单位为一个block
}

10 . 软件获取芯片ID

在前面 SLC和MLC 部分,我们说到了read ID 命令,现在具体实现一下:
我们主要关心nandflash第四个数据中包含的信息:

void nand_chip_id(void)
{unsigned char id_data[5] = {0};unsigned char i;nand_select();nand_cmd(0x90);nand_addr_byte(0x00);for (i = 0; i < 5; i++){id_data[i] = nand_data();nand_delay();}nand_deselect();printf("Maker Code:   0x%x\n\r", id_data[0]);printf("Device Code:  0x%x\n\r", id_data[1]);printf("3th cyc:      0x%x\n\r", id_data[2]);printf("4th cyc:      0x%x\n\r", id_data[3]);printf("page  size:   %d KBytes\n\r", 1 << (id_data[3] & 0x03));printf("block size:   %d KBytes\n\r", 64 << ((id_data[3] >> 4) & 3));printf("5th cyc       0x%x\n\r", id_data[4]);
}

经测试串口返回信息如下:

六、NAND操作菜单汇总及串口输出测试

有了以上这些函数,我们可以进一步封装调用,实现菜单操作:

void nand_flash_test(void)
{/* 打印菜单,供我们选择测试内容*//* 测试内容:* 1.识别nand flash* 2.擦除nand flash 摸个扇区* 3.编写某个地址* 4.读某个地址*/while (1){char c;printf("[s] Scan nand flash id\n\r");printf("[e] Erase nand flash\n\r");printf("[w] Write nand flash\n\r");printf("[r] Read nand flash\n\r");printf("[q] Quit nand flash\n\r");printf("Enter selection\n\r");c = getchar();/* 回车 换行 回显*/printf("%c\n\r", c);switch (c){case 'q':case 'Q':return;break;case 's':case 'S':nand_chip_id();break;case 'e':case 'E':do_erase_nand_flash();break;case 'w':case 'W':do_write_nand_flash();break;case 'r':case 'R':do_read_nand_flash();break;}}
}

测试顺序如下:
依次:
read ID
read data
erase block
read data
write data
read data
测试效果如图:

七、代码汇总

为了方便大家的测试和使用,将nandflash.c 的代码整理如下,
来源:韦东山老师
SOC: 2440
NandFlash : K9F2G08U0C

#include "s3c2440_soc.h"
#include "my_printf.h"
#include "nand_flash.h"
#include "include/string.h"
#include "string_utils.h"#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0void nand_delay(void)
{volatile unsigned int i;for (i = 0; i < 10; i++);
}
void nand_init(void)
{/*设置NAND FLASH 的时序 */NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);/* 设置NAND FLASH 控制器 */NFCONT = (1 << 4) | (1 << 1) | (1 << 0);
}
void nand_select(void)
{/*使能片选 bit 1 为 1 */NFCONT &= ~(1 << 1);nand_delay();
}
void nand_deselect(void)
{/*禁止片选 bit 1 为 1 */NFCONT |= (1 << 1);
}void nand_cmd(unsigned char cmd)
{NFCMD = cmd;nand_delay();
}
void nand_addr_byte(unsigned char addr)
{NFADDR = addr;nand_delay();
}
unsigned char nand_data(void)
{return NFDATA;
}
void nand_w_data(unsigned char val)
{NFDATA = val;
}
void nand_wait_ready(void)
{while (!(NFSTAT & 1));
}void nand_addr(unsigned int addr)
{unsigned int col = addr % 2048;unsigned int page = addr / 2048;NFADDR = col & 0xff;nand_delay();NFADDR = (col >> 8) & 0xff;nand_delay();NFADDR = page & 0xff;nand_delay();NFADDR = (page >> 8) & 0xff;nand_delay();NFADDR = (page >> 16) & 0xff;nand_delay();
}void nand_page(unsigned int page)
{NFADDR = page & 0xff;nand_delay();NFADDR = (page >> 8) & 0xff;nand_delay();NFADDR = (page >> 16) & 0xff;nand_delay();
}void nand_col(unsigned int col)
{NFADDR = col & 0xff;nand_delay();NFADDR = (col >> 8) & 0xff;nand_delay();
}int nand_bad(unsigned int addr)
{unsigned int col = 2048;unsigned int page = addr / (2048 * 1024);unsigned char val;/* 1. 选中 */nand_select();/* 2. 发出读命令00h */nand_cmd(0x00);/* 3. 发出地址(分5步发出) */nand_col(col);nand_page(page);/* 4. 发出读命令30h */nand_cmd(0x30);/* 5. 判断状态 */nand_wait_ready();/* 6. 读数据 */val = nand_data();/* 7. 取消选中 */nand_deselect();if (val != 0xff)return 1; /* bad blcok */elsereturn 0;
}void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{/* 定位当前待读取的列地址 */unsigned int column = addr % 2048;unsigned int cnt = 0; //累计void nand_chip_id(void)
{unsigned char id_data[5] = {0};unsigned char i;nand_select();nand_cmd(0x90);nand_addr_byte(0x00);for (i = 0; i < 5; i++){id_data[i] = nand_data();nand_delay();}nand_deselect();printf("Maker Code:   0x%x\n\r", id_data[0]);printf("Device Code:  0x%x\n\r", id_data[1]);printf("3th cyc:      0x%x\n\r", id_data[2]);printf("4th cyc:      0x%x\n\r", id_data[3]);printf("page  size:   %d KBytes\n\r", 1 << (id_data[3] & 0x03));printf("block size:   %d KBytes\n\r", 64 << ((id_data[3] >> 4) & 3));printf("5th cyc       0x%x\n\r", id_data[4]);
}/*cnt没读够len时,每发出一遍命令后,column一次最多只能遍历一个page的data区 ,当超出一个page时需要再发一遍读命令*/while (cnt < len){if (nand_bad(addr)) /* 一个block只判断一次 */{addr += (128 * 1024); /* 跳过当前block */continue;             /* 之所以加continue,是因为跳过当前block的下一个block也需要读oob区来判断好坏,continue结束当前循环,进入下一次去判断 */}/* 使能片选 */nand_select();/*发出00命令*/nand_cmd(0x00);/* 发出地址(分5步发出) */nand_addr(addr);/*发出30命令*/nand_cmd(0x30);/* 等待就绪 */nand_wait_ready();/*在累计读取次数cnt不够目标次数len的前提下(while),column只要未超出单个page的data区就可以继续读 */for (column = 0; (column < 2048) && (cnt < len); column++){buf[cnt++] = nand_data();addr++; //addr累加}//跳出for后的addr只能是2048的倍数,在下一个nand_addr(addr);会自动回车换行指向下一个page的起始地址/*禁止片选*/nand_deselect();}
}char nand_erase_block(unsigned int addr, unsigned int len)
{unsigned int page = 0;unsigned int cnt_Byte = 0;page = addr / 2048;if (nand_bad(addr)) /* 一个block只判断一次 */{printf("this block is bad !\n\r");return -1;}/* 如果page或者len不是block的整数倍,则提醒并返回 */if (page % 64 || len % (2 * 64 * 1024)){printf("nand_erase err, addr is not block align\n\r");return -1;}/* 即便是对齐也再强制对齐一遍 */else{page = (page >> 6) << 6; //保证起始擦除地址是64page即block对齐的(二的六次方)}while (cnt_Byte < len){nand_select();nand_cmd(0x60);nand_page(page);nand_cmd(0xd0);nand_wait_ready();nand_deselect();cnt_Byte += (64 * 2 * 1024);if (cnt_Byte == len) //如果查出的长度达到len,则停止下一次除break;else //否则,继续下一次的擦除page += 64;}return 0;
}
char nand_write(unsigned int addr, unsigned char *write_buf, unsigned int len)
{if (nand_bad(addr)) /* 一个block只判断一次 */{printf("this block is bad !\n\r");return -1;}unsigned int page = addr / 2048;unsigned int column = addr % 2048;unsigned int cnt_Byte = 0;while (cnt_Byte < len){nand_select();nand_cmd(0x80);/* 发出地址(分5步发出) */nand_addr(addr);/* 发出数据 */for (; (column < 2048) && (cnt_Byte < len);){nand_w_data(write_buf[cnt_Byte++]);}nand_cmd(0x10);nand_wait_ready();nand_deselect();cnt_Byte += 2048;if (cnt_Byte == len)break;else{column = 0; //类似于回车page++;     //类似于换行}}return 0;
}
void do_read_nand_flash(void)
{unsigned int i = 0, j = 0;unsigned char read_buf[64] = {0};volatile unsigned char *p = (volatile unsigned char *)read_buf;unsigned int addr = 0;printf("Enter the start address to read:");addr = get_uint();nand_read(addr, read_buf, 64);for (i = 0; i < 4; i++){for (j = 0; j < 16; j++){printf("%02x ", *p++);}printf("   ;");for (p -= 16, j = 0; j < 16; j++, p++){/* 后打印字符 */if (*p < 0x20 || *p > 0x7e) /* 不可视字符 */{putchar('.');}elseputchar(*p);}printf("\n\r");}
}
void do_write_nand_flash(void)
{unsigned int addr;unsigned char str[100];/* 获得地址 */printf("Enter the address of sector to write: \n\r");addr = get_uint();printf("Please enter less than 100 characters to write: ");gets(str);nand_write(addr, str, strlen(str) + 1); //+1,保留'\0'
}void do_erase_nand_flash(void)
{unsigned int addr;/* 获得地址 */printf("Enter the address of block to erase: \n\r");addr = get_uint();if (nand_erase_block(addr, (64 * 2 * 1024)) == 0){printf("erase is ok\n\r");}elseprintf("erase is fail\n\r");//最小擦除单位为一个block
}
void nand_flash_test(void)
{/* 打印菜单,供我们选择测试内容*//* 测试内容:* 1.识别nand flash* 2.擦除nand flash 摸个扇区* 3.编写某个地址* 4.读某个地址*/while (1){char c;printf("[s] Scan nand flash id\n\r");printf("[e] Erase nand flash\n\r");printf("[w] Write nand flash\n\r");printf("[r] Read nand flash\n\r");printf("[q] Quit nand flash\n\r");printf("Enter selection\n\r");c = getchar();/* 回车 换行 回显*/printf("%c\n\r", c);switch (c){case 'q':case 'Q':return;break;case 's':case 'S':nand_chip_id();break;case 'e':case 'E':do_erase_nand_flash();break;case 'w':case 'W':do_write_nand_flash();break;case 'r':case 'R':do_read_nand_flash();break;}}
}

NandFlash 驱动分析与基础功能实现相关推荐

  1. 20150311 NandFlash驱动分析

    20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备 ...

  2. 第十二章 NandFlash驱动移植

    12.1 NandFlash介绍         对Nandflash存储芯片操作,必须通过NandFlash控制器完成,不能通过对NandFlash进行总线操作         对NandFlash ...

  3. NandFlash驱动移植基础知识

    NandFlash驱动移植基础知识 文章借鉴博文:http://blog.csdn.net/leibniz_zsu/article/details/4977842 博文写的不错,谢谢分享 MTD 驱动 ...

  4. Nandflash 驱动深度分析(基于S3C2410)

    NAND Flash在嵌入式系统中的地位与PC机上的硬盘类似,用于保存系统运行所必需的操作系统.应用程序.用户数据.运行过程中产生的各类数据.与内存掉电后数据丢失不同,NAND Flash中的数据在掉 ...

  5. 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  6. linux3.2.0块设备及nandflash驱动框架

    块设备框架:app: open,read,write "1.txt" --------------------------------------------- 文件的读写 文件系 ...

  7. wince串口驱动分析(转)

    wince串口驱动分析 串行通讯接口主要是指UART(通用串行)和IRDA两种.通常的串行连接电气连接上有3wire和9wire两种.3wire的接线方式下定义了发送.接收和地三根连接.其用途就如名称 ...

  8. 医疗数据治理——构建高质量医疗大数据智能分析数据基础

    医疗数据治理--构建高质量医疗大数据智能分析数据基础 阮彤,邱加辉,张知行,叶琪 华东理工大学计算机科学与技术系,上海 200237   摘要:以专病真实世界研究为背景,分析了医疗数据治理和数据可用性 ...

  9. 音频audio/sound声卡驱动分析

    音频可以播放(可以听到声音), 说明音频解码和输出部分基本是正常的, 整个通道已经打通了. 感觉播放速度太快了(或太慢了)说明audio输出部分的频率不对, 太高了或者太低了.audio/sound音 ...

最新文章

  1. 第13天学习Java的笔记(类定义)
  2. 机器人在线“偷懒”怎么办?阿里研究出了这两套算法
  3. .Net连接Sybase数据库的几种方法[转]
  4. 如何添加地图控件到Windows Phone 8的页面中
  5. Session莫名丢失的原因及解决办法[转载]
  6. [跪了]Servlet 工作原理解析
  7. linux debian安装ssh,Ubuntu Linux上安装SSH和vsFTPd的方法
  8. Mybaits的运行原
  9. mod_rewrite模块的使用
  10. 固件编辑器android,定制 Android 固件
  11. 3DMax和Maya到底哪个更牛B?
  12. NLP - ngram - N元语言模型 python 实现
  13. MySQL 8.0+版本 导入.csv文件错误,出错号:1148 The used command is not allowed with this MySQL version问题
  14. 新华三2018校园招聘笔试面试题学习
  15. 白话讲解Dubbo服务
  16. 10 个有用的 Python 字符串函数你必须知道
  17. 值得一看的电脑教程下载
  18. 「高效程序员的修炼」代码版本管理工具 Git 用起来 01 Git 基础
  19. Qt QImage 加载 BMP 图像的一个BUG
  20. 需求决定供给(从网上看到的一个故事)

热门文章

  1. 不要使用矿泉水瓶接热水喝——致癌
  2. 你是真的“C”——宏与函数的英雄本色
  3. 个人小程序支持哪些小程序服务类目
  4. GitLab使用CAS服务进行单点登录配置
  5. 万变不离其宗之ZYNQ启动介绍
  6. 含文档+PPT+源码等]精品基于ssm的足球联赛管理系统的设计与实现vue[包运行成功]计算机Java毕业设计SSM项目源码
  7. linux给分区扩容
  8. 在windows下编译erlang内建函数(nif)的dll文件
  9. Dubbo---升级Dubbo2.7.13依赖问题
  10. 第19章 Linux电源管理的系统架构和驱动之CPUFreq驱动