NandFlash学习 NandFlash学习


title: NandFlash学习
tags: ARM
date: 2018-10-27 20:18:48:59

概述

  • NAND是公用数据线和地址线的,所以是需要命令操作的
  • NAND和其他内存接口公用数据线,所以需要片选信号
  • NAND有位反转,所以内部存在OOB(out of bank),这个一般无需cpu处理.,同时存在坏块,使用ecc处理

原理图(K9F2G08U0C)

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

启动的引脚配置

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

命令概述

NandFlash学习
NandFlash学习

操作概述

NandFlash学习
NandFlash学习

具体的操作时序,这个手册好评,短小好找

NandFlash学习
NandFlash学习

比如读操作

NandFlash学习
NandFlash学习

根据这个图就可以大致看出电平应该怎样,再查找时序图,基本就清楚了

NandFlash学习
NandFlash学习

地址信号操作,256M需要地址线=256*1024*1024*8=2^8*2^20=2^28也就是28个地址线

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

Uboot下操作体验

上述的操作,对于MCU来说就更简单了,读写相关寄存器即可.

NandFlash学习
NandFlash学习

NAND FLASH S3C2440
发命令 选中芯片->CLE设为高电平 输出命令值->在DATA0~DATA7上->发出一个写脉冲 NFCONT的bit1设为0,->NFCMMD=命令值
发地址 选中芯片->ALE设为高电平->在DATA0~DATA7上输出地址值->发出一个写脉冲 NFCONT的bit1设为0,->NFADDR=地址值
发数据 选中芯片->ALE,CLE设为低电平-> 在DATA0~DATA7上输出数据值->发出一个写脉冲 NFCONT的bit1设为0,->NFDATA=数据值
读数据 选中芯片->发出读脉冲->读DATA0~DATA7的数据 NFCONT的bit1设为0,->val=NFDATA

读ID

S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令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
退出读ID的状态 NFCMMD=0xff mw.b 0x4E000008 0xff

Enter your selection: q
OpenJTAG> md.l 0x4E000004 1
4e000004: 00000003    ....
OpenJTAG> mw.l 0x4E000004 1
OpenJTAG> mw.b 0x4E000008 0x90
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> md.b 0x4E000010 1
4e000010: ec    .
OpenJTAG> md.b 0x4E000010 1
4e000010: da    .
OpenJTAG> md.b 0x4E000010 1
4e000010: 10    .
OpenJTAG> md.b 0x4E000010 1
4e000010: 95 
OpenJTAG> md.l 0x4E000010 1
4e000010: 44    D
OpenJTAG> mw.b 0x4E000008 0xff

读数据

uboot可以使用 nand dump 0 读取nand的内容

NandFlash学习
NandFlash学习

S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30
读数据得到0x17 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0xea val=NFDATA md.b 0x4E000010 1
退出读状态 NFCMMD=0xff mw.b 0x4E000008 0xff
OpenJTAG> mw.b 0x4E000008 0xff
OpenJTAG> mw.l 0x4E000004 1
OpenJTAG> mw.b 0x4E000008 0x00
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> mw.b 0x4E00000C 0x00
OpenJTAG> mw.b 0x4E000008 0x30
OpenJTAG> md.b 0x4E000010 1
4e000010: 17    .
OpenJTAG> md.b 0x4E000010 1
4e000010: 00    .
OpenJTAG> md.b 0x4E000010 1
4e000010: 00    .

ID与地址编码

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

使用uboot读到的是EC-DA-10-95-44对比下第四字节的9515,也就是手册上写的是15=50ns/30ns,芯片读出来是95=25ns

  • Internal Chip Number =1
  • Number of
    Simultaneously
    Programmed Pages =2
  • Page Size=2kb
  • Block Size=128kb,所以1个block有128/2=64page
  • Redundant Area Size=16
  • Serial Access Minimum= 25ns
  • Plane Number=2
  • Plane Size=1Gb

最小擦除的单位是1个block=128k

内部的组织结构可以参考 K9F2G08UXA的数据手册

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

小结

发送地址addr的时候,先发col低地址也就是页内地址,再发高地址也就是第几个page.地址本身也还是先发送低位,也就是说col有2字节,先发低字节.

int page = addr / 2048;
int col  = addr & (2048 - 1);//相当于addr%2048

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

时序初始化

NAND的时序参数实际上大体分为两种,发送命令、地址以及发送数据,下图是MCU的时序配置图

NandFlash学习
NandFlash学习

查看下NAND手册的命令时序图

NandFlash学习
NandFlash学习

NandFlash学习
NandFlash学习

  • 可以拿一个直尺来划分各时间点到拐角

  • 可以看出 CLE和ALE的时间参数是一致的

    • MCU中的TACLS 拐点在CLE有效之后TALS后才允许nWE有效,找到NAND中CLE>=12,tWp>=12.也就是说可以同时发出
    • TWRPH0为nWE的有效时间,对应NAND的tER>=12
    • TWRPH0为nWE失效到CLE失效的时间对应的是NAND的tCh>=5

    NandFlash学习
NandFlash学习

其他寄存器设置如下

/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
NFCONT = (1<<4) | (1<<1) | (1<<0);

程序设计

忙判断

手册上写: 软件模式下,你必须用定时查询或中断来检测 RnB 状态输入引脚。这个状态在NFSTAT的BIT0,0表示忙

void wait_ready(void)
{
	while (!(NFSTAT & 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;
}
void nand_w_data(unsigned char val)
{
	NFDATA = val;
}
void wait_ready(void)
{
	while (!(NFSTAT & 1));
}

读NAND

NandFlash学习
NandFlash学习

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

擦除

NandFlash学习
NandFlash学习

NAND的擦除单位是一个blok=64page=64*2k=128k

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

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

");
		return -1;
	}
	
	nand_select(); 

	while (1)
	{
		page = addr / 2048;
		
		nand_cmd(0x60);
		
		/* row/page addr */
		nand_addr_byte(page & 0xff);
		nand_addr_byte((page>>8) & 0xff);
		nand_addr_byte((page>>16) & 0xff);

		nand_cmd(0xD0);

		wait_ready();

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

写NAND

NandFlash学习
NandFlash学习

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); )
		{
			nand_w_data(buf[i++]);
		}
		nand_cmd(0x10);
		wait_ready();

		if (i == len)
			break;
		else
		{
			/* 开始下一个循环page */
			col = 0;
			page++;
		}
		
	}
	
	nand_deselect(); 	
}

其他注意

  • 使用nand 代码重定位的时候,注意make file 中的 链接文件 start ,nand 等文件放前面,保证在4k内