韦一之Nor flash和Nand flash读写(015-016课)

编程入门 行业动态 更新时间:2024-10-25 14:31:28

插:网络上有2440中文手册,看起来挺方便。

Nor和Nand flash概述

nor flash不可以直接写,否则就跟内存一样了,上面的程序很容易被破坏。
但是读nor就和读内存一样,发出地址直接读就行了。

nand的接线少,数据线上既传输命令又传输数据还传输地址!
nor和nand都是不能直接写的,要发出一定的命令。
nor贵且容量小,为什么还用呢?
优点就是无位反转和坏块,如果要高可靠性地存储程序,尤其是重要的需要稳定的程序,错一点都不行的,一般就用nor。
海量数据用nand存储,比如视频监控的信息。
手机里面都是用的nand,现在技术好了,位反转和坏块出现的几率也小了。万一真的出现了,重新刷个固件也就好了。

Nor Flash支持XIP,即代码可以直接在Nor Flash上执行,无需复制到内存中。这是由于NorF lash的接口与RAM完全相同,可以随机访问任意地址的数据。Nor Flash进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小。NAND Flash进行擦除和写操作的效率更高,并且容量更大。一般而言,Nor Flash用于存储程序,NAND Flash用于存储数据。基于NAND Flash的设备通常也要搭配Nor Flash以存储程字。
cpu可以直接从nor上面读取指令执行,但是nand就得把程序弄到sram中了,不过这个是2440帮我们做好的,不用管,自动拷贝4K到片内内存SRAM。

Flash存储器件由擦除单元(也称为块)组成,当要写某个块时,需要确保这个块己经
被擦除。Nor Flash的块大小范围为64kB、128kB:NAND Flash的块大小范围为8kB,64kB,擦/写一个Nor Flash块需4s,而擦/写一个NAND Flash块仅需2ms。Nor Flash的块太大,不仅增加了擦写时间,对于给定的写操作,Nor Flash也需要更多的擦除操作——特别是小文件,比如一个文件只有IkB,但是为了保存它却需要擦除人小为64kB—128kB的Nor Flash块。

Nor Flash的接口与RAM完全相同,可以随意访问任意地址的数据。而NAND Flash的
接口仅仅包含几个I/O引脚,需要串行地访问。NAND Flash一般以512字节为单位进行读写。这使得Nor Flash适合于运行程序,而NAND Flash更适合于存储数据。

容量相同的情况下,NAND Flash的体积更小,对于空间有严格要求的系统,NAND Flash可以节省更多空间。市场上Nor Flash的容量通常为IMB~4MB(也有32MB的Nor Flash),NAND Flash的容量为8MB~512MB。容量的差别也使得Nor Flash多用于存储程序,NAND Flash多用于存储数据。

对于Flash存储器件的可靠性需要考虑3点:位反转、坏块和可擦除次数。所有Flash器件都遭遇位反转的问题:由于Flash固有的电器特性,在读写数据过程中,偶然会产生一位或几位数据错误(这种概率很低),而NAND Flash出现的概率远大于Nor Flash,当位反转发生在关键的代码、数据上时,有可能导致系统崩溃。当仅仅是报告位反转,重新读取即可:如果确实发生了位反转,则必须有相应的错误检测/恢复措施。在NAND Flash上发生位反转的概率史高,推荐使用EDC/ECC进行错误检测和恢复。NAND Flash上面会有坏块随机分布在使用前需要将坏块扫描出来,确保不再使用它们,否则会使产品含有严重的故障。NAND Flash每块的可擦除次数通常在100000次左右,是Nor Flash的10倍。另外,因为NAND Flash的块大小通常是NorF lash的1/8,所以NAND Flash的寿命远远超过Nor Flash。

嵌入式Linux对Nor、NAND Flash的软件支持都很成熟。在Nor Flash上常用jffs2文
件系统,而在NAND Flash常用yaffs文件系统。在更底层,有MTD驱动程序实现对它们的读、写、擦除操仵,它也实现了EDC/ECC校验。

