Nand flash实验
1.??????介绍 实验用的是mini2440开发板。Nand Flash型号是K9F1G08U0B。实验目的是读取Nand Flash。 2.??????关于NandFlash 该Nand Flash以2Kb为一页。64K页为一段,一共1024段。其中OOB是每页64b。(具体请看芯片手册) S3C2440有对应的控制器控制Nand Flash,不同的Nand Flash需要对应不同的S3C 2440的参数。根据PINCONFIGURATION(215页)和实际的芯片大小。参数选择应该是NCON=1,GPG13=1,GPG14=0,GPG15=0。这几个数值是固定的,制作开发板的时候就已经通过相应的硬件设置好了,编程的时候不需要改变,也不能改变。这几个值在重启的过程中将会重新获取。正常状态下,引脚应该设置输入。(重启芯片默认即为输入) 3.??????程序代码 首先介绍下程序。这个实验主要是验证是否能够读Nand Flash。之前的GPIO,MMU等实验都是用jlink commander的loadbin指令烧录到内存中的。所以,在简单的应用loadbin无法达到实验目的。 首先看一下连接脚本 SECTIONS { ???????? first 0x00000000:{head.o init.o nand.o} ???????? second 0x30000000:AT(4096){led_c.o} } 先介绍一下连接脚本的写法。SECTIONS和一些括号冒号是固定的结构。first和second是自己定义的。放在前面的first表示是程序的第一部分,有三个.o文件。这部分在程序的其实位置。second是程序的第二段。其中AT(4096)表示led_c.o是在bin中是从4096开始的。0x30000000应该叫链接地址(自己瞎叫的名字)吧!表示运行的地址。 第一段的程序包括head.oinit.o nand.o。主要是初始化工作,程序很小,不到4K,在这段程序中需要将led_c.o的内容加载到0x30000000中。注意led_c.o在4096开始。超过了4K,所以,就不能在SRAM中读取了。就必须依靠读NAND FLASH的操作。 nand.bin:head.o init.o nand.o led_c.o ???????? arm-linux-ld-Tnand.lds -o nand_elf $^ ???????? arm-linux-objcopy -Obinary -S nand_elf $@ ???????? arm-linux-objdump -D-m arm nand_elf > nand.dis %.o:%.S ???????? arm-linux-gcc -O2 -c-o $@ $< %.o:%.c ???????? arm-linux-gcc -O2 -c-o $@ $< clean: ???????? rm *.o nand_elf nand.disnand.bin 在看下Makefile。其中,$@表示目标的完整名称。$^表示所有的依赖文件,以空格分开,不包含重复的依赖文件。$<表示第一个依赖文件的名称。 ???????? 下面看一下head.S文件 .text .global _start _start: ???????? ldr sp,=4096 ???????? bl close_watchdog ???????? bl init_sdram ???????? bl nand_init ???????? ldr r0,=4096????????????? @r0,r1,r2表示nand_read的三个参数,第一个参数是源地址 ???????? ldr r1,=0x30000000????????? @第二个参数是目标地址 ???????? ldr r2,=2048????????????? @第三个参数是要拷贝的数据的长度 ???????? bl nand_read ???????? ldr sp,=0x34000000 ???????? ldr pc,=0x30000000 loop: ???????? b loop ???????? 程序首先关闭看门狗,初始化SDRAM,初始化NAND FLASH。然后传递nand_read函数的三个参数。设置好堆栈。这样初始化的工作就做好了。这些主要是first的工作,在S3C2440的内部的SRAM(steppingstone)中完成。然后ldr pc,=0x30000000将程序转到0x30000000中,也就是转到了SDRAM中,执行led_c.o。 ???????? 其中init.c是初始化看门狗和初始化SDRAM的程序,不介绍了。下面主要介绍一下nand.c程序。 #define NFCONF (*(volatile unsigned long *)0x4E000000) #define NFCONT (*(volatile unsigned long *)0x4E000004) #define NFCMMD (*(volatile unsigned long *)0x4E000008) #define NFADDR (*(volatile unsigned long *)0x4E00000C) #define NFDATA (*(volatile unsigned long *)0x4E000010) #define NFSTAT (*(volatile unsigned long *)0x4E000020) #define page_size 2048 static void select_chip(); static void close_select_chip(); static void wait_read_ok(); static void write_command(unsigned char a); static void write_address(unsigned long addr); static unsigned char read_data(); static void nand_reset(); void nand_init(); void nand_read(unsigned long src_addr,unsigned longdst_addr,unsigned long size); static void select_chip() { ???????? int i; ???????? NFCONT &=~(1<<1); ???????? for(i=0; i<10;i++); } static void close_select_chip() { ???????? int i; ???????? NFCONT |=(1<<1); ???????? for(i=0; i<10;i++); } static void wait_read_ok() { ???????? int i; ???????? volatile unsigned char* p = (volatile unsigned char *) & NFSTAT; ???????? while(!(* p &0x1)) ?????????????????? for(i=0;i<10; i++); } static void write_command(unsigned char a)????????? //仅使用低8位 { ???????? int i; ???????? volatile unsigned char* p = (volatile unsigned char *)& NFCMMD;?????? //注意转化为unsignedchar * ??????? * p = a; ???????? for(i=0; i<10;i++); } static void write_address(unsigned long addr) { ???????? int col,page; ???????? int i; ???????? volatile unsigned char*p = (volatile unsigned char *)& NFADDR;? //注意转化为unsignedchar * ???????? col = addr & 2047; ???????? page = addr / 2048; ???????? ???????? * p = col & 0xff;?????????????????????????? //Column AddressA0~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 AddressA28~A29 ???????? for(i=0; i<10;i++); } static unsigned char read_data() { ???????? volatile unsigned char* p = (volatile unsigned char *) & NFDATA; ???????? return *p; } static void nand_reset() { ???????? select_chip(); ???????? write_command(0xff);????????????? ???????? wait_read_ok(); ???????? close_select_chip(); //delay(1); } void nand_init() { ???????? NFCONF =(0<<12)| (3<<8) | (0<<4); ???????? NFCONT = (1<<0)| (1<<1) | (1<<4);???????? //nandflash 控制器开启,禁止选择芯片,初始化ECC??? ???????? nand_reset(); } /* src_addr存放源地址值。dst_addr存放目标地址值。size存放要拷贝的数据的数目 功能是从src_addr拷贝size个数据到dst_addr中 src_addr是不包括OOB的地址,要将其转化为NANDFLASH的内部地址才可以。 */ void nand_read(unsigned long src_addr,unsigned long size) { ???????? int i,j; ???????? int page_num =size/page_size; ???????? volatile unsigned char* dst = (unsigned char *)dst_addr; ???????? select_chip(); ???????? //nandflash应该按页读取 ???????? for(i=0;i<page_num;) ???????? { ?????????????????? write_command(0);???????? //读指令 ?????????????????? write_address(src_addr+i*page_size); ?????????????????? write_command(0x30); ?????????????????? wait_read_ok(); ?????????????????? for(j=0;j<page_size;j++)? //只读到2K,不读后面的OOB ?????????????????? { ??????????????????????????? *dst = read_data(); ??????????????????????????? dst++; ?????????????????? } ???????? ???????? i++; ???????? } ???????? close_select_chip(); } 主要就是nand_init和nand_read两个函数,其他函数都被这两个函数调用。其中nand_int是通过设置一些初始化信息。主要说一下nand_read函数。 nand_read函数汇中首先选中芯片(select_chip)。然后进入循环中,最外层的大循环的次数是要读取数据的页数,也就是size除以每一页的大小。在每一页读取中,首先发送一个00指令,然后发送地址序列(稍后详述),最后发送0x30。等待读取数据结束(wait_read_ok)。然后将读到的数据依次写到dst(0x30000000指向的SDRAM)中。这样就实现了nand flash的数据从nand flash拷到SDRAM中了。结束nand_read之前应该关掉片选(close_select_chip),以免影响其他工作。 ???????? 另外,对于写指令地址等操作需要用强制转化为unsigned char。估计写成unsigned long,编译器可能会当做00 00 00 XX四个指令进行编译吧。这个具体也不太清楚。 4.地址序列 ???????? 写程序的时候大部分时间都浪费在地址的问题上了。下面是自己的理解,也不一定全对。 首先要明白NandFlash是以页(Page)为最小单位进行读写的,以块(Block)为最小单位进行擦除的,也就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本Page的最后一个 Byte为止(可以包括Spare Field)。例如说,第0块第0页的100的地址应该为100,第0块第0页的OOB的地址在2048到2048+64之间。这些应该是确定的。但是第0块的第1页是多少呢?通过查阅的一些资料,我认为是4K,先暂时这么说。 然后看下write_address。之前问题一直处在这里,后来拿了别人的代码改了改,成功了。下面见分析一下这个。2047正好是0x7FF,也就是前11位数,2K,恰好是一页的大小,说明col是addr的前11位数,且第12位为0。当第12为为1的时候,应该是OOB的大小。然后注意,page是addr/2048,这样就变成了Addr的13-27位。这样看起来很怪。如果把这个Nand Flash的模型这样想,实际的一页的地址空间是4K,前2K是存储空间,后64是OOB,接下来的空间没有用。而我们主要是访问2K的存储空间,所以,将我们修的地址11-26当做12-27就相当于将2K的基础乘以个2。多了一个2K,就是OOB和没利用的地址空间。我们实际上访问依然用不包括OOB的地址空间,但是write_address会将这个不包括OOB的地址空间转化为真正的包括OOB的地址空间。 感觉说的不太明白。不知道怎么表达啊!换一种代码在写一下,也是一个效果的。简单那说就是nand_read里面的地址是按照没有OOB的地址进行输入的,然后在write_address函数中处理是按照实际的包括OOB的编制。这样做更方便的读出数据,而不用进行过多的地址计算。 static void write_address(unsigned long addr) { ???????? int i; ???????? NFADDR = addr &0xff;?????????????????????? ???????? for(i=0; i<10;i++);??? ???????? NFADDR = (addr>> 8) & 0x07; ?????? ???????? for(i=0; i<10;i++); ???????? NFADDR = (addr>>11) & 0xff;??????????????????????????? ???????? for(i=0; i<10;i++); ???????? NFADDR = (addr>> 19) & 0xff; ???????? for(i=0; i<10;i++);??? } 4.??????实验步骤 (1)??????利用jlink commander。输入r (2)??????输入speed 12000 (3)??????输入loadbin F:init.bin 0 (4)??????setpc 0 (5)??????输入g (6)??????输入h (7)??????输入loadbin F:u-boot.bin_openjtag 0x33f80000,并且打开串口工具SecureCRT (8)??????setpc 0x33f80000 (9)??????输入g (10)??输入h (11)??输入loadbin F:nand.bin 0x30000000 (12)??输入g (13)??在SecureCRT中输入nand erase 0 0x2000 (14)??在SecureCRT中输入nand write 0x30000000 0 0x2000。这样u-boot.bin就会烧到nandflash中 (15)??开发板拔掉jlink,重启,打到nandflash端,就能看到相应现象。 ? 其中(1)是重启开发板,(2)是设置速度。(3)-(6)将init.bin烧到片内SRAM(steppingstone)中。然后(7)-(10)烧u-boot.bin_openjtag烧到SDRAM中。然后(11)-(12)将我们编译好的nand.bin写入SDRAM的0x30000000中。现在已经启动了u-boot.bin_openjtag。然后(13)(14)利用u-boot.bin_openjtag通过串口擦除flash和写flash。这样就完成了实验过程。(15)是观察的实验现象。 具体:init.bin和u-boot.bin_openjtag可以下载笔者上传的资源。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |