s3c2440裸机编程-nandflash

1 nandflash原理#

1.1 原理图#

1.2 引脚描述#

引脚名称 引脚功能
IO0~IO7 数据输入输出(命令、地址、数据共用数据总线)
CLE 命令使能
ALE 地址使能
/CE 芯片使能(片选)
/RE 读使能
/WE 写使能
R/B 就绪/忙输出信号(低电平表示操作还在进行中,高电平表示操作完成)

1.3 nand存储结构#

我们常见的Nand Flash,内部只有一个chip,每个chip只有一个plane。但也有些复杂的,容量更大的Nand Flash,内部有多个chip,每个chip有多个plane,这类的Nand Flash,其实就是多了一个主控将多块flash叠加在一起,如下图:

1.3.1 Block块#

一个Nand Flash由很多个块(Block)组成,块的大小一般有64K,128KB,256KB,512KB,Block是Nand Flash的擦除操作的基本/最小单位。Nand Flash中,一个块中含有1个或多个位是坏的,就称为其为坏块Bad Block

1.3.2 Page页#

每个块里面又包含了很多页(page)。每个页的大小,对于现在常见的Nand Flash多数是2KB,当然也有的nand flash的页大小为4KB、8KB等。页Page,是读写操作的基本单位

1.3.3 oob区域#

每一个page页,对应还附加了一块区域,一般为64byte,叫做空闲区域(spare area)/oob区域(Out Of Band),由于nandflash在操作过程中容易产生位反转,这是nandflash的物理特性决定的,所以必须要有对应的检测和纠错机制,这种机制被叫做Error Checking and Correcting,所以设计了多余的oob区域,用于放置数据的校验值。oob的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了oob。

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

1
2
3
标记是否是坏快
存储ECC数据
存储一些和文件系统相关的数据。如jffs2就会用到这些空间存储一些特定信息

1.3.4 存储单位关系#

一般情况下:1block = 64page = 64 * (2K+64B) = 128K + 4K, 一个page包含2K数据和64B的oob。

1.4 Feature特性#

K9F2G08U0C这款nandflash为例:

可以看出此款nandflash特性如下:

1
2
3
4
5
6
1. 容量为256M,外加8M的冗余oob存储区。
2. page大小为2K,block大小为128K
3. 读一个page时顺序读取至少25*2048ns(数据可以每字节25ns的循环时间读出),随机读取不超过40us
4. 写一个page一般为250us
5. 擦除一个block一般为2ms
6. 封装上分为TSOP分装和FBGA封装(TSOP是指引脚在侧面,FBGA是引脚封在芯片底部,更能保障数据安全,有些客户为了保障数据安全性,防止被飞线进行数据破解,会要求用FBGA封装的flash)

1.5 常见的flash厂商#

常见的flash厂商有:Micron(镁光)、Toshiba(东芝)、Samsung(三星)、MXIC(旺宏)、dosilicon(东芯),(Winbond)华邦、ESMT等。

2 Nand控制器#

前面s3c2440裸机编程-内存控制器介绍讲到,我们知道nand没有独立地址线,cpu无法直接访问nand上的指令,所以nand不能片上执行。那么为何程序还能支持nand启动的呢?

为了支持NAND启动,S3C2440A配备了一个称为“ Steppingstone”的内部SRAM缓冲区,容量为4K。 开机时,Nandflash中的前4K数据将被加载到Steppingstone中,而引导代码将被加载到SRAM中将被执行,如下图所示:

2.1 OM启动介质选择#

我们知道s3c2440支持2种boot方式,nand或者nor,那么需要配置OM引脚来设置引导方式,如下图:

内存控制器的地址映射表如下:

当SW2闭合,OM0=1, OM[1:0]=01, 0地址对应nor,那么从nor启动。
当SW2断开,OM0=0, OM[1:0]=00, 0地址对应bootSRAM(4K),那么0地址对应该SRAM, 那么从nand启动。

2.2 nand控制器引脚配置#

当上电启动时,NAND Flash 控制器将通过下面的引脚配置来获取连接的 NAND Flash 的信息。

NCON:NAND Flash 存储器选择(普通/先进)

1
2
0:普通 NAND Flash(256 字或 512 字节页大小,34 个地址周期)
1:先进 NAND Flash(1K 字或 2K 字节页大小,45 个地址周期)

GPG13:NAND Flash 存储器page size选择

1
2
0:页=256 字(NCON=0)或页=1K 字(NCON=1
1:页=512 字节(NCON=0)或页=2K 字节(NCON=1

GPG14:NAND Flash 存储器地址周期选择

1
2
03 个地址周期(NCON=0)或 4 个地址周期(NCON=1
14 个地址周期(NCON=0)或 5 个地址周期(NCON=1

GPG15:NAND Flash 存储器总线宽度选择

1
2
08 位宽度
116 位宽度

如下表所示更直观:

3 Nand控制器访问时序#

nandflash访问时需要遵循一定的时序才能完成命令、地址、数据的发送。nandflash有8bit位宽数据总线,那么没有地址线它是怎么和cpu通信的呢?

1
2
3
4
5
6
7
8
9
1.nandflash是DATA0~DATA7上既传输数据,又传输地址,又传输命令;
①当ALE为高电平时传输的是地址;
②当CLE为高电平时传输的是命令;
③当ALE,CLE都为低电平表示传输的是数据

2. 先发送片选CS和WE/RE信号
3. 再发送CLE
4. 再发送ALE
5. 最后发送数据

下面分别介绍命令、地址、数据的发送过程。

3.1 命令/地址锁存时序(写命令/地址)#

  1. 首先看时钟,nand控制器的时钟源采用的是HCLK, 也就是AHB高速总线模式,可以参考s3c2440裸机编程-时钟体系那么HCLK=100Mhz, T=1/HCLK=10ns。
  2. 从上图可知命令、地址锁存的时序是一样的,复用一个时序图,当到达①的位置时,CLE/ALE=0;
  3. 当到达位置②时,CLE/ALE=1,表示命令/地址信号拉高,命令/地址开始使能,然后往数据总线DATA上放入命令或地址;
  4. 经过TACLS时间,到达位置③时,拉低nWE引脚,这时数据总线DATA上的命令/地址开始被锁存,锁存需要一定的时间,所以经过TWRPH0时间后,数据总线DATA上的命令/地址锁存完成;
  5. 到达位置④,此时释放nWE信号,nWE=1,这时还需要经过TWRPH1时间后,释放CLE/ALE,此时一个完整的命令/地址锁存过程完成。

上面分析了命令/地址的锁存时序过程,下面详细解释下上面几个时间参数的含义:

1
2
3
TACLS:CLE/ALE使能信号发送多久后才可以发送nWE信号
TWRPH0:nWE信号发送多久后数据(commamd/addr)才会被锁存成功
TWRPH1:nWE信号释放多久后才能释放CLE/ALE

这些时间参数要根据我们实际使用的具体nandflash型号和性能来配置我们的nand控制器。

3.1.1 K9F2G08U0C命令/地址锁存时序#

以K9F2G08U0C这款nandflash为例进行讲解,规格书上命令和地址锁存周期如下:

和nand控制器的命令/地址锁存时序图对比发现:

1
2
3
TACLS = max(tCLS,tALS) - tWP;
TWRPH0 = tWP;
TWRPH0 = max(tCLH,tALH);

nand控制器把命令、地址锁存时序复用成了一个时序图,其实命令和地址锁存时序参数基本一致,只不过发命令只需要一个周期就OK了,发地址需要5个时钟周期,为什么?

1
你想,命令多简单,无非就是读写擦,像我们这款nand数据位宽8bit,一个周期绰绰有余。但地址就不一样了,比如此款nandflash容量256M = 2^28,那么需要28根数据线来传输才能一个周期传输完,但这款nandflash的数据总线位宽只有8bit, 只有8根数据线,所以需要把地址拆分成多次发送,先发送col地址,再发送row地址,此款nandflash是用了5个周期发送地址。

3.2 数据锁存时序(写数据)#

从前面的命令地址锁存时序图中我们得知:

1
2
CLE信号拉高,ALE信号拉低时,表示发送的命令;
CLE信号拉低,ALE信号拉高时,表示发送的地址;

1
CLE信号拉低, ALE信号拉低时,表示发送的数据;
  1. 当到达①时,nWE还是高电平,写使能没有开启;
  2. 当到达②,③时,那么经过了tWP时间(TDS时间),数据开始被锁存;
  3. 到达④,经过tDH时间,数据锁存完成;
  4. 到达⑤,也就是数据开始锁存后再过了tWH时间后释放nWE信号;
  5. 重复②③④⑤过程,得到DIN0, DIN1, DIN final。

根据上面这三个图(手册上的命令、地址、数据锁存时序图),下面详细解释各个时间参数的含义:

3.3 数据顺序读时序(读数据)#

  1. ①处,表示要过tRR后才能发送读使能信号nRE进行访问(上一次的访问结束后,需要等待ready状态稳定后才可以进行下一次访问);
  2. 当到达②,需要经过rREA时间后nRE信号才有效(待nRE稳定);
  3. 当到达③,DATA总线上的读取被读取;
  4. 当到达④,nRE释放tREH时间后才允许下一次读使能;

我们看到连续顺序访问时,单次访问的时间为tRC,那么这些时间参数的值也可以从K9F2G08U0C datasheet中找到:为25ns

4 nandFlash初始化和识别#

4.1 nandFlash命令表#

找到K9F2G08U0C datasheet,对NAND FLASH的操作需要发出命令,下面有个NAND FLASH的命令表格,用此表格上的命令来访问我们的nandflash:

4.2 读ID时序#

命令表中的读id还不太直观,下图是从nand芯片手册中截取出的读id时序图:

  1. 第一条竖线位置,发送了nCE,CLE,nWE信号,所以90命令被锁存(readID命令);
  2. 第二条竖线位置,发送了nCE,ALE,nWE信号,所以地址00被锁存;继续往后,命令、地址都发完了,要read数据了,所以释放nWE,ALE,这里tAR表示ALE释放多久后才可以发送nRE信号,tREA表示nRE信号的建立时间;
  3. 第三条竖线位置,发送了nCE,nRE信号,所以数据被锁存,第一个访问周期锁存的数据为marker code,值为0xEC,第二个访问周期的数据为device code,值为0xDA。读id时读5个周期含义对应如下表:

该款nandflash的5个周期读出来的值对应如下:

第四个访问周期含义如下表:

第五个访问周期含义如下表:

根据第4、5个访问周期的结果0x15、0x44我们得知该flash的block_size=128K,page_size=2k, 有2个plane,plane_size=1Gb = 128M, 共256M。

4.3 初始化#

4.3.1 初始化nand控制器#

4.3.1.1 NFCONF-配置寄存器#

nand控制器要按照我们nandflash的实际型号和性能来设置初始值。NFCONF寄存器,也叫nand配置寄存器:

以K9F2G08U0C这款nandflash为例:

前面第3.1.1小节分析了TACLS = max(tCLS,tALS) - tWP,我们得知tCLS、tALS、tWP最小都可以取到12ns, 所以我们可以取TACLS=0;

TWRPH0 = tWP,我们的nand手册上要求tWP最少12ns, 那么取TWRPH0 =1, Duration = HCLK*(TWRPH0+1)=20ns>12ns,满足要求;

TWRPH0 = max(tCLH,tALH), 我们的nand手册上要求tCLH、tALH最少5ns, 那么取TWRPH1 =0, Duration = HCLK*(TWRPH1+1)=10ns>5ns,满足要求。

再配置BusWidth总线位宽为8bit;
所以NFCONF寄存器设置如下:

1
2
3
4
5
#define  TACLS   0
#define TWRPH0 1
#define TWRPH1 0
/*设置NAND FLASH的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

4.3.1.2 NFCONT-控制寄存器#


1
2
MODE [0]: 设置为1,使能NAND控制器。
Reg_nCE [1]: 设置为1,禁止片选(等要使用的时候再使能片选信号)

所以NFCONF寄存器设置如下:

1
2
/*使能NAND FLASH控制器,禁止片选*/
NFCONT = (1<<1) | (1<<0);

4.4 识别nandflash#

4.4.1 NFCMMD-命令寄存器#

我们可以使用2440上的NAND FLASH控制器简化操作,只需要往NFCMMD寄存器写入要传输的命令就可以了,NAND FLASH控制器默认把上面复杂的时序发出来。

NFADDR-地址寄存器

发命令后,后面就需要发送地址了,当nWE和ALE有效的时候,表示锁存的是地址,往NFADDR寄存器中写值就可以了,比如:NFADDR=0x00。
我们得知地址需要用5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址。前面第3小节已详细分析过了命令、地址、数据锁存时序过程。

1
2
1. column: 列地址A0~A10,就是页内地址,地址范围是从02047。(A11用来确定oob的地址,即2048-211164个字节的范围)
2. page:A12~A30,称作页号,page(row)编号。

4.4.2 NFDATA-数据寄存器#

当命令、地址都发送完后就可以从数据总线上DATA[7:0]获取数据或者写入数据。同样往NFDATA寄存器中写值或者读值就可以了,如unsigned char buf=NFDATA,由于是数据位宽是8位的,所以访问时数据组织形式如下:

从上图可以看出,当byte access时,只需一个时钟周期;当wold access的时候,需要4个时钟周期,小端模式下第一个时钟周期对应低字节,第四个时钟周期对应高字节。

识别nandflash代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*初始化nand控制器*/
void nand_init(void) {
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

NFCONT = (1<<1) | (1<<0);
}

/*使能片选*/
void nand_select(void) {
NFCONT &=~(1<<1);
}
/*禁止片选*/
void nand_deselect(void) {
NFCONT |= (1<<1);
}
/*发命令*/
void nand_cmd(unsigned char cmd) {
volatile int i;
NFCCMD = cmd;
for(i=0; i<10; i++);
}
/*发地址*/
void nand_addr_byte(unsigned char addr) {
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
}
/*读数据*/
unsigned char nand_data(void) {
return NFDATA;
}
/*识别nandflash*/
void nand_chip_probe(void) {
unsigned char buf[5]={0};

nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);

buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();

printf("maker id = 0x%x\n\r",buf[0]);
printf("device id = 0x%x\n\r",buf[1]);
printf("3rd byte = 0x%x\n\r",buf[2]);
printf("4th byte = 0x%x\n\r",buf[3]);
printf("page size = %d kb\n\r",1 << (buf[3] & 0x03));
printf("block size = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%x\n\r",buf[4]);
}

5 NandFlash数据操作#

5.1 读数据#

读数据以page为单位的。下图的表格,来说明NAND FLASH内部结构,前面2K(02047)表示页数据,后边64字节(20482111)表示oob。

CPU想读取,第2048个数据,它是哪以一个?

是Page1的第0个字节。CPU使用某个地址访问数据的时候,是在页数据空间来寻址的。

下图为读NAND FLASH的read时序操作:

  1. 首先需要锁存00命令,nCE、CLE、nWE有效,0x00命令被锁存;
  2. 此时CLE无效,ALE开始有效,地址被锁存(从NAND FLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址);
  3. 锁存0x30命令;
  4. 然后会有一个busy时间段,R/nB为低电平。tRR表示busy状态的持续时间(手册上最小为20ns)。
  5. 开始锁存数据,nRE使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期(tRC),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据。

下面开始写代码:

5.1.1 NFSTAT-状态寄存器#

当发完命令、地址后再进行读数据前我们知道有一段时间tRR处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。

wait_ready函数等待NAND FLASH空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时NAND FLASH是空闲的,我们可以通过该位来判断NAND FLASH是否繁忙。代码如下:

1
2
3
void wait_ready(void) {
while (!(NFSTAT & 1));
}

nand_read函数为NAND FLASH的读函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len) {
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);

nand_select();

while (i < len) {
/* 发出00h命令 */
nand_cmd(00);

/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);

/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);

/* 发出30h命令 */
nand_cmd(0x30);

/* 等待就绪 */
wait_ready();

/* 读数据 */
for (; (col < 2048) && (i < len); col++)
buf[i++] = nand_data();

col = 0;
page++;
}

nand_deselect();
}

我们看到每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

5.1.2 自动识别是nor还是nand启动#

在init.c文件中,加上如下代码,用来判断所使用的FLASH是NOR FLASH还是NAND FLASH。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*我们知道nand启动0地址对应片内SRAM,可以像内存一样的写0地址;nor启动,0地址对应nor,nor不能像内存一样的写地址,
**所以往0地址写入数据成功表示nand启动,写不成功表示nor启动
*/
int isBootFromNorFlash(void) {
volatile unsigned int *p = (volatile unsigned int *)0;
unsigned int val = *p;

*p = 0x12345678;
if (*p == 0x12345678) {
/* 写成功, 对应nand启动 */
*p = val;
return 0;
} else
return 1;
}

下面是代码重定位时可以自动区分nand和nor启动,无论是nand启动还是nor启动,都能将程序重定位到sdram中去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void copy2sdram(void) {
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/

extern int __code_start, __bss_start;

volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
unsigned int len = (unsigned int)(&__bss_start) - (unsigned int)(&__code_start);

if (isBootFromNorFlash()) {
while (dest < end)
*dest++ = *src++;
} else {
nand_init();
nand_read((unsigned int)src, dest, len);
}
}

5.2 擦数据#

擦除数据以block为单位的。block erase时序图的过程大致如下:

