【韦东山旧1期学习笔记】09.S3C2440 Nand Flash实验(一)

术语解释

Program(编程)

此处的编程,不是写软件,写代码,而是对于硬件来说的,可以理解为对硬件编程,只不过其工具是硬件内部的逻辑,而不是你用的软件。对Nand Flash的编程,本质上就是实现写操作,将数据写到Nand Flash里面去,所以对于Nand Flash,可以简单的理解为 program编程=write写(数据)。

Datasheet(数据手册)

这个词,本来没啥好说的,接触多了,自然就知道了。但是对于和我类似,最开始接触的时候,就是没搞懂这个词的具体含义。其中文翻译,一般称作,数据手册,意思就是,一个关于描述硬件各个硬件特性,参数以及/或者如何操作,如何使用的文档。

Erasesize / Writesize

这个是Linux MTD中,关于块大小和页大小的别名,第一次见到的时候,把我搞糊涂了,后来才慢慢明白的。因为nand操作中写基本单位页,所以writesize对应的就是pagesize,即页大小。而擦除操作的基本单位是blocksize,块大小,所以也叫erasesize。

Spare Area / Redundant Area / OOB

nand flash中每一页对应一块区域,用于存放校验的ECC数据和其他一些信息,比如上层文件系统放的和自己文件系统相关的数据。这个区域在Linux MTD相关系统中被称作oob(out of band),可以翻译为带外。因此nand flash的一个页可以称作一个band,band之外,对应的就是指那个多出来的,特殊的区域了。而nand flash的datasheet中,一般称为spare area,可译为空闲区域,另外,在ID的含义解释中也叫做redundant area,可译为冗余区域,归根结底,都是一个概念。不要搞糊涂了就好。

Page Register(页寄存器)

nand flash硬件中的一块地方,叫做register,实际就是一个数据缓存,一个buffer,用于存放那些从flash读出来或者将要写入到flash中的数据。其实叫做页缓存更合适,更容易明白其含义。此页寄存器的大小=页大小+ oob 大小,即pagesize+oob,对于常见的页是2KB的,此页寄存器就是2KB+64=2112字节。

Chip和Plane

对于chip,其实任何某个型号的flash,都可以称其是一个chip,但是实际上,此处的chip是针对内部来说的,也就是某型号的flash,内部有几个chip,比如说三星的2GB的K9WAG08U1A芯片(可以理解为外部芯片/型号)内部装了2个单片是1GB的K9K8G08U0A,此时就称 K9WAG08U1A内部有2个chip,而有些单个的chip,内部又包含多个plane,比如上面的K9K8G08U0A内部包含4个单片是2Gb的Plane。只有搞清楚了此处的chip和plane的关系,才能明白后面提到的多页(Multi Plane / Multi Page)编程和交互(interleave)编程的含义。

