NAND FLASH控制器
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闪烁程序.? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |