Nand flash基本操作

在本节内容中总结之前的nand flash操作,在另一篇文章中介绍了关于nor flash的一些测试,在那里简单的介绍了一下nor flash和nand flash的一些区别,这篇文章就来详细讲解一些nand flash的操作过程
使用开发板:jz2440
nand flash芯片:K9F2G08U0C
1、nand flash引脚介绍
在最开始的时候先来看一下jz2440上面的nand flash原理图,这一步好像是固定的,在每开始学习一款芯片的时候,都先要去看看它的原理图来了解最基本的操作信息,话不多少,上图
在这里插入图片描述
从原理图上可以看出nand flash的数据线是8位的,之前操作SDRAM的时候都是有地址线和数据线,那么这里好像没有区分地址线和数据线,这是因为在nand flash中地址线和数据线是复用的(8位)

引脚介绍
RnB:状态引脚,在读写的时候数据不可能立即达到,需要根据该引脚来判断芯片是否处于“忙状态”
CLE:命令锁存,当CLE为高电平的时候表示数据线上传输的是命令,并且在CLE为下降沿的时候锁存命令
CE:片选引脚,低电平有效
ALE:地址锁存,当ALE为高电平的时候表示数据线上传输的是地址,并且在ALE为下降沿的时候锁存地址
当ALE与CLE都为低电平的时候表示数据线上传输的是一般数据
WE:写信号,低电平有效
RE:读信号,低电平有效

2、操作时序
了解了nand flash的芯片引脚作用之后就可以去操作芯片了,那么在操作芯片之前还需要知道该芯片的操作时序才行,查看开发板上的nand flash数据手册,寻找关于操作时序的内容
以读ID的过程来分析一下nand flash的操作时序,其他的擦除、写操作也就是一个道理了
在这里插入图片描述

1、片选信号CE拉低,选中新芯片,并在整个过程中都处于低电平
2、CLE拉高,WE拉低,表明接下来的是写命令
3、数据线发出读ID命令90h
4、在WE信号上升沿锁存数据
5、CLE拉低,写命令过程结束
6、ALE拉高,WE拉低,表明接下来的是写地址
7、数据线发出地址数据,并在WE信号为上升沿的时候锁存数据
8、RE信号拉低,开始读取数据,在RE信号上升沿的时候锁存数据

从读取芯片ID的过程可以看出操作nand flash的整个过程的时序还是有一点复杂的,不过不用担心,在jz2440开发板中接有nand flash控制器,那么复杂的时序就由nand flash控制器来替我们发出去,我们只需要控制nand flash控制器即可,那么,我们该如何来使用nand flash控制器呢,其实我们的需求就只有发命令、发地址、读数据,每一个动作都对应nand flash控制器的一个寄存器,所以只需要去操作对应的寄存器就行了,接下来,先总结一下后面需要使用的命令
3、操作指令

发出0x00命令发出行列地址发出0x30命令等待就绪,并读取数据
发出0x80命令发出行列地址写数据发出0x10命令,等待就绪
擦除发出0x60命令发出行地址发出0xd0命令等待就绪

从上面的表格中可以看出,在操作的时候经常要发出行和列地址,这是为什么呢?
因为nand flash的结构可以看做是一张表格,既然是表格的话就有行和列的区分
在这里插入图片描述
我们所使用的这款nand flash芯片的结构如上图,它的每一个page的大小是2K,也就是从0到2047,后面的区域是OOB区域,用于nand flash的纠错,大小是64个字节
在这里插入图片描述
发送数据的时候根据上面的表来发送,先发出列地址的低8位,然后发出列地址的高8位
然后发出行地址的第八位,然后是中间八位以及最后的高八位
4、nand flash控制器初始化
在这里插入图片描述

nand flash最基本的时序信号
TACLS	:发出ALE/CLE之后多久可以发出写信号
TWRPH0	:写信号维持的时间
TWRPH1	:释放写信号之后多久可以释放ALE/CLE

在这里插入图片描述
在这里插入图片描述