Nor、Nand和eMMC

        Intel于1988年首先开发出Nor Flash 技术,彻底改变了原先由EPROM(Erasable Programmable Read-Only-Memory、可擦除可编程序只读存储器)和E2PROM(电可擦只读存储器Electrically Erasable Programmable Read-Only-Memory)一统天下的局面。紧接着,1989年,东芝公司发明了Nand Flash技术,强调降低每比特的成本,有更高的性能,并且像磁盘一样可以通过接口轻松升级。
        Nor Flash的特点是芯片内执行(XIP ,eXecute In Place),这样应用程序可以直接在Nor Flash闪存内运行,不必再把代码读到系统RAM中。Nor的传输效率很高,在1~4MB的小容量时具有很高的成本效益,但是很低的写入和擦除速度大大影响到它的性能。Nand的结构能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很快。应用Nand的困难在于Flash的管理需要特殊的系统接口。通常读取Nor的速度比Nand稍快一些,而Nand的写入速度比Nor快很多,在设计中应该考虑这些情况。
        Nor Flash占据了容量为1~16MB闪存市场的大部分,而Nand Flash用在8~128MB的产品当中,这也说明Nor主要应用在代码存储介质中,Nand适合于数据存储,Nand在CompactFlash、Secure Digital、PC Cards和MMC(多媒体存储卡Multi Media Card)存储卡市场上所占份额最大。
        因为多数微处理器与微控制器要求字节等级的随机存取,所以Nand Flash不适合取代那些用以装载程序的ROM。从这样的角度看来,Nand Flash比较像光盘、硬盘这类的次级存储设备。Nand Flash非常适合用于储存卡之类的大量存储设备。第一款创建在Nand Flash基础上的可移除式存储媒体是SmartMedia,此后许多存储媒体也跟着采用Nand Flash,包括MultiMediaCard、Secure Digital、Memory Stick与xD卡。
        eMMC存储器(Embedded Multi Media Card) 为MMC协会所订立的,eMMC 相当于 NandFlash+主控IC ,对外的接口协议与SD、TF卡一样,主要是针对手机或平板电脑等产品的内嵌式存储器标准规格。eMMC的一个明显优势是在封装中集成了一个控制器,它提供标准接口并管理闪存,使得手机厂商就能专注于产品开发的其它部分,并缩短向市场推出产品的时间。这些特点对于希望通过缩小光刻尺寸和降低成本的NAND供应商来说,同样的重要。
        eMMC由一个嵌入式存储解决方案组成,带有MMC(多媒体卡)接口、快闪存储器设备(Nand Flash)及主控制器,所有都在一个小型的BGA 封装。接口速度高达每秒52MBytes,eMMC具有快速、可升级的性能。同时其接口电压可以是 1.8v 或者是 3.3v。现在很多智能电视已经逐步抛弃Nor或Nand,使用更为先进的eMMC芯片,然而普通编程器无法读写eMMC芯片,新开发的可支持eMMC芯片烧写的编程器性价比高,还能支持Nor、Nand芯片,支持全面、功能强大。

Nand的特性

        对于Flash存储器件的可靠性需要考虑三点:

  • 位反转
  • 坏块
  • 可擦除次数
            所有的Flash器件都会遭遇位反转的问题。由于Flash固有的电气特性,在读写数据的过程中,偶尔会出现一个或几个比特位数据错误,并且Nand出现的概率远大于Nor。当位反转发生在关键的代码、数据上时,有可能导致系统崩溃。所以当确实发生了位反转时,就必须有检测和纠错机制。Nand Flash上发生位反转的概率更高,所以它使用ECC进行错误的检测和恢复。
            Nand Flash上面会有坏块随机分布,在使用前需要将坏块扫描出来,确保不再使用它们。嵌入式Linux对Nor、Nand Flash的软件支持都很成熟。在Nor Flash上常用jffs2文件系统,而在Nand上常用yaffs文件系统。在更底层,有MTD驱动程序实现对它们的读、写、擦除操作,它也实现了EDC/ECC校验。

Nand Flash的物理结构

在这里插入图片描述
由于我是用的JZ2440开发板上用的Nand Flash芯片的型号是三星的K9F2G08U0C-SCB0。这是一款容量为256M x 8bit的Nand Flash,数据总线宽度为8位。常用于消费类电子产品,它的引脚封装如下所示:
在这里插入图片描述
外部引脚介绍详情如下表所示:
在这里插入图片描述

功能结构图

在这里插入图片描述
上述结构图包含了以下10个功能部件:

  1. X-Buffers Latches & Decoders:用于行地址(页)
  2. Y-Buffers Latches & Decoders:用于列地址
  3. Command Register:用于命令字
  4. Control Logic & High Voltage Generator:控制逻辑及产生Flash所需高压
  5. Nand Flash Array:存储部件
  6. Data(Page) Register & S/A:页寄存器。当读写某页时,会先将数据读入/写入此寄存器,大小为2112字节
  7. Y-Gating
  8. I/O Buffers & Latches
  9. Global Buffers
  10. Output Driver
    由于该芯片为2Gbit,即256MB容量大小,故需要28根地址线才能完全访问整个地址空间。这28根地址线分为行地址和列地址,其中行地址17根线,列地址12根线。

存储单元组织结构

在这里插入图片描述
由上图也可知,K9F2G08U0C芯片存储结构被分为行列,每一行又叫做一页。行地址17根线,列地址12根线。故K9F2G08U0C芯片共有217=131072行(页),211x 8 + 64 x 8 = 2048 x 8 + 64 x 8 = 2112 x 8列。
        每一页的大小为(2K + 64)字节,包括用于正常存储数据的2KB空间和用于存储ECC的64字节空间组成。每一页正常数据空间列地址为0~2047,额外ECC空间列地址为2048~2111。
        一个块的大小是128K + 4KB,也就是64个页组成一个块。因为一共有217个页,所以一共有211=2048个块。在Nand Flash中,一个cell可以存储一个比特位,所以一个块共有132KB=‭135168‬Byte=‭1,081,344‬bit。也就是一个块总共含有1081344个cell。
        Nand Flash的写操作和读操作都是以页为单位进行的,而擦除操作则是以块为单位进行的。这表明了Nand Flash不能以比特位为单位进行擦除操作。
        命令、地址、数据都通过8个I/O口输入/输出,这种形式减少了芯片的引脚个数,使得在相同的结构下更容易升级到更大的容量。写入命令、地址或数据时,都需要将WE#、CE#信号同时拉低。数据在WE#信号的上升沿被Nand Flash锁存;命令锁存信号CLE、地址锁存信号ALE用来分辨、锁存命令或地址。
        我们知道,对于256MB的存储容量来说,访问时需要28根地址线信号,先发出列地址,后发出行地址(页地址)。

Nand Flash电路原理图

在这里插入图片描述

Nand Flash控制器

        Nand Flash控制器提供了几个寄存器来简化对Nand Flash的操作。比如要发出命令时,只需向NFCMMD寄存器写入相应的数值即可。之后,Nand控制器就会自动发出各种控制时序信号。
        访问Nand Flash时需要先发出命令,然后发出地址序列,之后进行读/写操作。同时需要使用各个使能信号来区分是命令、地址还是数据。S3C2440的Nand Flash控制器提供了NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT和其他与ECC有关的寄存器。

NFCONF寄存器

在这里插入图片描述
在这里插入图片描述
        该寄存器用来设置时序参数TACLS、TWRPH0、TWRPH1;同时设置数据总线的宽度。还有一些只读位,这些位由硬件相应管脚的高低电平决定,用于决定复位启动时的页大小和地址访问周期。

NFCONT寄存器

在这里插入图片描述
        该寄存器用于使能/禁止Nand Flash控制器、使能/禁止Nand Flash芯片片选信号、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定Nand Flash。

NFCMMD寄存器

用于发出Nand Flash操作命令,具体命令与具体型号的Nand Flash芯片有关,我们要结合具体的芯片来看。对于本实验中用到的K9F2G08U0C-SCB0芯片来说,其命令如下所示:
在这里插入图片描述

NFADDR寄存器

在这里插入图片描述
当写这个寄存器的时候,Nand Flash控制器将对Flash芯片发出地址时序信号。

NFDATA寄存器

在这里插入图片描述
只用到了该寄存器的最低8位,读、写此寄存器将分别会启动对Flash芯片的读数据或写数据操作。

NFSTAT寄存器

在这里插入图片描述
目前本实验只用到了最低比特位0,即表明Nand Flash芯片是处于繁忙工作中还是空闲就绪状态。

Nand Flash操作实验

