NAND Flash裸板编程
????nand flash按照我的理解,在开发板上就类似我们所用的电脑中的硬盘,用来保存系统运行的操作系统,应用程序,数据等,掉电之后还可以永久得保存数据(不包括临时数据)。通过控制或配置NAND Flash的控制器寄存器,即可完成对nand的操作:包括读、写、擦除等。 ????而控制、配置这些寄存器是根据板子的原理图、用户手册以及nand flash芯片手册上的说明来配置的。 通过原理图,可见nand flash与cpu之间的数据传输主要是通过LDDATA0~7这8根引脚线,其中传输的“数据”可以是地址、数据,也可以是命令,这就要靠CLE、ALE引脚的状态进行选择。 对于命令的传输,主要是对NFCMD寄存器写以上相对应的命令值,但这个命令是分两个周期进行的。 而对于数据的传输,在数据传输模式下,传输数据也是要根据以上的格式去进行传输,分5个周期,每一个周期传输的数据都十分讲究,这就要求要有一个符合这种格式的巧妙算法(在接下来的代码中体现)。 ?了解了这些命令、数据的传输格式之后,就是要对具体的寄存器进行相应的配置了。首先要介绍这些寄存器的作用(数据手册上对各寄存器都有说明): (1)NFCONF:用来设置时序参数,设置位宽。 (2)NFCMD:命令寄存器。 (3)NFADDR:地址寄存器。 (4)NFDATA:数据寄存器,用于读写数据。 (5)NFSTAT:状态寄存器,只用到最低1位,表示是否忙碌。 编程举例: 实现从nand flash中拷贝程序到sdram中运行,其中涉及到启动代码的编程,初始化内存控制器,读取并拷贝nand flash上的数据。(只展示部分核心代码) head.S .text .global????_start _start: ????ldr?sp,=4096????????????@设置堆栈 ????bl?disable_watch_dog????@关闭看门狗 ????bl?set_mem????????????????@设置内存控制器 ????bl?nand_init????????????@nand初始化 ????ldr?r0,=0x30000000 ????ldr?r1,=4096 ????ldr?r2,=4096?????????????@传递参数 ????bl?nand_read????????????@从nand中拷贝 ????ldr?sp,=0x38000000 ????ldr?lr,?=halt??????????@设置返回地址 ????ldr?pc,?=main?? halt: ????b?halt nand.c typedef?struct?s3c2440_nand?? { ????unsigned?int?NFCONF; ????unsigned?int?NFCONT; ????unsigned?int?NFCMMD; ????unsigned?int?NFADDR; ????unsigned?int?NFDATA; ????unsigned?int?NFMECCD0; ????unsigned?int?NFMECCD1; ????unsigned?int?NFSECCD; ????unsigned?int?NFSTAT; ????unsigned?int?NFESTAT0; ????unsigned?int?NFESTAT1; ????unsigned?int?NFMECC0; ????unsigned?int?NFMECC1; ????unsigned?int?NFSECC; ????unsigned?int?NFSBLK; ????unsigned?int?NFEBLK; }s3c2440_nand; //定义nandflash控制器的起始地址 static?s3c2440_nand*?nand_base?=?(s3c2440_nand*)0x4E000000; //片选 void?select_chip_or_not(int?flag)//0不片选,1片选 { ????if(flag?==?1) ????{ ????????nand_base->NFCONT?|=?(0x1<<1); ????} ????if(flag?==?0) ????{ ????????nand_base->NFCONT?&=?~(0x1<<1); ????} } void?write_command(unsigned?char?cmd) { ?????volatile?unsigned?char?*p?=?(volatile?unsigned?char?*)&nand_base->NFCMMD; ????*p?=?cmd; } void?write_addr(unsigned?int?addr) { ????int?i; ????volatile?unsigned?char*?p?=?(volatile?unsigned?char?*)nand_base->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++); } //等待nand?flash就绪 void?wait_ldle(void) { ????volatile?unsigned?char*?p?=?(volatile?unsigned?char*)nand_base->NFSTAT; ????int?flag?=?*p?&?1; ????while(!flag) ????{ ????????int?i; ????????for(i=0;i<20;i++); ????} } unsigned?char?read_data(void) { ????volatile?unsigned?char?*p=?(volatile?unsigned?char*)nand_base->NFDATA; ????return?*p; } void?nand_read(unsigned?char*?buf,unsigned?char?base_addr,unsigned?int?size) { ????int?i,j; ????//片选 ????select_chip_or_not(1); ????//复制数据 ????for(i=base_addr;i<base_addr+size;) ????{ ????????write_command(0);//发送读命令 ????????write_addr(i);???//发送地址 ????????write_command(0x30); ????????wait_ldle(); ????????for(j=0;j<512;j++,i++) ????????{ ????????????*buf?=?read_data();?//读取数据,一次读取一页(512个字节) ????????????buf++; ????????} ????} ????select_chip_or_not(0);//取消片选 } void?nand_reset(void) { ????select_chip_or_not(1);//片选 ????write_command(0xff); ????wait_ldle(); ????select_chip_or_not(0); } void?nand_init(void) { ???? ????nand_base->NFCONF?=?(0<<12)|(3<<8)|(0<<4); ????nand_base->NFCONT?=?(1<<4)|(1<<1)|(1<<0); ????nand_reset(); } 还要说下Makefile objs?:=?head.o?init.o?nand.o?main.o nand.bin?:?$(objs) ????arm-linux-ld?-Tnand.lds?$^?-o?nand_elf ????arm-linux-objcopy?-O?binary?-S?nand_elf??$@ %.o?:?%.S ????arm-linux-gcc?-c?$<?-o?$@ %.o?:?%.c ????arm-linux-gcc?-c?$<?-o?$@ clean?: ????rm?-f?nand.bin?*.o?nand_elf 平时写的Makefile不一样,这里使用了一个名为nand.lds的链接脚本,这样做主要是为了试验让编译好的程序在链接时存放的地址和理论运行地址都不同且不在同一个存储设备上,方便看试验效果。 nand.lds SECTIONS?{ ????first????0x00000000?:?{head.o?init.o?nand.o} ????second?????0x30000000?:?AT(4096)?{main.o} } 链接脚本上的第一段是放head.S init.c nand.c编译出的内容,从0地址开始存放和执行,而main.o则是需要从nand flash手动复制到sdram中去执行。 实现的效果如图: (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |