NAND Flash 在嵌入式系统中的地位与PC上的硬盘类似,用于保存系统运行所必需的操作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存掉电数据丢失不同, NAND Flash 中的数据在掉电后仍可永久保存。
1、操作方法概述
访问 NAND Flash 时需要先发出命令,然后发出地址序列,最后读/写数据;需要使用各个使能信号来分辨是命令、地址还是数据。 S3C2410 的 NAND Flash 控制器提供了 NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT和NFECC 等 6 个寄存器来简化这些操作。 S3C2440 的 NAND Flash 控制器则提供了 NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT 和其他与ECC有关的寄存器。对 NAND Flash 控制器的操作, S3C2410 和 S3C2440 有一点小差别:有些寄存器的地址不一样,有些寄存器的内容不一样。
NAND Flash 的读写操作次序如下:
<strong><span style="font-family:KaiTi_GB2312;font-size:18px;"> 设置NFCONF(对于 S3C2440 , 还要设置 NFCONT)寄存器,配置 NAND Flash。
向 NFCMD 寄存器写入命令。
向 NFADDR 寄存器写入地址。
读/写数据:通过寄存器 NFSTAT 检测 NAND Flash 的状态,在启动某个操作后,应该检测 R/nB 信号以确定该操作是否完成、是否成功。</span></strong>
2、寄存器介绍
NFCONF :NAND Flash 配置寄存器。
S3C2410 的 NFCONF 寄存器。
被用来使能/禁止 NAND Flash 控制器、 使能/禁止控制引脚信号 nFCE、初始化ECC、设置NAND Flash的时序参数等。
S3C2440 的 NFCONF 寄存器。
被用来设置 NAND Flash 的时序参数 TACLS、TWRPH0、TWRPH1,设置数据位宽;还有一些只读位,用来指示是否支持其他大小的页(比如一页大小为 256/512/1024/2048 字节)。
它没有实现 S3C2410 的 NFCONF 寄存器的控制功能,这些功能在 S3C2440 的NFCONT 寄存器里实现。
NFCONT:NAND Flash 控制寄存器, S3C2410 没有这个寄存器。
被用来使能/禁止 NAND Flash 控制器、使能/禁止控制引脚信号 nFCE、初始化ECC。它还有其他功能,在一般的应用中用不到,比如锁定 NAND Flash。
NFCMD:NAND Flash 命令寄存器。(同 S3C2440 的 NFCMMD 寄存器)
对于不同型号的 Flash,操作命令一般不一样。参考相关数据手册。
NFADDR:NAND Flash 地址寄存器。
当写这个寄存器时,它将对 Flash 发出地址信号。
NFDATA:NAND Flash 数据寄存器。
只用到低8位,读、写此寄存器将启动对 NAND Flash 的读数据、写数据操作。
NFSTAT:NAND Flash 状态寄存器
只用到位0, 0:busy ,1:ready
代码实例:
@******************************************************************************
@ File:head.s
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@******************************************************************************
.text
.global _start
_start:
ldr sp, =4096 @ 设置堆栈
@ 因为函数 disable_watch_dog、memsetup、nand_init、nand_read
@ 都是在 C 程序中定义
bl disable_watch_dog @ 关闭开门狗
bl memsetup @ 初始化 SDRAM
bl nand_init @ 初始化 NAND Flash
@ 将 NAND Flash 中地址4096开始的1024字节代码(main.c编译得到)复制到 SDRAM 中
@ nand_read 函数需要3个参数,分别在 r0 , r1 , r2
ldr r0, =0x30000000 @ 目标地址,这是 SDRAM 的起始地址
mov r1, #4096 @ 源地址,连接的时候,main.c中的的代码都存在 NAND Flash 地址 4096 开始处
mov r2, #2048 @ 复制长度。(单位 bytes)
bl nand_read @ 调用 C 函数 nand_read
ldr sp, =0x34000000 @ 设置栈
ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ b指令和bl指令只能前后跳转32M的返回,所以这里使用向PC赋值的方法进行跳转
halt_loop:
b halt_loop
/*
init.c 关闭开门狗 初始化SDRAM
*/
/*WATCH DOG 寄存器*/
#define WTCON (*(volatile unsigned long *)0x53000000)
/*SDRAM 寄存器*/
#define MEM_CTL_BASE 0x48000000
void disable_watch_dog();
void memsetup();
//上电后,WATCH DOG默认是开着的,要把它关掉
void disable_watch_dog(){
WTCON = 0;
}
//设置控制SDRAM的13个寄存器
void memsetup(){
int i = 0;
unsigned long *p = (unsigned long *)MEM_CTL_BASE;
unsigned long const mem_cfg_val[] = {
0x22011110, // BWSCON
0x00000700, // BANKCON0
0x00000700, // BANKCON1
0x00000700, // BANKCON2
0x00000700, // BANKCON3
0x00000700, // BANKCON4
0x00000700, // BANKCON5
0x00018005, // BANKCON6
0x00018005, // BANKCON7
0x008C07A3, //REFRESH
0x000000B1, //BANKSIZE
0x00000030, //MRSRB6
0x00000030, //MRSRB7
};
for(;i < 13; ++i)
p[i] = mem_cfg_val[i];
}
/*
nand.c
*/
#define LARGER_NAND_PAGE
#define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
#define BUSY 1
#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
#define NAND_SECTOR_SIZE_LP 2048
#define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1)
typedef unsigned int S3C24X0_REG32;
/*NAND FLASH (see S3C2410 manual chapter 6)*/
typedef struct{
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFECC;
} S3C2410_NAND;
/*NAND FLASH (see S3C2440 manual chapter 6)*/
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} S3C2440_NAND;
typedef struct {
void (*nand_reset)(void);
void (*wait_idle)(void);
void (*nand_select_chip)(void);
void (*nand_deselect_chip)(void);
void (*write_cmd)(int cmd);
void (*write_addr)(unsigned int addr);
unsigned char (*read_date)(void);
} t_nand_chip;
static S3C2410_NAND * s3c2410nand = (S3C2410_NAND *)0x4e000000;
static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
static t_nand_chip nand_chip;
/* 供外部调用的函数 */
void nand_init(void);
void nand_read(unsigned char *buf, unsigned long start_addr, int size);
/* NAND Flash操作的总入口, 它们将调用S3C2410或S3C2440的相应函数 */
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static unsigned char read_date(void);
/* S3C2410的NAND Flash处理函数 */
static void s3c2410_nand_reset(void);
static void s3c2410_wait_idle(void);
static void s3c2410_nand_select_chip(void);
static void s3c2410_nand_deselect_chip(void);
static void s3c2410_write_cmd(int cmd);
static void s3c2410_write_addr(unsigned int addr);
static unsigned char s3c2410_read_data(void);
/* S3C2440的NAND Flash处理函数 */
static void s3c2440_nand_reset(void);
static void s3c2440_wait_idle(void);
static void s3c2440_nand_select_chip(void);
static void s3c2440_nand_deselect_chip(void);
static void s3c2440_write_cmd(int cmd);
static void s3c2440_write_addr(unsigned int addr);
static unsigned char s3c2440_read_data(void);
/*S3C2410的NAND Flash操作函数*/
//复位
static void s3c2410_nand_reset(void){
s3c2410_nand_select_chip(); //发出片选信号
s3c2410_write_cmd(0xff); //reset命令
s3c2410_wait_idle(); // 循环查询 NFSTAT位0, 直到它等于1
s3c2440_nand_deselect_chip(); //进制 NAND Flash
}
//等待NAND Flash就绪 (循环查询 NFSTAT位0, 直到它等于1)
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++);
}
// 发出片选信号 (NFCONF 位 11 写入0)
static void s3c2410_nand_select_chip(void){
int i;
s3c2410nand->NFCONF &= ~(1 << 11);
for(i = 0; i < 10; i++);
}
//取消片选信号(禁止 NAND Flash)
static void s3c2410_nand_deselect_chip(void){
s3c2410nand->NFCONF |= (1 << 11);
}
//发出命令(将命令对应的数字 写入寄存器 NFCMD 中)
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++);
}
// 读取数据(取出 NFDATA寄存器中的数据)
static unsigned char s3c2410_read_data(void){
volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA;
return *p;
}
/* S3C2410的NAND Flash操作函数 */
// 复位
static void s3c2440_nand_reset(void){
s3c2440_nand_select_chip(); //发出片选信号
s3c2440_write_cmd(0xff); //reset命令
s3c2440_wait_idle(); // 循环查询 NFSTAT位0, 直到它等于1
s3c2440_nand_deselect_chip(); //进制 NAND Flash
}
//等待NAND Flash就绪 (循环查询 NFSTAT位0, 直到它等于1)
static void s3c2440_wait_idle(void){
int i;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
while(!(*p & BUSY))
for(i = 0; i < 10; i++);
}
// 发出片选信号 (NFCONT 位 1 写入0)
static void s3c2440_nand_select_chip(void){
int i;
s3c2440nand->NFCONT &= ~(1 << 1);
for(i = 0; i < 10; i++);
}
//取消片选信号(禁止 NAND Flash)
static void s3c2440_nand_deselect_chip(void){
s3c2440nand->NFCONT |= (1 << 1);
}
//发出命令(将命令对应的数字 写入寄存器 NFCMMD 中)
static void s3c2440_write_cmd(int cmd){
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMMD;
*p = cmd;
}
// 发出地址信号
static void s3c2440_write_addr(unsigned int addr){
int i;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->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 void s3c2440_write_addr_lp(unsigned int addr){
int i;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
int col, page;
col = addr & NAND_BLOCK_MASK_LP;
page = addr / NAND_SECTOR_SIZE_LP;
*p = col & 0xff; /* Column Address A0~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 Address A28~A29 */
for(i = 0; i < 10; i++);
}
// 读取数据(取出 NFDATA寄存器中的数据)
static unsigned char s3c2440_read_data(void){
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
return *p;
}
static void nand_reset(void){
nand_chip.nand_reset();
}
static void wait_idle(void){
nand_chip.wait_idle();
}
static void nand_select_chip(void){
int i;
nand_chip.nand_select_chip();
for(i = 0; i < 10; i++);
}
static void nand_deselect_chip(void){
nand_chip.nand_deselect_chip();
}
static void write_cmd(int cmd){
nand_chip.write_cmd(cmd);
}
static void write_addr(unsigned int addr){
nand_chip.write_addr(addr);
}
static unsigned char read_date(void){
return nand_chip.read_date();
}
/* 初始化NAND Flash */
void nand_init(void){
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 判断是S3C2410还是S3C2440 */
if((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002)){
nand_chip.nand_reset = s3c2410_nand_reset;
nand_chip.wait_idle = s3c2410_wait_idle;
nand_chip.nand_select_chip = s3c2410_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
nand_chip.write_cmd = s3c2410_write_cmd;
nand_chip.write_addr = s3c2410_write_addr;
nand_chip.read_date = s3c2410_read_data;
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */
s3c2410nand->NFCONF = (1 << 15) | (1 << 12) | (1 << 11) | (TACLS << 8) | (TWRPH0 << 4) | (TWRPH1 << 0);
}
else{
nand_chip.nand_reset = s3c2440_nand_reset;
nand_chip.wait_idle = s3c2440_wait_idle;
nand_chip.nand_select_chip = s3c2440_nand_select_chip;
nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;
nand_chip.write_cmd = s3c2440_write_cmd;
#ifdef LARGER_NAND_PAGE
nand_chip.write_addr = s3c2440_write_addr_lp;
#else
nand_chip.write_addr = s3c2440_write_addr
#endif
nand_chip.read_date = s3c2440_read_data;
/* 设置时序 */
s3c2440nand->NFCONF = (TACLS << 12) | (TWRPH0 << 8) | (TWRPH1 << 4);
/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
s3c2440nand->NFCONT = (1 << 4) | (1 << 1) | (1 << 0);
}
/* 在第一次使用NAND Flash前,复位一下NAND Flash */
nand_reset();
}
/*
读函数
从 NAND Flash 位置 start_addr 开始,将数据复制到 SDRAM 地址 buf 处,共复制 size 字节
*/
void nand_read(unsigned char *buf, unsigned long start_addr, int size){
int i, j;
#ifdef LARGER_NAND_PAGE
if((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)){
return ; /* 地址或长度不对齐 */
}
#else
if((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)){
return ; /* 地址或长度不对齐 */
}
#endif
/* 选中芯片 */
nand_select_chip();
for(i = start_addr; i < (start_addr + size); ){
/* 发出READ0命令 */
write_cmd(0);
/* Write Address */
write_addr(i);
#ifdef LARGER_NAND_PAGE
write_cmd(0x30);
#endif
wait_idle();
#ifdef LARGER_NAND_PAGE
for(j = 0; j < NAND_SECTOR_SIZE_LP; j++,i++){
#else
for(j = 0; j < NAND_SECTOR_SIZE; j++,i++){
#endif
*buf = read_date();
buf++;
}
}
/* 取消片选信号 */
nand_deselect_chip();
return ;
}
/*
main.c
*/
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2))
void wait(volatile unsigned long dly)
{
for(; dly > 0; dly--);
}
int main(void)
{
unsigned long i = 0;
GPFCON = GPF4_out|GPF5_out|GPF6_out; // 将LED1-3对应的GPF4/5/6三个引脚设为输出
while(1){
wait(30000);
GPFDAT = (~(i<<4)); // 根据i的值,点亮LED1-3
if(++i == 8)
i = 0;
}
return 0;
}
/*
nand.lds
*/
SECTIONS {
firtst 0x00000000 : {head.o init.o nand.o }
second 0x30000000 : AT(4096) { main.o }
}
# Makefile
objs := nand.o head.o init.o main.o
nand.bin : $(objs)
arm-linux-ld -Tnand.lds -o nand_elf $^
arm-linux-objcopy -O binary -S nand_elf $@
arm-linux-objdump -D -m arm nand_elf > nand.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f nand.dis nand_elf nand.bin *.o