初始化Nand Flash控制寄存器

  1. 我们首先设置NFCONF配置寄存器。
    由上述介绍可知,我们本实验使用的芯片是三星的K9F2G08U0C-SCB0。这是一款容量为256M x 8bit的Nand Flash,数据总线宽度为8位。所以配置数据总线宽度为8-bit,即NFCONF[0] = 0x0。
    下图是S3C2440的时序参数说明:
    在这里插入图片描述
    下图是K9F2G08U0C芯片的时序参数说明:
    在这里插入图片描述
    在这里插入图片描述
    对比图片可知,TACLS = max(tCLS, tALS) - tWP = 12 - 12 = 0 ns. (NFCONF[13:12] = 0x0)
    TWRPH0 = tWP = 12ns.
    又因为Duration = HCLK x ( TWRPH0 + 1 ),而HCLK为100MHz,所以 NFCONF[10:8] = 0x1
    TWRPH1 = tCH = 5ns.
    且Duration = HCLK x ( TWRPH1 + 1 ),所以NFCONF[6:4] = 0x0
    综上所述,NFCONF = (0x0 << 12) | (0x1 << 8) | (0x0 << 4) | (0x0 << 0);

  2. 我们配置NFCONT寄存器
    首先我们使能Nand Flash控制器,即NFCONT[0] = 0x1;
    然后我们先不选中Nand Flash芯片,即NFCONT[1] = 0x1;
    综上所述,NFCONT = (0x1 << 1) | (0x1 << 0);

Nand Flash控制器的初始化工作我们就做完了。

读取芯片id

根据K9F2G08U0C芯片数据手册,其读取id的操作时序图如下所示:
在这里插入图片描述
因此我们需要先片选Nand Flash芯片,然后发出读id命令0x90,之后发出地址0x00,在经过tAR + tREA时间之后,就可以开始读取数据了。通过之前的介绍,我们发现没有设置tAR和tREA,所以我们要在代码中使用软件延时来模拟该时序。
nand.c

#include "s3c2440_soc.h"
#include "myprintf.h"

void K9F2G08U0C_nand_init(void){
	NFCONF = (0x0 << 12) | (0x1 << 8) | (0x0 << 4) | (0x0 << 0);
	NFCONT = (0x1 << 1) | (0x1 << 0);
}

void nand_init(void){
	K9F2G08U0C_nand_init();
	printf("K9F2G08U0C init is done.\n\r");
}

void nand_selectChip(void){
	NFCONT &= ~(1 << 1);
}

void nand_deSelectChip(void){
	NFCONT |= (1 << 1);
}

void nand_cmd(unsigned char cmd){
	NFCMMD = cmd;
	volatile unsigned int idx = 0;
	for(idx = 0; idx < 10; idx++){
		//do nothing,delay
	}
	
}

unsigned char nand_ReadData(void){
	return NFDATA;
}

void nand_WriteData(unsigned char data){
	NFDATA = data;
}

void nand_addr(unsigned char addr){
	NFADDR = addr;
}

void nand_waitReady(void) {
	//0: NAND Flash memory busy
	//1: NAND Flash memory ready to operate
	while(!(NFSTAT & 0x1));
}

void nand_ReadID(void) {
	nand_selectChip();
	nand_cmd(0x90);
	nand_addr(0x00);
	nand_waitReady();
	unsigned char data[5];
	unsigned int idx = 0;
	for(idx = 0; idx < 5; idx++){
		data[idx] = nand_ReadData();
	}
	//reset nand chip
	nand_cmd(0xFF);
	nand_waitReady();
	nand_deSelectChip();
	printf("Nand Chip ID: 0x%x-0x%x-0x%x-0x%x-0x%x\r\n", data[0],
			data[1], data[2], data[3], data[4]);
}

main.c

#include "myprintf.h"
#include "nand.h"
#include "uart.h"

void test_nand();

int main(void) {
	uart0_init();
	printf("%s\n\r", "Nand Flash Test.");

	test_nand();
	return 0;
}

void test_nand(){
	unsigned char op;
	nand_init();

	while(1) {
		printf("Now testing nand flash...\n\r");
		printf("----> s/S: Scan Nand Flash Chip ID.\n\r");
		printf("----> e/E: Exit the test.\n\r");
		printf("Please select one operation: ");
		op = uart0_getc();
		uart0_putc(op);
		printf("\n\r");
		switch(op){
			case 's':
			case 'S':
				nand_ReadID();
				break;
			case 'e':
			case 'E':
				printf("Test is done. Bye.\n\r");
				return;
			default:
				//do nothing.
				printf("Invalid input.Please try again.\n\r");
				break;
		}
		printf("\n\r");
	}
}