TACLS = tcls - twp         tcls(最小12ns) - twp(最小12ns),TACLS可以为0  
TWRPH0 = twp(最小12ns)
TWRPH1 = tclh(最小5ns)	

在开发板上配置寄存器满足上面的时序
在这里插入图片描述
在这里插入图片描述

TACLS可以配置为0,所以[13:12] = 0
HCLK(10ns) x ( TWRPH0 + 1 ) >= 12  TWRPH0 >= 1 [10:8] = 1
HCLK x ( TWRPH1 + 1 ) >= 5   TWRPH1 >= 0	   [6:4] = 0
  [0] : 1 使能nand flash控制器
  [1] : 1 开始的时候先禁止片选
  [4] : 1 使能ECC
####################################################################
void nand_init(void)
{
#define  TACLS   0
#define  TWRPH0  1
#define  TWRPH1  0
 /*设置NAND FLASH的时序*/
 NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
 /*使能NAND FLASH控制器,初始化ECC,禁止片选*/
 NFCONT = (1<<4) | (1<<1) | (1<<0);
}

5、实现函数

使能片选、禁止片选

void nand_select(void)
{
 /*使能片选*/
 NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
 /*禁止片选*/
 NFCONT |= (1<<1);
}

发送命令、地址

/* 写命令,往NFCCMD寄存器写值 */
bashvoid nand_cmd(unsigned char cmd)
{
 volatile int i;
 /* 直接写入命令就行 */
 NFCCMD = cmd;
 for(i=0; i<10; i++);
}

/* 写地址,往NFDATA寄存器写值 */
void nand_addr_byte(unsigned char addr)
{
 volatile int i;
 /* 直接写入地址 */
 NFADDR = addr;
 for(i=0; i<10; i++);
}
/* 读数据,直接返回NFDATA寄存器的值就行 */
unsigned char nand_data(void)
{
 return NFDATA;
}
/*写数据,往寄存器NFDATA寄存器写值*/
void nand_w_data(unsigned char val)
{
 NFDATA = val;
}

/* 等待就绪,读取状态寄存器的引脚 */
void wait_ready(void)
{
 while (!(NFSTAT & 1));
}

测试菜单

void nand_flash_test(void)
{
 char c;
  while (1)
 {
  /* 打印菜单, 供我们选择测试内容 */
  printf("[s] Scan nand flash\n\r");
  printf("[e] Erase nand flash\n\r");
  printf("[w] Write nand flash\n\r");
  printf("[r] Read nand flash\n\r");
  printf("[q] quit\n\r");
  printf("Enter selection: ");
    c = getchar();
  printf("%c\n\r", c);
    switch (c)   
  {
   case 'q':
   case 'Q':
    return;
    break;
   case 's':
   case 'S':
    nand_chip_id();
    break;
   case 'e':
   case 'E':
    do_erase_nand_flash();
    break;
   case 'w':
   case 'W':
    do_write_nand_flash();
    break;
   case 'r':
   case 'R':
    do_read_nand_flash();
    break;
   default:
    break;
  }
 }
}

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
 int i = 0;
 int page = addr / 2048;		/* 计算行地址 */
 int col  = addr % 2048;		/* 计算列地址 */
 nand_select(); 			/* 使能片选 */
  while (i < len)
 {
   /* 发出00h命令 */
  nand_cmd(00);
  /* 发出地址 col addr */
  nand_addr_byte(col & 0xff);			/* 低8位 */
  nand_addr_byte((col>>8) & 0xff);		/* 高8位 */
  /* row/page addr */
  nand_addr_byte(page & 0xff);
  nand_addr_byte((page>>8) & 0xff);
  nand_addr_byte((page>>16) & 0xff);
  /* 发出30h命令 */
  nand_cmd(0x30);
  /* 等待就绪 */
  wait_ready();
  /* 读数据 */
  for (; (col < 2048) && (i < len); col++)
  {
   buf[i++] = nand_data();   
  }
  if (i == len)	/* 根据上面的循环条件,如果i先等于len,就说明是读取的数据长度已经满足,跳出 */
   break;
  col = 0;	/* 否则,调到下一个page的第0列开始读取,直到i==len */
  page++;
  }
   nand_deselect();  //禁止片选
}

