Chinaunix首页 | 论坛 | 博客
  • 博客访问: 493801
  • 博文数量: 223
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2145
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-01 10:23
个人简介

该坚持的时候坚持,该妥协的时候妥协,该放弃的时候放弃

文章分类

全部博文(223)

文章存档

2017年(56)

2016年(118)

2015年(3)

2014年(46)

我的朋友

分类: 嵌入式

2016-10-05 09:42:29

一、Nandflash原理解析
1、角色分析

类似于电脑的硬盘

2、NandFlash分类
根据物理结构上的区别,NandFlash主要分为如下两类:
SLC (Single Level Cell): 单层式存储
MLC (Multi Level Cell): 多层式存储
SLC在存储格上只存一位数据,而MLC则存放两位数据。


2.2 MLC对比SLC
1. 价格
由于MLC采用了更高密度的存储方式,因此同容量的MLC价格上远低于SLC.
2. 访问速度
SLC的访问速度一般要比MLC快3倍以上.
3. 使用寿命
SLC能进行10万次的擦写,MLC能进行1万次
4. 功耗
MLC功耗比SLC高15%左右

3.访问过程
3.1 对比内存与NandFlash编址区别
内存的存取方式:比如*(
0x3008000) = 5; 等号左边走的是地址线,而等号右边走的是数据线,所以读取速度很快。
NandFlash是独立编址的,ARM中有个NandFlash控制器,用8根I/O口传送地址、命令、数据。这些地址、命令、数据当然是对Nand Flash控制器对应的寄存器操作的。

3.2 NandFlash地址结构
Nand Flash中的一个块被分成64个页,其中每一页又有2KB+64B的大小B。当然不同nandflash可能大小有所不同,但是结构都是一致的。
如何访问呢:下面的表格说明了5个周期内的行地址和列地址信息。从行地址获取页的位置,用列地址找出那个页(偏移)。

我们知道K9F2G08U0A的每一页有(2K+64)=2112Byte,2112byte 需要12bit来表示,对于2112byte系列的NAND,这2112byte被分成1st half Page Register和2nd half Page Register,各自的访问由地址指针命令来选择,A[11:0]就是所谓的column address(列地址),在进行擦除操作时不需要它,因为以块为单位擦除。64个page需要6bit来表示,占用A[17:12],即该page在块内的相对地址,也就是确定位于哪一页。A11这一位地址被用来设置2048byte的1st half page还是2nd half page,0表示1st,1表示2nd。Block的地址是由A18以上的bit来表示,也就是确定位于哪一块。用A[0:28]这29位,就可以将K9F2G08U0A这256M的数据存储空间全部访问到。
http://blog.csdn.net/feihuxiaozi/article/details/6943124


3.3 信号引脚
1. CLE(Command Latch Enable): 命令锁存允许
2. ALE(Address Lactch Enable): 地址锁存允许
3. CE:芯片选择
4. RE:读允许
5. WE:写允许
6. WP:在写或擦除期间,提供写保护
7. R/B:读/忙

二、Nand Flash读取编写
①页读: (页地址),此处实现页读。
②随机读: (页地址or行地址 +列地址)

2.1 实现按页读取Nand Flash
打开Nandflash手册后,一般会搜索operating,然后去找read时序图。然后I/Ox中可以了解到读取的步骤:
1.发送命令0x00 2.发送5个周期的地址 3.发送命令0x30 4.读取数据

然后有一个r/b信号,发送0x30信号后。它从高电平到低电平了,要等待它恢复到高电平后,把前面的事情做完,才能读取数据。同时在等待r/b信号之前,需要清除r/b信号
最后在操作Nandflash时需要选中芯片,最后要取消选中芯片。
总结:NF_PageRead的具体步骤就出来了。详细步骤见函数注释。

然后要对S3C2440中的具体寄存器操作:
  1. #define NFCONF (volatile unsigned long*) 0x4E000000
  2. #define NFCONT (volatile unsigned long*) 0x4E000004
  3. #define NFSTAT (volatile unsigned char*) 0x4E000020
  4. #define NFCMMD (volatile unsigned char*) 0x4E000008
  5. #define NFADDR (volatile unsigned char*) 0x4E00000C
  6. #define NFDATA (volatile unsigned char*) 0x4E000010

  7. void select_chip()
  8. {
  9.     *(NFCONT) &= ~(1<<1);
  10. }

  11. void dselect_chip()
  12. {
  13.     *(NFCONT) |= (1<<1);
  14. }

  15. void clear_RnB()
  16. {
  17.     *(NFSTAT) |= (1<<2);
  18. }

  19. void send_cmd(unsigned cmd)
  20. {
  21.     *(NFCMMD) = cmd;
  22. }

  23. void send_addr(unsigned addr)
  24. {
  25.     *(NFADDR) = addr;
  26. }

  27. void wait_RnB()
  28. {
  29.     while(!(*(NFSTAT)&(1<<2)))
  30.     {
  31.         ;
  32.     }
  33. }

  34. void NF_PageRead(unsigned long addr, unsigned char* buff)
  35. {
  36.     int i;
  37.     //选中Nand flash芯片
  38.     select_chip();

  39.     //清除R/B信号
  40.     clear_RnB();

  41.     //发送命令0x00
  42.     send_cmd(0x00);

  43.     //发送列地址
  44.     send_addr(0x00);
  45.     send_addr(0x00);

  46.     //发送行地址
  47.     send_addr(addr&0xff);
  48.     send_addr((addr>>8)&0xff);
  49.     send_addr((addr>>16)&0xff);

  50.     //发送命令0x30
  51.     send_cmd(0x30);

  52.     //等待R/B信号
  53.     wait_RnB();

  54.     //读取数据
  55.     for(i = 0; i<2048; i++)
  56.     {
  57.         buff[i] = *(NFDATA);
  58.     }

  59.     //取消选中nand flash芯片
  60.     dselect_chip();
  61. }

2.2 然后首先需要对Nand Flash初始化

在NFCONF中有TACLS、TWRPH0、TWRPH1三个属性。TACLS对应twp,TWRPH0对应tcls-twp,TWRPH1对应tclh的值。
其中twp、tcls、tclh最小值在nandflash中有对应的值。初始化是保证这个值大于最小值。
表格中有对应的公式,同时又涉及到了HCLK-->100MHz-->10ns。


  1. #define TACLS  1
  2. #define TWRPH0 2
  3. #define TWRPH1 1

  4. //初始化后必须复位
  5. void nand_reset()
  6. {
  7.     //选中flash
  8.     select_chip();
  9.     //清除Rnb信号
  10.     clear_RnB();
  11.     //发送0xff
  12.     send_cmd(0xff);
  13.     //等待RnB信号
  14.     wait_RnB();
  15.     //取消选中
  16.     dselect_chip();
  17. }

  18. void nand_init()
  19. {
  20.     //初始化NFCONF
  21.     *(NFCONF) = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);            //10ns    30ns    20ns   
  22.     //初始化NFCONT,使能nandflash,使能芯片选中
  23.     *(NFCONT) = (1<<0)|(1<<1);
  24.     //初始化后复位
  25.     nand_reset();
  26. }

2.4 启动时数据复制
2440中的启动IRAM只有4k,之前是把4k大小复制到内存中去运行的。但是这样做有一个问题,就是当我的bootload大于4k时就会出问题了。
这样首先要清楚程序启动流程,2440把nandflash中的启动代码复制4k到IRAM中,IRAM再把剩下的从Nandflash复制进内存中。最后从内存中运行剩下的bootload。
旧的复制程序:
  1. copy_to_ram:
  2.         ldr r0, =0x0
  3.         ldr r1, =0x30008000
  4.         add r3, r0, #1024*4
  5. copy_loop:
  6.         ldr r2, [r0], #4
  7.         str r2, [r1], #4
  8.         cmp r0, r3
  9.         bne copy_loop
  10.         mov pc, lr