1.首先发送0x60命令
2.发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可)
3.发送0xd0,执行擦除动作
4.然后会有一个busy时间段,R/nB为低电平
5.发送0x70命令,用来读取状态
6.判断NFDATA寄存器的第0位是否擦除成功

代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int nand_erase(unsigned int addr, unsigned int len) {
int page = addr / 2048;

if (addr & (0x1FFFF)) {
printf("nand_erase err, addr is not block align\n\r");
return -1;
}

if (len & (0x1FFFF)) {
printf("nand_erase err, len is not block align\n\r");
return -1;
}

nand_select();

while (1) {
page = addr / 2048;

nand_cmd(0x60);

/* page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);

nand_cmd(0xD0);

wait_ready();

nand_cmd(0x70);
if (nand_data()&0x1) {
printf("nand_erase err, at addr:0x%x\n\r", addr);
return -1;
}

len -= (128*1024);
if (len == 0)
break;
addr += (128*1024);
}

nand_deselect();
return 0;
}

5.3 写数据#

写数据以page为单位。往NAND FLASH写数据时,只需要把要写的数据复制给NFDATA寄存器即可。代码如下:

1
2
3
void nand_w_data(unsigned char val) {
NFDATA = val;
}

page write的写时序图如下:

1.首先发送0x80命令
2.发送地址(5个周期)
3.发送数据
4.发送0x10命令,执行烧写动作
4.然后会有一个busy时间段,R/nB为低电平
5.发送0x70命令,用来读取状态
6.判断NFDATA寄存器的第0位是否烧写成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len) {
int page = addr / 2048;
int col = addr & (2048 - 1);
int i = 0;

nand_select();

while (1) {
nand_cmd(0x80);

/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);

/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);

/* 发出数据 */
for (; (col < 2048) && (i < len); col++) //还需确认
nand_w_data(buf[i++]);

nand_cmd(0x10);
wait_ready();

nand_cmd(0x70);
if (nand_data() & 0x1) {
printf("nand_write err, at page:0x%x, addr:0x%x\n\r", page, page<<11);
return -1;
}

if (i == len)
break;

/* 开始下一个循环page */
col = 0;
page++;
}

nand_deselect();
}