因为我现在的编译生成的BIN文件的大小已经超过了4KB,但是我还没有进行到Nand Flash数据读取步骤,所以只能编译并烧写到Nor Flash中进行测试。效果图如下所示:
在这里插入图片描述

芯片ID的含义

通过程序我们获得了K9F2G08U0C的芯片ID,即0xec-0xda-0x10-0x95-0x44。关于ID的具体含义,如下表所示:
在这里插入图片描述
        我们分别来解释每一个字节对应的意义是什么。
        第一个字节表示制造商Code(0xEC),第二个字节表示设备Code(0xDA)。

在这里插入图片描述
由表中定义可以看出:

  1. Internal Chip Number意思是,内部芯片有几颗。
    有些型号的Nand Flash,为了实现更高的容量,在芯片内部封装了多个芯片。
    比如三星的K9WAG08U1A容量是2GB,内部是装了2个单片是1GB的K9K8G08U0A,对应地,里面要包含2个片选CE1和CE2(均是低电平有效),而4GB的K9NBG08U5A包含了4片的K9K8G08U0A。

  2. Cell Type:SLC / MLC
    bit2&bit3表示的是芯片的类型,是SLC还是某种MLC:

    Bit2,bit3=0x00 : SLC,简单说就是内部单个存储单元,存储一位的数据,所能表示的数值只有0,1,也就需要两种不同的电压来表示,所以叫做2 Level的Cell。
    Bit2,bit3=0x01/0x10/0x11 : 4 /8/16 Level Cell,都叫做MLC,其含义是内部单个存储单元设计成可以表示多个,即4/8/16个不同的电压,对应地,可以表示2,3,4位的数据。
    这类的MLC的nand flash,由于单个存储单元,要存储更多的数据,所以内部结构更复杂,读取和写入数据的逻辑更复杂,相对数据出错的几率也比SLC要大。

    所以,一般MLC的使用,都需要检错和纠错能力更强的硬件或软件算法,以保证数据的正确性。

    软件实现此类的多位数据的检错和纠错的效率相对较低,一般是硬件本身就已经提供此功能。

    对应的其为硬件ECC,也就是Linux内核MTD中的HW_ECC。

    其他关于SLC/MLC的更详细解释,感兴趣的可以去看另一个帖子:【详解】如何编写Linux下Nand Flash驱动

  3. Number of Simultaneously Programmed Pages 可以对几个页同时编程/写。

    此功能简单的说就是,一次性地写多个页的数据到对应的不同的页。

    对应支持此操作的,硬件上必须要有多个plane,而每个plane,都有一个自己的页寄存器。

    比如K9K8G08U0A有4个plane,分别叫做,plane0,plane1,plane2,plane3。

    它们共分成2组,plane0和plane1,plane2和plane3。如图:
    在这里插入图片描述
    在多页编程时候,只能对某一组中的两个plane操作,不允许类似于plane0和plane2或plane3一起去做多页编程。

    以plane0和plane1为例,在实现具体的编程动作之前,将你要写入的2个页的数据,分别写入plane0和plane1中的页寄存器,然后才能发命令,去实现具体的编程操作。

    正是因为多页编程需要底层的多plane支持,底层实现的时候,是同时对多个plane编程,所以,也被叫做Multi Plane Program

  4. Interleave Program Between Multiple chips
    交错,从字面意思就可以看出,此操作涉及对象就不止一个。

    交错编程,就是对多个chip,交错地进行编程,先对一个编程,充分利用第一个编程过程中需要等待的时间,转去操作另一个,以此实现总体效率的提高。

    如果支持Interleave Program的话,那么前面的chip number必然大于1。

  5. Cache Program
    Cache读
    在开始了一次cache读之后,在你把数据读出去的这段时间,nand flash会自动地把下一页的数据读取出来放到页寄存器。

    Cache写
    在你写入数据的时候,对应的内存中的数据,不是直接写到页寄存器中,而是到了cache buffer中

    然后再发cache 写的命令,此时,数据才从cache buffer中,转递到页寄存器中,然后把数据一点点编程到nand flash

    此时,你可以去利用页编程的时间,去准备下一次的数据,然后依此地写入下一个页。

    Cache读或写,是充分利用了读一页数据出来,或者将一页数据写到flash里面去的时间,去准备新的一页的数据,这样就可以实现连续的读或写,大大提高读写效率。