新的复制程序:
  1. copy_to_ram:
  2.     mov r0, #0                            
  3.     ldr r1, =_start                               //从0地址开始拷贝
  4.     ldr r2, =bss_end                              //拷贝的结束地址
  5.     sub r2, r2, r1                                //size是地址相减

  6.     mov ip, lr                                    //保存lr指针,后面要跳转bl指令,
  7.     bl nand_to_ram                                //调用nand_to_ram,参数用r0,r1,r2中传递
  8.     mov lr, ip
  9.     mov pc, lr

  1. void nand_to_ram(unsigned long start_addr, unsigned char* sdram_addr, int size)
  2. {
  3.     int i;
  4.     for(i = (start_addr>>11);size >0;)            //左移11位页地址,只用了第一排的页
  5.     {
  6.         NF_PageRead(i, sdram_addr);               //存放到sdram_addr
  7.         size -= 2048;                             //size减少2048
  8.         sdram_addr += 2048;                       //sdram_addr要加上2k
  9.         i++;
  10.     }
  11. }
注意:之前的MPLLCON的宏设置地址不对。改回0x4c000004。这里我没错。。。。

错误记录:
Nand flash错误总结:
#define NFCMMD (volatile unsigned long*) 0x4E000008
#define NFADDR (volatile unsigned long*) 0x4E00000C
#define NFDATA (volatile unsigned long*) 0x4E000010
应改成,地址长度是不一样的。
#define NFCMMD (volatile unsigned char*) 0x4E000008
#define NFADDR (volatile unsigned char*) 0x4E00000C
#define NFDATA (volatile unsigned char*) 0x4E000010

#define NFCONF (volatile unsigned long*) 0x4E00C000
地址写错
#define NFCONF (volatile unsigned long*) 0x4E000000

NF_PageRead(1, sdram_addr);
1应该写成i,初始地址要不断增加
NF_PageRead(i, sdram_addr);

三、Nand Flash写入编写
3.1 同样找到page program operation

通过时序图,分析出操作步骤:
  1. int NF_WritePage(unsigned long addr, unsigned char* buff)
  2. {
  3.     int ret,i;
  4.     //选中芯片
  5.     select_chip();
  6.     //清除r/b
  7.     clear_RnB();
  8.     //发送命令0x80
  9.     send_cmd(0x80);
  10.     //发送列地址
  11.     send_addr(0x00);
  12.     send_addr(0x00);
  13.     //发送行地址
  14.     send_addr(addr&0xff);
  15.     send_addr((addr>>8)&0xff);
  16.     send_addr((addr>>16)&0xff);
  17.     //写入数据
  18.     for(i = 0; i<2048; i++)
  19.     {
  20.         *(NFDATA) = buff[i];
  21.     }

  22.     //发送命令0x10
  23.     send_cmd(0x10);
  24.     //等待R/B
  25.     wait_RnB();
  26.     //发送命令0x70
  27.     send_cmd(0x70);
  28.     //读取是否成功
  29.     ret = *(NFDATA);
  30.     //取消选中
  31.     dselect_chip();

  32.     return ret;
  33. }

3.2 写入之前需要擦除步骤

通过时序图,分析出操作步骤:
  1. int NF_Erase(unsigned long addr)
  2. {
  3.     int ret;
  4.     //选中芯片
  5.     select_chip();
  6.     //清除R/B
  7.     clear_RnB();
  8.     //发送命令0x60
  9.     send_cmd(0x60);
  10.     //发送行地址
  11.     send_addr(addr&0xff);
  12.     send_addr((addr>>8)&0xff);
  13.     send_addr((addr>>16)&0xff);
  14.     //发送擦除命令0xd0
  15.     send_cmd(0xD0);
  16.     //等待R/B
  17.     wait_RnB();
  18.     //发送命令0x70
  19.     send_cmd(0x70);
  20.     //读取擦除结果
  21.     ret = *(NFDATA);
  22.     //取消片选
  23.     dselect_chip();

  24.     return ret;
  25. }
3.3 在main.c函数中增加验证
  1.     NF_Erase(64*5 +1);                        //擦除
  2.     buf[0] =100;
  3.     NF_WritePage(64*5+1, buf);                //写入
  4.     buf[0] = 10;
  5.     NF_PageRead(64*5+1, buf);                 //读取

  6.     if(buf[0] == 100)
  7.     {
  8.         led_off();
  9.     }
  10.     else
  11.     {
  12.         led_on();
  13.     }



阅读(1222) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~