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

嵌入式学习之Nand Flash编程

发布时间:2020-12-15 18:05:48 所属栏目:百科 来源:网络整理
导读:一、NandFlash内部结构: ??????? 不同开发板使用的NandFlash的型号可能不一样,本文只是以K9F1208U0M为例做个简单介绍。引脚描述如下所示: ? ?? NandFlash存储单元结构图如下所示: ???? Device、Block和Page之间的关系---1 Device = 4,096 Blocks = 4096*3

一、NandFlash内部结构:

??????? 不同开发板使用的NandFlash的型号可能不一样,本文只是以K9F1208U0M为例做个简单介绍。引脚描述如下所示:

?

?? NandFlash存储单元结构图如下所示:

???? Device、Block和Page之间的关系---1 Device = 4,096 Blocks = 4096*32 Pages = 128K Pages;1 Block = 32 Page;1 Page = 528 Byte = 512 Byte + 16 Byte。其中1 Page中包含有数据寄存器512 Byte和16 Byte的备用位用于ECC校验存储。所以有528 columns * 128K rows(Pages)。1 Page中的512 Byte的数据寄存器又分为两个部分1st 256 Bytes和 2nd 256 Bytes。用于数据存储的单元有 512 Bytes * 32 Pages * 4096 Blocks = 64 MB,用于ECC校验单元有16 Bytes * 32 Pages * 4096 Blocks = 2MB 。

二、NandFlash命令字:

?????? 操作NandFlash时,先传输命令,然后传输地址,最后进行数据的读/写。K9F1208U0M的命令字如下所示:

??

??????由于寻址需要26bit的地址,该26bit地址通过四个周期发送到NandFlash,如下图所示:

?

?????? Read 1操作:该操作是对512 Bytes * 32 Pages * 4096 Blocks = 64 M的数据寄存器进行寻址。第一个周期发送A7~A0的8bit Column地址,8bit的寻址范围是0~255,只能对1st 256 Bytes部分进行寻址。00h命令是1st 256 Bytes部分寻址。当发送01h命令时,A8将会被置1,此时寻址范围变成了256~511了,所以01h命令是对2nd 256 Bytes部分进行寻址。(*注意:A8在发送00h命令后被清0,在发送01h命令后被置1,并且在发送01h对2nd寻址完毕后,A8会自动清0,指针会自动地指向1st);第二个周期的A9~A13的5bit是对Page进行寻址(因为1 Block = 32 Pages,5bit的寻址范围是0~31,可以对1 Block里面的所有Page进行寻址)。A14~A25的12bit则是对Block进行寻址,12bit的寻址范围是0~4095,对整个Device的4096个Blocks进行寻址。Read 2操作:该操作是对16 Bytes * 32 Pages * 4096 Blocks =2MB的备用位(ECC)进行寻址。50h命令为Read2操作,对1 Page里面的后16 Byte寻址。这样,通过四个周期的发送即可对整个Device的所有存储单元进行寻址。

  三、NandFlash存储控制器

  S3C2410为简化对NandFlash的操作,提供了一组NandFlash控制器来实现对K9F1208U0M命令字的操作,主要有配置寄存器NFCONF、控制寄存器NFCONT、命令寄存器NFCMD、地址寄存器NFADDR、数据寄存器NFDATA和状态寄存器NFSTAT。

  ★NFCONF被用来使能/禁止NandFlash控制器、使能/禁止控制引脚信号nFCE、初始化ECC、设置NandFlash的时序参数。TACLS、TWRPH0、TWRPH1---这三个参数控制着NandFlash信号线CLE/ALE与写控制信号new的时序关系。根据NandFlash的Datasheet中对其最小读/写/控制时间的要求,联系HCLK实际取值一般为100MHz,可以设这三个参数分别为1:3:1个HCLK即可(貌似ViVi中是1:3:1),这样可以满足其时序要求。

  ★NandFlash状态寄存器NFSTAT。只用到最低位[0],0:busy;1:ready。

  NandFlash存储控制器根据OM[1:0]位的取值可以工作在①自动启动模式---OM[1:0]=00时,复位之后,NandFlash的最先4KB的代码被复制到Steppingstone中即内部4KB的SRAM。Steppingstone被映射为Bank0(nGS0),且CPU在此4KB内部SRAM中开始执行启动代码;②NandFlash模式。

  示例代码解析:

  ★head.S头文件来设置SDRAM,设置SDRAM,将第二部分代码复制到SDRAM,然后跳到SDRAM继续执行。