我们看到每写一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

5.4 测试#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
void do_erase_nand_flash(void) {
unsigned int addr;

/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();

printf("erasing ...\n\r");
nand_erase(addr, 128*1024);
}

void do_read_nand_flash(void) {
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];

/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();

nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;

printf("Data : \n\r");
/* 长度固定为64 */
for (i = 0; i < 4; i++) {
/* 每行打印16个数据 */
for (j = 0; j < 16; j++) {
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}

printf(" ; ");

for (j = 0; j < 16; j++) {
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
}

void do_write_nand_flash(void) {
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;

/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();

printf("Enter the string to write: ");
gets(str);

printf("writing ...\n\r");
nand_write(addr, str, strlen(str)+1);
}

说明:本节的读、写、擦都只涉及到页数据区,不涉及到oob区的操作。

5.5 坏快的标记和解除#

Nand Flash怎么标记某一个BLOCK是坏的? 如何识别一个flash中的坏快?

1
2
3
它使用该BLOCK中第1个扇区的OOB数据中某一个字节来标记: 其值为0xff表示该BLOCK是好的, 其值为非0xff表示该BLOCK是坏的。
在uboot中直接输入“nand bad ”命令即可识别某一个块是否为坏快,在linux用户态的情况下,需要用ioctl(MEMGETBADBLOCK)来获取该block是否为坏快。
有时候我们会误写这个OOB区的值导致有些BLOCK被误认为是"坏块",可以在u-boot中执行"nand scrub"后, 根据提示信息输入小写字母'y'并回车, 它会强制擦除整个Nand Flash(包括把OOB擦除为0xff), 这样就可以恢复被误标为坏块的区域了。