第三个字节表示芯片内部编码。0x10,即0b00010000,表示:

  • 内部芯片数量为1个
  • Cell的Type为2,即每个cell有两种状态(电平等级),可表示1bit数据(0或者1)
  • 同时可以写两页
  • 不支持多芯片交错写
  • 不支持cache写操作

这里多解释一点,关于Cell的类型:2 Level Cell即每个cell有两种状态(电平等级),可表示1bit数据(0或者1);4 Level Cell即每个cell有四种状态,可表示2bit数据(00、01、10、11),以此类推。其中,2 Level Cell又称为SLC(Single Layer Cell 单层单元),其他为MLC(Multi-Level Cell多层单元)。
在这里插入图片描述
第四个字节是0x95,即0b1001 0101,表示页大小为2KB,块大小为128KB,冗余区大小为16byte/512byte, I/O宽度为8bit,最小串行访问时间是25ns。
我们计算页大小可以使用 1024 << (extid & 0x3) 即可。
计算块大小,具体算法很清楚,算出是64KB的多少倍,得出总大小。此处之所以是64KB为基础,是因为已知最小的blocksize,就是64KB的。
Organization
X8/X16,表示的是,硬件I/O位宽(Bus Width)是8位的还是16位的。目前大多数,都是x8的。旧的nand flash的一些参数,是知道设备ID后,可以直接从表中读取出来的。
根据读取出来的生长厂商的ID,去和表中对应项匹配,找到是哪家的nand flash芯片。
其中,nand_manuf_ids和上面nand_flash_ids类似,也是个预先定义好的数组,其定义和nand_flash_ids同文件drivers\mtd\nand\nand_ids.c中:

/*
*	Manufacturer ID list
*/
struct nand_manufacturers nand_manuf_ids[] = {
	{NAND_MFR_TOSHIBA, "Toshiba"},
	{NAND_MFR_SAMSUNG, "Samsung"},
	{NAND_MFR_FUJITSU, "Fujitsu"},
	{NAND_MFR_NATIONAL, "National"},
	{NAND_MFR_RENESAS, "Renesas"},
	{NAND_MFR_STMICRO, "ST Micro"},
	{NAND_MFR_HYNIX, "Hynix"},
	{NAND_MFR_MICRON, "Micron"},
	{NAND_MFR_AMD, "AMD"},
	{0x0, "Unknown"}
};          

对应的各个厂家的宏和结构体的定义是在:include\linux\mtd\nand.h中:

/*
 * NAND Flash Manufacturer ID Codes
 */
#define NAND_MFR_TOSHIBA	0x98
#define NAND_MFR_SAMSUNG	0xec
#define NAND_MFR_FUJITSU	0x04
#define NAND_MFR_NATIONAL	0x8f
#define NAND_MFR_RENESAS	0x07
#define NAND_MFR_STMICRO	0x20
#define NAND_MFR_HYNIX		0xad
#define NAND_MFR_MICRON		0x2c
#define NAND_MFR_AMD		0x01

/**
 * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
 * @name:	Manufacturer name
 * @id:		manufacturer ID code of device.
*/
struct nand_manufacturers {
	int id;
	char * name;
};

在这里插入图片描述

设置坏块的标记位置。

关于nand flash的small block和large block,据我了解,好像就是对应的small pagesize和large pagesize,而此处的大小,是针对于旧的nand flash,其页大小pagesize是512

所以,Small block就是页大小是512B的nand flash,而larger block就是新的,页大小大于512B的,比如2KB,4KB等的nand flash。

下面的宏定义在include\linux\mtd\nand.h中:

/*
* Constants for oob configuration
*/
#define NAND_SMALL_BADBLOCK_POS		5
#define NAND_LARGE_BADBLOCK_POS		0

约定俗成的,small block的nand ,坏块标记在byte5,而large block的nand flash在byte0。

关于坏块标记,实际情况更复杂些:

对于2K页的nand flash,标记位置都是页内oob开始处,都是非0xFF表示坏块,
但是,对于是第几页,不同nand flash就有不同的规定了:
有些nand flash,是标记在坏块的第一个页(或者是第二个页,这点是考虑到,万一第一个页是坏的,所以才做此规定的。一般都是在第一个页处做标记)
比如三星的多数SLC,Hynix等
另一些,是在一个块内的最后一页或倒数第二页做此标记,比如samsung MLC , Numonyx等。
所以,真正比较完整的检查坏块的做法,至少要检测块内第一,第二,倒数第一,倒数第二页,是否是0xFF,才能比较全面的判断是否是坏块的。

读取芯片 块大小 和 页大小

我们通过上述分析可知,读取ID时的第四个数据中包含有页大小和块大小的信息。所以我们据此来读取芯片的块大小和页大小。
nand.c

#include "s3c2440_soc.h"
#include "myprintf.h"

void K9F2G08U0C_nand_init(void){
	NFCONF = (0x0 << 12) | (0x1 << 8) | (0x0 << 4) | (0x0 << 0);
	NFCONT = (0x1 << 1) | (0x1 << 0);
}

void nand_init(void){
	K9F2G08U0C_nand_init();
	printf("K9F2G08U0C init is done.\n\r");
}

void nand_selectChip(void){
	NFCONT &= ~(1 << 1);
}

void nand_deSelectChip(void){
	NFCONT |= (1 << 1);
}

void nand_cmd(unsigned char cmd){
	NFCMMD = cmd;
	volatile unsigned int idx = 0;
	for(idx = 0; idx < 10; idx++){
		//do nothing,delay
	}
	
}

unsigned char nand_ReadData(void){
	return NFDATA;
}

void nand_WriteData(unsigned char data){
	NFDATA = data;
}

void nand_addr(unsigned char addr){
	NFADDR = addr;
}

void nand_waitReady(void) {
	//0: NAND Flash memory busy
	//1: NAND Flash memory ready to operate
	while(!(NFSTAT & 0x1));
}

void nand_ReadID(void) {
	nand_selectChip();
	nand_cmd(0x90);
	nand_addr(0x00);
	nand_waitReady();
	unsigned char data[5];
	unsigned int idx = 0;
	for(idx = 0; idx < 5; idx++){
		data[idx] = nand_ReadData();
	}
	//reset nand chip
	nand_cmd(0xFF);
	nand_waitReady();
	nand_deSelectChip();
	printf("Nand Chip ID: 0x%x-0x%x-0x%x-0x%x-0x%x\r\n", data[0],
			data[1], data[2], data[3], data[4]);
	unsigned int blockSize = (64 << ((data[3] >> 4) & 0x3));
	unsigned int pageSize = (1 << (data[3] & 0x3));
	printf("Nand Flash Block Size: %dKB, Page Size: %dKB\n\r", blockSize, pageSize);
}

编译烧写到开发板上Nor Flash,并以Nor方式启动,观察输出如下所示:
在这里插入图片描述
通过输出内容可知,该芯片的块大小为128KB,页大小为2KB,与数据手册的参数一致。

发布了26 篇原创文章 · 获赞 2 · 访问量 1071

猜你喜欢

转载自blog.csdn.net/BakerTheGreat/article/details/104323306