第16课Nand Flash
1.简介
引导启动时,NAND FLASH存储器开始的4K字节将被加载到Steppingstone中并且执行加载到Steppingstone的引导代码。
通常引导代码会复制NAND FLASH的内容到SDRAM中,通过使用硬件ECC,有效地检查NAND FLASH的数据在赋值完成的基础上,将在SDRAM中执行主程序。
注:
当自动引导启动期间,ECC不会去检测,所以,NAND FLASH的开始4KB不应当包含位相关的错误。
2.原理图
===


3.启动的引脚配置


4.操作原理
1.原理图上NAND FLASH和S3C2440之间只有数据线,怎么传输地址?
DATA0~DATA7上既传输数据,又传输地址
ALE为高电平时传输的是地址
CLE为高电平时传输的是命令
ALE,CLE都为低电平时传输的是数据
RnB为高表示就绪,为低表示正忙
2.怎么操作NAND FLASH呢?
根据NAND FLASH的芯片手册,一般的过程是:
a.发出命令
选中芯片
CLE设为高电平
在DATA0~DATA7上出输出命令值
b.发出地址
选中芯片
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
c.发出数据/读数据
Ⅰ.发出数据
选中芯片
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
Ⅱ.读数据
选中芯片
发出读脉冲
读DATA0~DATA7的数据


5.ID与地址编码




1.选中芯片
2.发出命令 0x90
3.发出地址 0x00
4.读数据得到 0xEC
5.读数据得到device code (DAh)
6.读数据得到 (10h)
7.读数据得到页大小,块大小(15h)
8.读数据得到 (44h)
9.退出读ID状态
6.读NAND FLASH
===

1.选中芯片
2.发出0x00命令
3.发出地址 col(低8bit)
4.发出地址 col(高)
5.发出地址 page(低)
6.发出地址 page(中)
7.发出地址 page(高)
8.发出命令0x30
9.wait_ready
10.读数据
11.退出读状态
7.写NAND FLASH
===

1.选中芯片
2.发出0x80命令
3.发出地址 col(低8bit)
4.发出地址 col(高)
5.发出地址 page(低)
6.发出地址 page(中)
7.发出地址 page(高)
8.发出命令0x10
9.发出数据
8.擦除
===

擦除的最小单位(block)
1.选中芯片
2.发命令0x60
3.发出地址 page(低)
4.发出地址 page(中)
5.发出地址 page(高)
6.发命令0xD0
7.wait_ready
9.NAND FLASH的结构
===

1block = 64page
1page = 2048col
OOB为了解决Nand的位反转的缺点而存在的。
2k的page data CPU使用addr寻址。其后的64bit的OOB区CPU无法访问。
eg:
CPU访问Nand Flash上的第2048位数据实际上是访问Page1的第0位
10.寄存器解析
===
NFCONF:Nand Flash配置寄存器

被用来设置NAND FLASH的时序参数TACLS、TWRH0、WERPH1、设置数据位宽;还有一些只读位,用来指示是否支持其他大型的页。
NFCONT: Nand Flash控制寄存器

被用来使能/禁止NAND FLASH控制器、使能/禁止控制引脚信号nFCE、初始化ECC。它还有一些其他功能,在一般的应用中用不到,比如锁定NAND FLASH
NFCMMD: 命令寄存器

NFADDR: 地址寄存器

NFDATA: 数据寄存器

只用到了低8位,读写此寄存器将对NAND FLASH的读数据、写数据操作
NFSTAT: 状态寄存器

只用到了[0]:0:buys 1:ready
11.代码实现
===
nand_flash.h
ifndef _NAND_FLASH_H
define _NAND_FLASH_H
extern void nand_init(void);
extern void nand_flash_test(void);
endif /* _NAND_FLASH_H */
nand_flash.c
include "s3c2440_soc.h"
include "my_printf.h"
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);
}
void nand_select(void)
{
/使能片选/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/禁止片选/
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCCMD = cmd;
for(i=0; i<10; i++);
}
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
void wait_ready(void)
{
while (!(NFSTAT & 1));
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();
printf("maker id = 0x%x\n\r",buf[0]);
printf("device id = 0x%x\n\r",buf[1]);
printf("3rd byte = 0x%x\n\r",buf[2]);
printf("4th byte = 0x%x\n\r",buf[3]);
printf("page size = %d kb\n\r",1 << (buf[3] & 0x03));
printf("block size = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%x\n\r",buf[4]);
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
nand_select();
while (i < len)
{
/* 发出00h命令 */
nand_cmd(00);
/* 发出地址 /
/ 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);
/* 发出30h命令 */
nand_cmd(0x30);
/* 等待就绪 */
wait_ready();
/* 读数据 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
int nand_erase(unsigned int addr, unsigned int len)
{
int page = addr / 2048;
/* addr是page的起始地址,长度为一个page的长度 */
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 -= (1281024);
if (len == 0)
break;
addr += (1281024);
}
nand_deselect();
return 0;
}
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
int page = addr / 2048;
int col = addr & (2048 - 1);
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();
}
void do_read_nand_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
printf("Data : \n\r");
/* 长度固定为64 /
for (i = 0; i < 4; i++)
{
/ 每行打印16个数据 /
for (j = 0; j < 16; j++)
{
/ 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 /
if (str[j] < 0x20 || str[j] > 0x7e) / 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
}
void do_erase_nand_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nand_erase(addr, 128*1024);
}
void do_write_nand_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...\n\r");
nand_write(addr, str, strlen(str)+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);
/* 测试内容:
* 1. 识别nand flash
* 2. 擦除nand flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
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;
}
}
}

浙公网安备 33010602011771号