.equ        MEM_CTL_BASE,0x48000000
 
  .text
 
  .global _start
 
  _start:
 
  bl  disable_watch_dog               @关门喂狗
 
  bl  mem_control_setup               @设置存储控制器
 
  ldr sp,=4096                                 @设置栈指针,以下C函数调用前需要设好栈
 
  bl  nand_init                                  @初始化NandFlash
 
  @将NandFlash中地址4096开始的1024字节代码(led.c编译得到)复制到SDRAM中
 
  ldr  r0,=0x30000000                  @目标地址=0x30000000,SDRAM起始地址
 
  mov  r1,#4096                            @源地址=4096,连接的时候led代码在4096开始处
 
  mov  r2,#1024                            @复制长度=1024,对于本实验的led足够
 
  bl  nand_read                              @调用C函数nand_read
 
  ldr  lr,=halt_loop                         @设置返回地址
 
  ldr  sp,=0x34000000                 @重新设置栈
 
  ldr  pc,=main                               @使用向pc赋值的方法进行跳转到点灯代码
 
  halt_loop:
 
  b  halt_loop


★nand.c文件实现NandFlash的初始化和数据读取

  #define BUSY? 1

  typedef unsigned long S3C2410_REG32; //貌似此处定义为unsigned int反汇编结果一样的,也没问题。猜可能是arm指令直接按32位存储了吧,知道的可以和我说一下!

/* NandFlash结构体 */
 
  typedef struct {
 
     S3C2410_REG32   NFCONF;
 
     ......
 
     S3C2410_REG32   NFECC;
 
  }S3C2410_NAND;
 
  static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
 
  /* 供外部调用的函数声明 */
 
  void nand_init(void);
 
  void nand_read(unsigned char *buf,unsigned long start_addr,int size);
 
  /* S3C2410的NandFlash处理函数声明 */
 
  static void s3c2410_nand_reset(void);
 
  ......
 
  static unsigned char s3c2410_read_data();
 
  /* S3C2410的NandFlash操作函数实现 */
 
  /* 复位 */
 
  static void s3c2410_nand_reset(void)
 
  {
 
     s3c2410_nand_select_chip();
 
     s3c2410_write_cmd(0xff);  //发命令字0xFF实现复位操作复位
 
     s3c2410_wait_idle();
 
     s3c2410_nand_deselect_chip();
 
  }
 
  /* 等待NandFlash就绪 */
 
  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);  //对NFCONF的11位写0,激活NandFlash
 
      for(i=0; i<10; i++);
 
  }
 
  /* 取消片选信号 */
 
  static void s3c2410_nand_deselect_chip(void)
 
  {
 
     s3c2410nand->NFCONF |= (1<<11);  //对NFCONF的11位写1,使NandFlash不活动
 
  }
 
  /* 发出命令 */
 
  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;
 
  }
 
  //设置TACLS、TWRPH0、TWRPH1三者的值,貌似ViVi等代码中TWRPH0设为3,不知这样的好处,知道的可以告诉我!
 
  #define TACLS   0
 
  #define TWRPH0  2
 
  #define TWRPH1  0
 
  /* 初始化NandFlash */
 
  void nand_init(void)
 
  {
 
      /* 使能NandFlash控制器,初始化ECC,禁止片选,设置时序 */
 
      s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
 
      /* 复位s3c2410 NandFlash */
 
      s3c2410_nand_reset();
 
  }
 
  #define NAND_SECTOR_SIZE    512
 
  #define NAND_BLOCK_MASK     (NAND_SECTOR_SIZE - 1)
 
  /* 读函数 */
 
  void nand_read(unsigned char *buf,int size)
 
  {
 
     int i,j;
 
     if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
 
     return ;    /* 地址或长度不对齐 */
 
  }
 
  /* 选中芯片 */
 
  s3c2410_nand_select_chip();
 
  for(i=start_addr; i < (start_addr + size);) {
 
     /* 发出READ0命令 */
 
     s3c2410_write_cmd(0);
 
     /* 写地址*/
 
     s3c2410_write_addr(i);
 
     /*等待*/
 
     s3c2410_wait_idle();
 
     for(j=0; j < NAND_SECTOR_SIZE; j++,i++) {
 
        *buf = s3c2410_read_data();
 
        buf++;
 
      }
 
  }
 
  /* 取消片选信号 */
 
  s3c2410_nand_deselect_chip();
 
  return ;
 
};

(编辑:李大同)

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

    推荐文章
      热点阅读