nand读写原理(Flash操作原理及裸机程序分析)(1)

来源:韦东山嵌入式专栏_ARM裸机加强版维基教程

作者:韦东山

本文字数:2299,阅读时长:3分钟

在上节 我们实现了芯片ID的读取,可是那个程序已经超过了4k,我们想把它烧到开发板的话,必须把它烧写到NOR FLASH上去,这节我们来讲解NAND FLASH数据的读取,并且实现超过4k的程序从NAND FLASH启动。

下图为NAND FLASH内部结构图,从图中可以可以知道,一个page含有2k 字节的页数据,和64字节的oob区,后面会介绍页数据和oob区有什么关系。

nand读写原理(Flash操作原理及裸机程序分析)(2)

下图的表格,来说明NAND FLASH内部结构,前面2K(0~2047)表示页数据,后边64字节(2048~2111)表示oob。

nand读写原理(Flash操作原理及裸机程序分析)(3)

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

答:是Page1的第0个字节。CPU使用某个地址访问数据的时候,是在页数据空间来寻址的,根本就看不到oob区。

我们知道NAND FLASH 和 NOR FLASH相比有个缺点,NAND FLASH读或写一页数据的时候,可能会发生位反转,里面可能有一位是错误的,为了解决这个问题,引入oob区, 它写页数据的时候,把数据写进页数据的同时会生成一个校验码,把这个校验码写进oob区里面,当读数据的时候,读出1页数据,读取1数据里面有可能有某一位发生错误,它继续读出原来的校验码,使用oob区里面的校验码,来修正页数据里面的数据。从这里我们可以得出一个结论,oob区的存在是为了解决NAND FLASH的缺陷而存在的。

CPU: 只关心数据,不需要看到oob区的校验码(把数据读出来,然后进行校验再把正确的数据返回,就可以了)。CPU想使用某个addr来访问数据的时候,addr是在页数据区间来寻址的,addr根本不会在oob区里面寻址。

为了形象在下面说一个幽默的对话来说明一下CPU和NAND FLASH的功能:

CPU大爷: 小nand啊,你的性能比不上小nor啊,听说你有位反转的毛病

Nand : 是的,大爷,位反转是我天生的毛病,时有时无

CPU大爷: 靠,你说你价格便宜容量大,这不是害我嘛

Nand : 没事,我有偏方,用OOB就可以解决这问题

CPU大爷: 得得得,你那偏方是什么也别告诉我,我只管能读写正确的数据

Nand : 是的,大爷,我这OOB偏方也就我自个私下使用。您就像使用nor一样使唤我就可以了

下面我们开始写程序,想去读NAND FLASH应该怎样操作,下面是nand flash的地址周期。

nand读写原理(Flash操作原理及裸机程序分析)(4)

nand读写原理(Flash操作原理及裸机程序分析)(5)

读NAND FLASH步骤:(从程序的角度来说),我们需要先发出00命令再发出5个周期的地址,再发出30命令,然后就可以读数据了。比如:我想访问某个地址的数据,需要确定在哪一行page(row),在哪一列col(0~2047)。从NAND FLASH的地址周期中可以看出来,先发出2个col(列地址),再发出3个(Row)行地址。 下面是程序的编写:

nand读写原理(Flash操作原理及裸机程序分析)(6)

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

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

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

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(); } if (i == len) break; col = 0; page ; } nand_deselect(); }

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

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; } }

在init.c文件中的copy2sdram函数里面加上如下代码,用来支持NAND FLASH启动,当isBootFromNorFlash函数的返回值为1时,是从NOR FLASH启动,当isBootFromNorFlash函数的返回值为0时,是从NAND FLASH启动。

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

「新品首发」STM32MP157开发板火爆预售!首批仅300套

,