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

Nand flash实验

发布时间:2020-12-15 18:19:52 所属栏目:百科 来源:网络整理
导读:1.??????介绍 实验用的是mini2440开发板。Nand Flash型号是K9F1G08U0B。实验目的是读取Nand Flash。 2.??????关于NandFlash 该Nand Flash以2Kb为一页。64K页为一段,一共1024段。其中OOB是每页64b。(具体请看芯片手册) S3C2440有对应的控制器控制Nand Flas

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可以下载笔者上传的资源。

(编辑:李大同)

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

    推荐文章
      热点阅读