Nand Flash只有8个I/O引脚的好处:

  1. 减少外围引脚
  2. 提高系统的可扩展性,因为没有像其他设备一样用物理大小对应的完全数目的addr引脚,在芯片内部换了芯片的大小等的改动,对于用全部的地址addr的引脚,那么就会引起这些引脚数目的增加,比如容量扩大一倍,地址空间/寻址空间扩大一倍,所以,地址线数目/addr引脚数目,就要多加一个,而对于统一用8个I/O的引脚的Nand Flash,由于对外提供的都是统一的8个引脚,内部的芯片大小的变化或者其他的变化,对于外部使用者(比如编写nand flash驱动的人)来说,不需要关心,只是保证新的芯片,还是遵循同样的接口,同样的时序,同样的命令,就可以了。这样就提高了系统的扩展性。

nor flash不需要初始化,但是nand flash需要!详情见nand初始化章节!

补:
sdram需要初始化才能读写,实际是配置些寄存器满足时序。
nor flash呢?
要读些nor flash也是要配置寄存器满足时序的,我们前面的代码中都没有配置,因为nor只需要配置bank0的寄存器,只有两个需要设置,默认值却都可以工作,所以没有配置。
而是直接可读: volatile unsigned short *p = (volatile unsigned short *)(base + offset); return *p;
(寄存器配置见内存控制器章节,nor和sdram同属于内存控制器管理,用一套寄存器。只不过sdram寄存器设置更复杂,需要专门初始化用来配置时序)
但是我们的代码放在nor可以直接启动,貌似xip片内执行代码就不需要配置时序了??????
(可能也是因为不需要配置就可以读吧,毕竟CPU也是要从nor中读取指令运行的。)

eXecute In Place,即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.
(注:片内执行不是说程序在存储器内执行哦,CPU的基本功能就是取指、译码和执行。norflash能在芯片内执行,就是指CPU的取指模块能够直接从norflash中把指令取出来,供后面的译码和执行模块使用。

那么为什么norflash可以实现XIP,而nandflash就不可以呢?
芯片内执行主要是是看芯片可不可以线性存储代码(假如硬件支持芯片接口),只要能保证芯片的存储空间是线性的(也就是无坏块),都可以片上执行
在读取Flash时候,容易出现“位翻转(bitconvert)
在Flash的位翻转(一个bit位发生翻转)现象上,NAND的出现几率要比NorFlash大得多。这个问题在Flash存储关键文件时是致命的,所以在使用NandFlash时建议同时使用EDC/ECC等校验算法。 ”
但是,如果能保证不出错,也还是可以进行XIP,可以在其上执行代码的:
“所谓XIP,就是CODE是在FLASH上直接运行. NANDFLASH只是不适合做XIP,但并不是不能做XIP“

用Uboot操作Nor Flash查看其特性

(下文中有uboot操作nand,跟nor不同,值得关注。nor是直接操作内存,nand是操作寄存器,因为nand时序复杂,有自己的控制器方便操作。对uboot而言,还是差不多的)

uboot有命令可以访问内存,nor又可以像内存一样访问,所有把uboot下载到nor中启动,然后就可以用UBOOT的命令读写nor了,不用编程就可以读写nor了,方便我们学习和验证nor的特征和命令格式等。比如下表:

下面我们使用u-boot来体验Nor Flash的操作(开发板设置Nor启动,进入u-boot)。
开发板一定要设为nor启动,因为nor启动时,cpu访问0开始的地址就是nor地址了。
而在nand启动的,CPU可以看到的零地址是片内内存SRAM,看不到NOR,更无法读写nor了。

首先要使用OpenJTAG烧写UBOOT到Nor Flash
OpenJTAG开发了一个工具:oflash,用它来烧写。

那么我们怎么用u-boot来操作呢?
Nor Flash手册里都会有一个命令的表格,如图:

上面的word和byte指的是,这个nor芯片(很多的Nor Flash)可以配置成位宽是16或者是8。
(word是16?!!!!!)
对于我们使用的jz2440开发板,我们的nor接了16根数据线,位宽是16,看word行就好了。

复位(reset):往任何一个地址写入F0。

怎样读ID呢?
读ID(ReadSiliconID):往Nor Flash的555地址写AA,再往2AA的地址写入55,再往555的地址写入90,然后就可以读数据了。通过读ADI这个地址,就可以读到DDI数据了。

ADI是什么呢?A1=0,A0=0的时候读到的是厂家ID。0/1时读出的是设备ID。
这A1,A0是什么意思呢???????????????????
根据后面的实验,貌似是00表示0地址,01就表示地址1。不是很明白。

后面实验可以看一下读出的内容和这里是否对应。

实验1:
读数据:在u-boot上执行:md.b0(这应该就是一个读内存的指令)
结果(和我们烧进去的数据完全一样):

这些数据就是我们烧写的uboot,在电脑上用二进制工具打开uboot.bin:

可见,内容和上面是一样的。
可以得出结论:u-boot可以像读内存一样来读nor flash。

实例2
读ID(参考Nor手册):
往地址555H写入AAH(解锁)
往地址2AAH写入55H(解锁)
往地址555H写入90H(命令)
读0地址得到厂家ID(C2H)
读1地址得到设备ID(22DAH或225BH)
退出读ID状态:给任意地址写F0H就可以了。

容易忘记:
2440发出(555H<<1)地址,Nor Flash才能收到555H这个地址。

其中涉及的地址的变换容易忘记做!!2440发出的地址其实就是nor需要的地址×2。

1).当执行过
md.w 0 1
结果(输出厂家ID):
00000000:00c2…(00c2就是厂家ID)
2).当执行过
md.w 2 1
结果(输出设备ID):
00000002:2249I"(2249就是设备ID)
3).当执行
mw.w 0 f0
就退出读ID的状态,
执行:
md.b0
结果:
00000000:17.(读到的就是Nor Flash地址·0的数据)

Nor Flash的两种规范:
两种规范,貌似就是对应两种接口。

通常内核里面要识别一个 Nor Flash 有两种方法:
一种是 jedec 探测,读取Flash的制造商ID和设备ID(在内核里有这个nor flash 的所有信息,换另外一款芯片时,若内核里没有这款芯片的描述,需手动添加该器件的各种参数),以确定Flash的大小和算法。
就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID 一一比较,如果发现相同的,就使用该数组的参数。
(jedec就是读ID的命令,发出命令实现识别nor,和代码中的数组信息进行匹配。老式的norflash规范都是要先读id然后跟数组比较,如果数组中没有对应的信息还得修改数组的内容使有匹配的,比较麻烦。)(貌似芯片中不带有更多信息,都靠软件数组的内容获取信息。)
(可是这里nor flash的信息又可以通过发上面表格的命令获取,貌似 jedec 探测也能获取信息呀????????上面表格table5中的命令是什么模式的呢????好像只有最后一个是进入cfi模式呀???????)
总结:上面表格中nor的大部分命令都是和这两个规范无关的。两种规范仅是针对如何获取某些特定描述信息而言的,(具体可以查看cfi的命令都是查询些什么)。和规范相关的只有两个命令:

查询ID,后续通过jedec规范得到那些信息。

进入cfi模式,通过后续cfi的命令查询哪些信息。

jedec规范对应的内核中的数组:

jedec 探测的优点就是简单,缺点是如果内核要支持的 flash 种类很多,这个数组就会很庞大。内核里面用 jedec 探测一个芯片时,是先通过发命令来获取 flash 的 ID,然后和数组比较,但是 flash.c 中连 ID 都是自己通过宏配置的。

② 一种是 CFI(common flash interface)探测,公共Flash接口。
用来帮助程序从Flash芯片中获取操作方式信息,就是直接发各种命令来读取芯片的信息,比如 ID、容量等,芯片本身就包含了电压有多大,容量有有多少等信息。
新的nor flash规范让nor flash去支持 CFI接口,芯片内部本身就带有各种属性,可以直接读出来。换芯片时,不需修改内核代码。
而上面的EDEC用来帮助程序读取Flash的制造商ID和设备ID(在内核里有这个nor flash 的所有信息,换另外一款芯片时,若内核里没有这款芯片的描述,需手动添加该器件的各种参数),以确定Flash的大小和算法,如果芯片不支持CFI,就需使用JEDEC了。

下面对在Nor Flash上操作,2440上操作,U-BOOT上操作cfi 探测(读取芯片信息)进行比较参考芯片手册。

往nor flash的55H地址写入98H,就会进入cfi 接口查询的模式。

…中间省略很多对应于cfi接口查询信息的命令。


往nor flash的55H地址写入98H,就会进入cfi 接口查询的模式之后,读取读地址10H就可以得到0051,读地址11H得到0052,读地址12H得到0059 (连起来其实就是cfi)
读地址27H得到容量。

实验现象:

15的十进制就是21,2的21次方=2M,所以这个nor flash就是两兆的。

