加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

NAND FLASH控制器

发布时间:2020-12-15 17:37:51 所属栏目:百科 来源:网络整理
导读:存储组织形式 K9F2G08X0A共有2048个Block(块),每个Block含有 64 Page(页),每个Page含有2k byte的正常存储空间以及64 byte的校验空间 . 总空间 = 2048 * 64 * (2 * 1024 + 64) ?byte 实际存储空间 = 2048 * 64 * 2 * 1024 byte 2. 引脚定义及接法 3. 寻址方式
  1. 存储组织形式


K9F2G08X0A共有2048个Block(块),每个Block含有 64 Page(页),每个Page含有2k byte的正常存储空间以及64 byte的校验空间 .

总空间 = 2048 * 64 * (2 * 1024 + 64) ?byte

实际存储空间 = 2048 * 64 * 2 * 1024 byte

2. 引脚定义及接法

3. 寻址方式

列地址: 进行 Block 和 Page 寻址

行地址: 进行 Page 内寻址

4. 命令

以下为几个命令操作示意,更多命令请参靠芯片手册.

(1) Read ID

说明: 先发送 ?0x90 ?然后再发送 0x00,然后 nand flash 会返回5个数据,依据芯片型号的不同数据内容也不尽相同,具体值见手册

(2) Page Read

说明: 先发送 0x00 然后发送要读取数据的地址,之后发送 0x30,根据 R/B 可以判断是否发送完成. 命令发送完成之后 从RE 的第一个下降沿开始,芯片将从该地址开始到当页结束的所有数据依次输出

(3) Page Program

说明: 先发送 0x80 然后发送 地址 和数据,之后发送 0x10,读取 R/B,命令写入完成之后 发送 0x70 再依据 I/O0来判断写入是否成功,0 : 成功 1 : 失败

(4) Block Erase?

说明: 先发送 0x90 ?然后再发送 行地址 1 2 3,然后再发送 擦除命令 0xD0,根据 R/B 引脚判断擦除操作是否完成,完成之后发送 0x70 根据 I/O0的状态来判断擦除是否成功,0 : 成功 ?1: 失败

注释1:

/*?初始化NAND?Flash?*/
void?nand_init(void)
{
#define?TACLS???0
#define?TWRPH0??3
#define?TWRPH1??0

????/*?判断是S3C2410还是S3C2440?*/
????if?((GSTATUS1?==?0x32410000)?||?(GSTATUS1?==?0x32410002))????//2410
????{????
????????nand_chip.nand_reset?????????=?s3c2410_nand_reset;
????????nand_chip.wait_idle??????????=?s3c2410_wait_idle;
????????nand_chip.nand_select_chip???=?s3c2410_nand_select_chip;
????????nand_chip.nand_deselect_chip?=?s3c2410_nand_deselect_chip;
????????nand_chip.write_cmd??????????=?s3c2410_write_cmd;
????????nand_chip.write_addr?????????=?s3c2410_write_addr;
????????nand_chip.read_data??????????=?s3c2410_read_data;

	/*?使能NAND?Flash控制器,?初始化ECC,?禁止片选,?设置时序?*/
????????s3c2410nand->NFCONF?=?(1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
????}
????else
????{
????????nand_chip.nand_reset?????????=?s3c2440_nand_reset;
????????nand_chip.wait_idle??????????=?s3c2440_wait_idle;
????????nand_chip.nand_select_chip???=?s3c2440_nand_select_chip;
????????nand_chip.nand_deselect_chip?=?s3c2440_nand_deselect_chip;
????????nand_chip.write_cmd??????????=?s3c2440_write_cmd;
#ifdef?LARGER_NAND_PAGE
????????nand_chip.write_addr?????????=?s3c2440_write_addr_lp;
#else
	nand_chip.write_addr?????????=?s3c2440_write_addr;
#endif
????????nand_chip.read_data??????????=?s3c2440_read_data;

	/*?设置时序?*/
????????s3c2440nand->NFCONF?=?(TACLS?<<?12)|(TWRPH0?<<?8)|(TWRPH1?<<?4);
???????
?????????/*?使能NAND控制器,?禁止片选?*/
????????s3c2440nand->NFCONT?=?(1?<<?4)|(1?<<?1)|(1?<<?0);
????}
????
????/*?复位NAND?Flash?*/
????nand_reset();
}

这里需要注意的是 大页 nand,地址发送五次,前两次是 colum addr 后三次是 row addr,?colum addr负责页内寻址,我们实际寻址的空间为2k = 2^11,所以只需要11位即可,所以程序中只取了低11位

A0~A10用来页内寻址. 另外64byte的OOB空间可以使用A11来寻址.?

A12~A17用来在块内寻址页,共64页

A18~A28用来寻址块,共2048块

nand flash 底层操作函数:

/*?复位?*/
static?void?s3c2410_nand_reset(void)
{
????s3c2410_nand_select_chip();???//?选中芯片?
????s3c2410_write_cmd(0xff);??????//?复位命令
????s3c2410_wait_idle();??????????//?等待nand就绪
????s3c2410_nand_deselect_chip();?//?取消选中???
}

/*?等待NAND?Flash就绪?*/
static?void?s3c2410_wait_idle(void)
{
????int?i;
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2410nand->NFSTAT;
????while(!(*p?&?BUSY))
????????for(i=0;?i<10;?i++);
}

/*?发出片选信号?*/
static?void?s3c2410_nand_select_chip(void)
{
????int?i;
????s3c2410nand->NFCONF?&=?~(1<<11);
????for(i=0;?i<10;?i++);????
}

/*?取消片选信号?*/
static?void?s3c2410_nand_deselect_chip(void)
{
????s3c2410nand->NFCONF?|=?(1<<11);
}

/*?发出命令?*/
static?void?s3c2410_write_cmd(int?cmd)
{
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2410nand->NFCMD;
????*p?=?cmd;
}

/*?发出地址?*/
static?void?s3c2410_write_addr(unsigned?int?addr)
{
????int?i;
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2410nand->NFADDR;
????
????*p?=?addr?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?9)?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?17)?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?25)?&?0xff;
????for(i=0;?i<10;?i++);
}