void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
 int page = addr / 2048;
 int col  = addr % 2048;
 int i = 0;
 nand_select(); 
 while (1)
 {
 /* 发出写命令 */
  nand_cmd(0x80);
  /* 发出地址 col addr */
  nand_addr_byte(col & 0xff);
  nand_addr_byte((col>>8) & 0xff);
  /* row/page addr */
  nand_addr_byte(page & 0xff);
  nand_addr_byte((page>>8) & 0xff);
  nand_addr_byte((page>>16) & 0xff);
  /* 发出数据 */
  for (; (col < 2048) && (i < len); )
  {
   nand_w_data(buf[i++]);
  }
  /* 发出确定命令,并等待就绪 */
  nand_cmd(0x10);
  wait_ready();
  if (i == len)
   break;
  else
  {
   /* 开始下一个循环page */
   col = 0;
   page++;
  }
 }
  nand_deselect(); 
}

擦除

int nand_erase(unsigned int addr, unsigned int len)
{
 int page = addr / 2048;	/* 擦除一块,只需要发送行地址就行 */
 /* 地址对齐 */
 if (addr & (0x1FFFF))
 {
  printf("nand_erase err, addr is not block align\n\r");
  return -1;
 }
 /* 长度对齐 */
 if (len & (0x1FFFF))
 {
  printf("nand_erase err, len is not block align\n\r");
  return -1;
 }
 nand_select(); 
 while (1)
 {
  page = addr / 2048;
  nand_cmd(0x60);
  /* row/page addr */
  nand_addr_byte(page & 0xff);
  nand_addr_byte((page>>8) & 0xff);
  nand_addr_byte((page>>16) & 0xff);
  nand_cmd(0xD0);
  wait_ready();
  len -= (128*1024);
  if (len == 0)
  	break;
  addr += (128*1024); /* 调到下一个page */
  }
  nand_deselect(); 
  return 0;
 }

6、扩展
nand flash的特性引起nand flash中有坏块的存在,那么在读的时候进行坏块的检测,如果是坏块就跳到下一个block
检测坏块

int nand_bad(unsigned int addr)
{
 /* 读取block的第2048个字节,每一个block的检测数据存放在OOB区(2048字节处),如果这个block为坏块,则2048字节处读出的数据是0xff */
 unsigned int col  = 2048;
 unsigned int page = addr / 2048;
 unsigned char val;
 /* 1. 选中 */
 nand_select();
 /* 2. 发出读命令00h */
 nand_cmd(0x00);
 /* 3. 发出地址(分5步发出) */
 nand_col(col);
 nand_page(page);
 /* 4. 发出读命令30h */
 nand_cmd(0x30);
 /* 5. 判断状态 */
 wait_ready();
 /* 6. 读数据 */
 val = nand_data();
 /* 7. 取消选中 */  
 nand_deselect();
 if (val != 0xff)
  return 1;  /* bad blcok */
 else
  return 0;
 }

读函数改进

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
 int col = addr % 2048;		/* 计算列地址 */
 int i = 0;
 while (i < len)
 {
   if (!(addr & 0x1FFFF) && nand_bad(addr)) /* 一个block只判断一次 */
   {
     addr += (128*1024);  /* 跳过当前block */
     continue;
   }
   /* 1. 选中 */
   nand_select();
   /* 2. 发出读命令00h */
   nand_cmd(0x00);
   /* 3. 发出地址(分5步发出) */
   nand_addr(addr);
   /* 4. 发出读命令30h */
   nand_cmd(0x30);
   /* 5. 判断状态 */
   wait_ready();
   /* 6. 读数据 */
   for (; (col < 2048) && (i < len); col++)
   {
    buf[i] = nand_data();
    i++;
    addr++;
   }
   if (i == len)
     break;
    col = 0;
  }
  /* 7. 取消选中 */  
  nand_deselect();
}
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值