读完信息,怎么退出CFI模式呢?
往任意一个地址写F0就可以了。如mw.w 0 f0

(上文中jedec模式下可以发命令读取ID和软件中数组比较。读取ID后怎么退出读ID的状态,也是任意地址写F0,如mw.w 0 f0!)

只有退出了上面这些查询的模式,才能读取nor上实际的数据的(像读内存那样)。

补充:手册表格里下面两个区域是什么意思呢?

有的norflash可以支持:前面一部分32K擦除,后面一个区域是64K为单位擦除。

用Uboot写nor flash

上面说的都是读nor,写是怎样的?
nor是不可以直接像内存一样写的!

注意,地址要选大一点,我们uboot是放在nor中的,做这个实验如果写在前面的地址会破坏uboot的。

怎样把数据写进Nor Flash进去呢?
写数据之前必须保证,要写的地址是擦除的。
因为nor的写操作只能把1写成0,0是不能写成1的。而擦除是写成全1的。
我们烧写时,如果上面的数据,不是0ffff,就是没有被擦除过,我们就要先擦出,擦除完后,才可以烧写,擦除烧写的命令可以从芯片手册里面获得。

芯片手册中命令表格查到:

下面是Nor Flash的写操作,如下表:

1).U-BOOT执行完上述指令后,0x1234,就被写到0x100000地址处,
执行:
md.w1000001
结果(1234被写进去):
00100000:1234 4
从这里可以看出来U-BOOT的操作不是很复杂。

2).我们再次往0x100000地址处,写入0x5678,执行如下命令,出错!!!!易错点!
mw.w aaa aa
mw.w 554 55
mw.w aaa a0
mw.w 100000 5678
查看0x100000地址处的数据
md.w 100000 1
结果:
00100000:12300.
0x100000地址处的数据不是0x5678,写操作失败,失败的原因是,原来的数据已经是0x1234不是全0xffff,再次写操作失败,(Nor Flash只有先擦出,才能烧写)。


注意擦除:

SA表示的是这个扇区地址。

Nor Flash编程,代码读写nor

上面的内容回了,这里挺简单的,视频不看了,笔记不记了。
详情参考https://blog.csdn/thisway_diy/article/details/79397638

这里部分代码挺有意思,复制过来。

最关键的基础函数,值得学习:

#define NOR_FLASH_BASE  0  /* jz2440, nor-->cs0, base addr = 0 */


/* 比如:   55H 98 
 * 本意是: 往(0 + (0x55)<<1)写入0x98
 */
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	*p = val;
}

/* offset是基于NOR的角度看到,往NOR Flash某个地址发送指令 */
void nor_cmd(unsigned int offset, unsigned int cmd)
{
	nor_write_word(NOR_FLASH_BASE, offset, cmd);
}

/*nor_read_word函数是从NOR Flash 读取两个字节(本开发板位宽16bit),读取数据的地址,是基于2440,所以读取NOR Flash某个地址上的数据时,需要把NOR Flash对应的地址左移一位(地址乘以2)。*/
unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
	return *p;
}

/*读取地址中的数据
向nor_dat函数中写入NOR Flash某个地址,返回该NOR Flash地址上的数据。*/
unsigned int nor_dat(unsigned int offset)
{
	return nor_read_word(NOR_FLASH_BASE, offset);
}

//等待烧写完成 : 读数据, Q6无变化时表示结束 (参考芯片手册),
void wait_ready(unsigned int addr)
{
	unsigned int val;
	unsigned int pre;

	pre = nor_dat(addr>>1);
	val = nor_dat(addr>>1);
	while ((val & (1<<6)) != (pre & (1<<6)))
	{
		pre = val;
		val = nor_dat(addr>>1);		
	}
}

翻看nor flash芯片手册,
发出program,数据写入命令后,怎么判断写入结束呢?

上面提到查询polling一个数据位。

toggle就是变化的意思,貌似这里如果是还在写入的过程,那么每读一次它就会变一个值。第一次读可能是0,下次读就是1。当我们两次读到Q6的值没变化的时候,就说明读写已经完成了。

下面是main直接调用的大函数,自己创建个菜单的这种格式,自己值得学一学

	void nor_flash_test(void)
233	{
234		char c;
235
236		while (1)
237		{
238			/* 打印菜单, 供我们选择测试内容 */
239			printf("[s] Scan nor flash\n\r");
240			printf("[e] Erase nor flash\n\r");
241			printf("[w] Write nor flash\n\r");
242			printf("[r] Read nor flash\n\r");
243			printf("[q] quit\n\r");
244			printf("Enter selection: ");
245
246			c = getchar();
247			printf("%c", c);
248
249			/* 测试内容:
250			 * 1. 识别nor flash
251			 * 2. 擦除nor flash某个扇区
252			 * 3. 编写某个地址
253			 * 4. 读某个地址
254			 */
255			switch (c)		 
256			{
257				case 'q':
258				case 'Q':
259					return;
260					break;
261				
262				case 's':
263				case 'S':
264					do_scan_nor_flash();
265					break;
266
267				case 'e':
268				case 'E':
269					do_erase_nor_flash();
270					break;
271
272				case 'w':
273				case 'W':
274					do_write_nor_flash();
275					break;
276
277				case 'r':
278				case 'R':
279					do_read_nor_flash();
280					break;
281				default:
282					break;
283			}
284		}
285	}

功能代码如下:

/* 进入NOR FLASH的CFI模式
 * 读取各类信息
 */
void do_scan_nor_flash(void)
{
	char str[4];
	unsigned int size;
	int regions, i;
	int region_info_base;
	int block_addr, blocks, block_size, j;
	int cnt;

	int vendor, device;
	
	/* 打印厂家ID、设备ID */
	nor_cmd(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0x90);    /* read id */
	vendor = nor_dat(0);
	device = nor_dat(1);
	nor_cmd(0, 0xf0);        /* reset */
	
	nor_cmd(0x55, 0x98);  /* 进入cfi模式 */

	str[0] = nor_dat(0x10);
	str[1] = nor_dat(0x11);
	str[2] = nor_dat(0x12);
	str[3] = '\0';
	printf("str = %s\n\r", str);

	/* 打印容量 */
	size = 1<<(nor_dat(0x27));
	printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size/(1024*1024));

	/* 打印各个扇区的起始地址 */
	/* 名词解释:
	 *    erase block region : 里面含有1个或多个block, 它们的大小一样
	 * 一个nor flash含有1个或多个region
	 * 一个region含有1个或多个block(扇区)

	 * Erase block region information:
	 *    前2字节+1    : 表示该region有多少个block 
	 *    后2字节*256  : 表示block的大小
	 */

	regions = nor_dat(0x2c);
	region_info_base = 0x2d;
	block_addr = 0;
	printf("Block/Sector start Address:\n\r");
	cnt = 0;
	for (i = 0; i < regions; i++)
	{
		blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
		block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
		region_info_base += 4;

//		printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);

		for (j = 0; j < blocks; j++)
		{
			/* 打印每个block的起始地址 */
			//printf("0x%08x ", block_addr);
			printHex(block_addr);
			putchar(' ');
			cnt++;
			block_addr += block_size;
			if (cnt % 5 == 0)
				printf("\n\r");
		}
	}
	printf("\n\r");
	/* 退出CFI模式 */
	nor_cmd(0, 0xf0);
}

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

	printf("erasing ...\n\r");
	nor_cmd(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0x80);	 /* erase sector */
	
	nor_cmd(0x555, 0xaa);    /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(addr>>1, 0x30);	 /* 发出扇区地址 */
	wait_ready(addr);
}

