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