/*?读取数据?*/
static?unsigned?char?s3c2410_read_data(void)
{
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2410nand->NFDATA;
????return?*p;
}

/*?S3C2440的NAND?Flash操作函数?*/

/*?复位?*/
static?void?s3c2440_nand_reset(void)
{
????s3c2440_nand_select_chip();
????s3c2440_write_cmd(0xff);??//?复位命令
????s3c2440_wait_idle();
????s3c2440_nand_deselect_chip();
}

/*?等待NAND?Flash就绪?*/
static?void?s3c2440_wait_idle(void)
{
????int?i;
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2440nand->NFSTAT;//状态寄存器,?只用到位0.?0?:?busy???1?:?ready
????while(!(*p?&?BUSY))
????????for(i=0;?i<10;?i++);
}

/*?发出片选信号?*/
static?void?s3c2440_nand_select_chip(void)
{
????int?i;
????s3c2440nand->NFCONT?&=?~(1<<1);
????for(i=0;?i<10;?i++);????
}

/*?取消片选信号?*/
static?void?s3c2440_nand_deselect_chip(void)
{
????s3c2440nand->NFCONT?|=?(1<<1);
}

/*?发出命令?*/
static?void?s3c2440_write_cmd(int?cmd)
{
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2440nand->NFCMD;//NFCMD?不同的flash命令不一样,?发送命令信号
????*p?=?cmd;
}

/*?发出地址(小页?4周期)?*/
static?void?s3c2440_write_addr(unsigned?int?addr)
{
????int?i;
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2440nand->NFADDR;
????
????*p?=?addr?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?9)?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?17)?&?0xff;
????for(i=0;?i<10;?i++);
????*p?=?(addr?>>?25)?&?0xff;
????for(i=0;?i<10;?i++);
}
/*?发出地址(大页?5周期)?*/
static?void?s3c2440_write_addr_lp(unsigned?int?addr)
{
	int?i;
	volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2440nand->NFADDR;//NFADDR当向该寄存器写入数据时,?芯片向nand发送地址信号
	int?col,?page;
?????????????//#define?NAND_SECTOR_SIZE_LP????2048
?????????????//#define?NAND_BLOCK_MASK_LP?????(NAND_SECTOR_SIZE_LP?-?1)
	col?=?addr?&?NAND_BLOCK_MASK_LP;????????//2048?-1?=?22222222221b,?这里是屏蔽高位,?取低11位数据,?因为寻址空间只到?2k?=?2^11?,?所以最多用11位,?这里直接不考虑第12位
	????????????????????????????????????????//参考链接:http://bbs.csdn.net/topics/360034390
	page?=?addr?/?NAND_SECTOR_SIZE_LP;??????//2048?=?2^11,?这里将数据右移11位,?获取高位数据
	
	*p?=?col?&?0xff;			/*?Column?Address?A0~A7?*/
	for(i=0;?i<10;?i++);		
	*p?=?(col?>>?8)?&?0x0f;?	/*?Column?Address?A8~A11?*/
	for(i=0;?i<10;?i++);
	*p?=?page?&?0xff;			/*?Row?Address?A12~A19?*/
	for(i=0;?i<10;?i++);
	*p?=?(page?>>?8)?&?0xff;	/*?Row?Address?A20~A27?*/
	for(i=0;?i<10;?i++);
	*p?=?(page?>>?16)?&?0x03;	/*?Row?Address?A28~A29?*/
	for(i=0;?i<10;?i++);
}

/*?读取数据?*/
static?unsigned?char?s3c2440_read_data(void)
{
????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&s3c2440nand->NFDATA;//数据寄存器,?读写都是这个寄存器.?只用到它的低?8?位
????return?*p;
}

/*?在第一次使用NAND?Flash前,复位一下NAND?Flash?*/
static?void?nand_reset(void)
{
????nand_chip.nand_reset();
}

????发送地址:?NFADDR ? ?? 发送命令:?NFCMD ? ? ?? 发送/读取数据:?NFDATA ? ? ? ? ?读取状态: NFSTAT ? ? ? ? ?初始化控制器: NFCONT?

由以上代码可以看出: 初始化完毕之后就不必关系总线上的时序只需要把 地址/命令/数据放到对应的寄存器,芯片就能在总线上发出对应的时序

而初始化需要配置的是: 时序的参数/数据位宽/只读位/页的大小

注释2:

/*?读函数?*/
void?nand_read(unsigned?char?*buf,?unsigned?long?start_addr,?int?size)//在head.S中设置的r0?r1?r2?分别为该函数的三个参数
{
????int?i,?j;

#ifdef?LARGER_NAND_PAGE
????if?((start_addr?&?NAND_BLOCK_MASK_LP)?||?(size?&?NAND_BLOCK_MASK_LP))?{
????????return?;????/*?地址或长度不对齐?*/
????}
#else
????if?((start_addr?&?NAND_BLOCK_MASK)?||?(size?&?NAND_BLOCK_MASK))?{
????????return?;????/*?地址或长度不对齐?*/
????}
#endif

????/*?选中芯片?*/
????nand_select_chip();

????for(i=start_addr;?i?<?(start_addr?+?size);){
??????/*?发出READ0命令?*/
??????write_cmd(0);

??????/*?Write?Address?*/
??????write_addr(i);
#ifdef?LARGER_NAND_PAGE
??????write_cmd(0x30);
#endif
??????wait_idle();

#ifdef?LARGER_NAND_PAGE
??????for(j=0;?j?<?NAND_SECTOR_SIZE_LP;?j++,?i++)?{
#else
	??for(j=0;?j?<?NAND_SECTOR_SIZE;?j++,?i++)?{
#endif
??????????*buf?=?read_data();
??????????buf++;
??????}
????}

????/*?取消片选信号?*/
????nand_deselect_chip();

????return?;
}

总结一下:

(1)选中芯片

(2)发送00h

(3)发出地址

(4)发30h

(5)等待就绪

(6)读一页数据

链接文件:

SECTIONS?{?
??firtst??	0x00000000?:?{?head.o?init.o?nand.o}
??second?	0x30000000?:?AT(4096)?{?main.o?}
}

main.c存放到了nand的4096地址处.

入口文件:

@******************************************************************************
@?File:head.s
@?功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************???????
??
.text
.global?_start
_start:
????????????????????????????????????????????@函数disable_watch_dog,?memsetup,?init_nand,?nand_read_ll在init.c中定义
????????????ldr?????sp,?=4096???????????????@设置堆栈?
????????????bl??????disable_watch_dog???????@关WATCH?DOG
????????????bl??????memsetup????????????????@初始化SDRAM
????????????bl??????nand_init???????????????@初始化NAND?Flash?????????????????????????????????????????????????????????????????注释1

????????????????????????????????????????????@将NAND?Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
????????????????????????????????????????????@nand_read_ll函数需要3个参数:
????????????ldr?????r0,?????=0x30000000?????@1.?目标地址=0x30000000,这是SDRAM的起始地址
????????????mov?????r1,?????#4096???????????@2.??源地址???=?4096,连接的时候,main.c中的代码都存在NAND?Flash地址4096开始处
????????????mov?????r2,?????#2048???????????@3.??复制长度=?2048(bytes),对于本实验的main.c,这是足够了
????????????bl??????nand_read???????????????@调用C函数nand_read???????????????????????????????????????????????????????????????注释2

????????????ldr?????sp,?=0x34000000?????????@设置栈
????????????ldr?????lr,?=halt_loop??????????@设置返回地址
????????????ldr?????pc,?=main???????????????@b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转
halt_loop:
????????????b???????halt_loop

这里将nand flash 从4096地址开始的2048字节复制到sdram的0x3000 0000地址处

main.c中是led闪烁程序.?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读