/*开发板上的NOR Flash的位宽是16bit,所以可以把要写的数据构造出16bit然后在写进NOR Flash中。*/
void do_write_nor_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");

	/* str[0],str[1]==>16bit 
	 * str[2],str[3]==>16bit 
	 */
	i = 0;
	j = 1;
	while (str[i] && str[j])
	{
		val = str[i] + (str[j]<<8);
		
		/* 烧写 */
		nor_cmd(0x555, 0xaa);	 /* 解锁 */
		nor_cmd(0x2aa, 0x55); 
		nor_cmd(0x555, 0xa0);	 /* program */
		nor_cmd(addr>>1, val);
		/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
		wait_ready(addr);

		i += 2;
		j += 2;
		addr += 2;
	}

	val = str[i];
	/* 烧写 */
	nor_cmd(0x555, 0xaa);	 /* 解锁 */
	nor_cmd(0x2aa, 0x55); 
	nor_cmd(0x555, 0xa0);	 /* program */
	nor_cmd(addr>>1, val);
	/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
	wait_ready(addr);
}
void do_read_nor_flash(void)
{
	unsigned int addr;
	volatile unsigned char *p;
	int i, j;
	unsigned char c;
	unsigned char str[16];
	
	/* 获得地址 */
	printf("Enter the address to read: ");
	addr = get_uint();

	p = (volatile unsigned char *)addr;

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

NAND_FLASH操作原理

原理图:

八根数据线,其余的引脚什么意思呢?
当ALE为高电平时数据线上传输的是地址,低电平是传输的是数据。

对于一个引脚定义,有些字母上面带一横杠的,或者在字母后面加“#”,或者字母前面有个n,那都是数字电路中说明此引脚/信号是低电平有效的。否则都是高电平有效。

Nand Flash只有8个I/O引脚的好处:

  1. 减少外围引脚:相对于并口(Parellel)的Nor Flash的48或52个引脚来说,的确是大大减小了引脚数目,这样封装后的芯片体积,就小很多。现在芯片在向体积更小,功能更强,功耗更低发展,减小芯片体积,就是很大的优势。同时,减少芯片接口,也意味着使用此芯片的相关的外围电路会更简化,避免了繁琐的硬件连线。
  2. 提高系统的可扩展性,因为没有像其他设备一样用物理大小对应的完全数目的addr引脚,在芯片内部换了芯片的大小等的改动,对于用全部的地址addr的引脚,那么就会引起这些引脚数目的增加,比如容量扩大一倍,地址空间/寻址空间扩大一倍,所以,地址线数目/addr引脚数目,就要多加一个,而对于统一用8个I/O的引脚的Nand Flash,由于对外提供的都是统一的8个引脚,内部的芯片大小的变化或者其他的变化,对于外部使用者(比如编写nand flash驱动的人)来说,不需要关心,只是保证新的芯片,还是遵循同样的接口,同样的时序,同样的命令,就可以了。这样就提高了系统的扩展性。

NAND FLASH是一个存储芯片
那么: 这样的操作很合理”读地址A的数据,把数据B写到地址A”。
原理图上NAND FLASH和S3C2440之间只有数据线,怎么传输地址?
复用!在DATA0~DATA7上既传输数据,又传输地址。(对NAND FLASH的操作还需要发出命令,所以数据线上还传输命令)。
当ALE为高电平时传输的是地址,当CLE为高电平时传输的是命令。当ALE和CLE都为低电平时传输的是数据。

怎么操作NAND FLASH呢?
根据NAND FLASH的芯片手册,一般的过程是:
发出命令
发出地址
发出数据/读数据

命令表格:


时序图解析:
① 8条数据线上发出90h这个值(命令),怎么知道他是命令呢?这时候芯片是选中的,CLE是高电平,WE来一个写脉冲,nand收会把90这个值存进去,而且他知道了这是个命令。
② 发00h地址。
③ 剩下的就是数据出来了,可以读了。第一个字节是EC字符,然后就是设备ID。

NAND FLASH控制器,帮我们简化了对NAND FLASH的操作,下面来分析一下不使用NAND FLASH控制器和使用NAND FLASH控制器对外设NAND FLASH的操作。
一句话,有了控制器,我们不用管时序了,给寄存器进行操作就OK了!
注意!!易错点:一个与我想象不一样的地方:我以为是把一个32位的地址值给地址寄存器,他就会按照时序分多次在数据线上传出去。
实际不是的。地址寄存器和命令寄存器都是低八位有效的,尤其是地址寄存器要注意!!!!
我们在读写nand的时候,还是需要看读写的时序图,按照时序图的要求把地址按照一个字节一个字节的传出去!!!!!并不是地址一次性给地址寄存器就不用管了的!!!!!
我们想要读某个地址,就得需要确定在哪一行page(row),在哪一列col(0~2047)。从NAND FLASH的地址周期中可以看出来,先发出2个col(列地址),(每次只能发8位,两次凑个16位)。再发出3个(Row)行地址。
这也不是我自己想当然的那样的,详情还是得参考nand读写的章节!!!!!



用UBOOT来体验NAND FLASH的操作

值得关注,uboot操作nand flash就是操作寄存器。
注意是在uboot中操作,开发板上不要进到启动内核里。

一、读ID:

操作:                   S3C2440                 u-boot 
选中                     NFCONT的bit1设为0   md.l 0x4E000004 1; mw.l 0x4E000004  1
(md表示读出内存/寄存器,1表示读一次,然后我们要修改数据!!!再mw.l写进去)
发出命令0x90             NFCMMD=0x90         mw.b 0x4E000008 0x90 
发出地址0x00             NFADDR=0x00         mw.b 0x4E00000C 0x00
读数据得到0xEC           val=NFDATA          md.b 0x4E000010 1
读数据得到device code    val=NFDATA          md.b 0x4E000010 1
          0xda
退出读ID的状态           NFCMMD=0xff         mw.b 0x4E000008 0xff

md: memory display
mw: memory write
md.l是因为这个寄存器是个四字节长度的!!!!
b:8位
w:16位
l:32位(默认值)
后面为什么用mw.b呢???难道寄存器是8位?

UBOOT中所有的数据都认为是16进制的,所以加不加0x都一样的。
小端模式(little endian),简单记忆:低低模式,即低位在低地址。

二、读数据
uboot功能强大,本来内部就有nand的操作函数。相应,也提供了命令直接供我们查看nand的数据。

而我们用寄存器操作一样可以读出这些数据。

读写nand也要看时序图的,不过关注点不是引脚电平变换,而主要是发送的顺序:

对于存储为256M的NAND FLASH,需要28条地址线,来表示这个地址值,根据原理图可以,只用8根地址线,所以需要4个周期的地址,为了兼容更大容量的NAND FLASH,要发出5个周期的地址!!!!!
我们不用管别的引脚时序,但是必须手动给寄存器赋值五次,发出五次地址!!!!!!(寄存器只有第八位有效)。

NandFlash时序及初始化

nor flash不需要初始化,但是nand flash需要。

NAND FLASH控制器的时序,是为了让NAND FLASH外设工作起来,假如外接不同的NAND FLASH外设,那么它的操作时序可能就会不同,所以NAND FLASH控制器发出的时序图,就是不一样的,所以我们根据NAND FLASH外设来设置NAND FLASH控制器。

我们在读写时直接操作寄存器,而不用管we那些引脚在读写时的变化。是有前提的,就是我们初始化了nand控制器,把时序反映到了寄存器的配置中。


(nand的读写一般是一个页为单位。一个块一般是64个页)
必须一个页读写吗??????????????最小单位了吗?????
好像是可以读取到字节的,后面nand读写章节介绍了。读的时候要指定想读取的地址所在的页以及偏移,而不是直接使用一个从0排到FFFFFFFF那样的地址!!!!

识别ID这个步骤有战略意义,如果成功的话,说明我们初始化是正确的,而且nand是可以工作的。

左上是2440nand flash的时序图,上面涉及的时间在手册里搜索,可以找到是在坐下的寄存器里面设置。
右上是芯片命令锁存周期图。右下是针对右上的时间的说明。
这些图怎么看呢?
2440的TACLS这个时间就对应芯片的Tcls-Twp这个时间!从芯片中可以计算出这里可以是0,说明CLE和nWE可以同时发出。


编程实现nand读写

视频没看,没有什么要补充。
全部参考韦东山老师博客即可:https://blog.csdn/thisway_diy/article/details/79397811。
韦老师威武!

提取重点须知。
一、
我们一般先操作片选使能,只有片选使能之后才能进行后边的操作,**片选使能也是通过寄存器操作的!!**虽然有引脚直接接了CE#(nCE),chip enable。

void nand_select(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++);
}

接下来就可以读取数据了,数据可以直接通过读取NFDATA寄存器里面数据来获得数据,根据时序图,是读5个字节的数据,代码如下:

unsigned char nand_data(void)
{//这是读取一次的,每次只能读取一个字节!!!!!得按照时序图中的要求读一定的次数。
    return  NFDATA;
}

所以真正读取是这样的:


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

三、

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

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

/* 发出地址 */
        /* 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);

四、
如果程序大于4K,按照以前的方式放到nand,由硬件自动拷贝到sram执行,这种方式就不行了,得全部重定位到sdram再执行。(或者在nor中启动)。

https://wwwblogs/pengdonglin137/p/3324494.html

更多推荐

韦一之Nor flash和Nand flash读写(015-016课)

本文发布于:2023-06-14 07:27:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1451578.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Nand   flash

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!