Mini2440裸机开发之Nand Flash 编程
一、Nand Flash命令
1.1 命令表
对Nand Flash的操作需要发出命令,下面有个Nand Flash的命令表格,那么我们可以此表格上的命令来访问我们的Nand Flash。
针对每一个命令的时序可以参考Nand Flash芯片使用手册。下面我们将会分析一些常用命令的时序。时序中部分信号的信息如下:
- 当ALE为高电平时传输的是地址;
- 当CLE为高电平时传输的是命令;
- 当ALE,CLE都为低电平表示传输的是数据 ;
- $\overline{CE}$片选信号,低电平有效;
- $\overline{RE}$读使能,低电平有效;
- $\overline{WE}$写使能,低电平有效;
1.2 Read ID时序分析
- 第一条竖线位置,写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,90h命令被锁存;
- 第二条竖线,写地址,发送了$\overline{WE}$、$ALE$、$\overline{CE}$信号,地址00被锁存;
- 继续往后,命令、地址都发完了,要read数据了,所以释放$\overline{WE}$,$ALE$,这里$tAR$表示$ALE$释放多久后才可以发送$\overline{RE}$信号,$tREA$表示$\overline{RE}$信号的建立时间;
- 第三条竖线位置,发送了$\overline{CE}$,$\overline{RE}$信号,所以数据被锁存,第一个访问周期锁存的数据为marker code,值为0xEC,第二个访问周期的数据为device code,值为0xDA。读id时读5个周期含义对应如下表:
该Nand Flash的5个周期读取出来的值对应如下:
第三个访问周期含义如下表:
第四个访问周期含义如下表:
第五个访问周期含义如下表:
根据第4、5个访问周期的结果0x15、0x44我们得知该Nand Flash的block_size=128K,page_size=2k, 有2个plane,plane_size=1Gb = 128M, 共256M。
1.3 页读取(page read)
- 第一条竖线,写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,00h命令被锁存;
- 然后,写地址,发送了$\overline{WE}$、$ALE$、$\overline{CE}$信号,NDFLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址,关于5个周期地址的计算可以参考上一篇博客;
- 写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,30h命令被锁存;
- 然后会有一个busy时间段,$R/\overline{B}$为低电平。$tRR$表示busy状态的持续时间(手册上最小为20ns);
- .开始锁存数据,$\overline{RE}$使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期($tRC$),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据;
1.4 块擦除(block erase)
- 首先发送0x60命令;
- 发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可);
- 发送0xDO命令,执行擦除动作
- 然后会有一个busy时间段,$R/\overline{B}$为低电平;
- 发送0x70命令,用来读取状态;
- 判断NFDATA寄存器的第0位是否擦除成功;
1.5 页写入(page write)
- 首先发送0x80命令;
- 发送地址(5个周期);
- 发送数据;
- 发送0x10命令,执行烧写动作;
- 然后会有一个busy时间段,$R/\overline{B}$为低电平;
- 发送0x70命令,用来读取状态;
- 判断NFDATA寄存器的第0位是否烧写成功
二、寄存器介绍以及初始化
2.1 寄存器
Nand控制器要按照我们Nand Flash的实际型号和性能来设置初始值。
S3C2440 Nand Flash相关寄存器如下:
2.2 配置寄存器(NFCONF)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFCONF | 0X4E000000 | R/W | Nand Flash配置寄存器 | 0x0000100X |
寄存器位信息:
NFCONF | 位 | 描述 | 初始状态 |
保留 | [15:14] | 保留 | —— |
TACLS | [13:12] |
CLE 和ALE 持续值设置(0 至3) Duration = HCLK × TACLS |
01 |
保留 | [11] | 保留 | 0 |
TWRPH0 | [10:8] |
TWRPH0 持续值设置(0~7) Duration = HCLK × ( TWRPH0 + 1 ) |
000 |
保留 | [7] | 保留 | 0 |
TWRPH1 | [6:4] |
TWRPH1 持续值设置(0~7) Duration = HCLK × ( TWRPH1 + 1 ) |
000 |
AdvFlash |
[3] |
自动引导启动用的先进NAND Flash 存储器。 |
硬件设置 |
PageSize |
[2] |
自动引导启动用的NAND Flash 存储器的页面大小。 |
硬件设置 |
AddrCycle |
[1] |
自动引导启动用的NAND Flash 存储器的地址周期。 |
硬件设置 |
BusWidth |
[0] |
自动引导启动和普通访问用的NAND Flash 存储器的输入输出总线宽 |
硬件设置 |
假设$HCLK=100MHZ$,则$T=10ns$。前面一节分析了:
$$TACLS = max(tCLS,tALS) - tWP$$
我们从NAND手册得知$tCLS$、$tALS$、$tWP$最小都可以取到12ns, 所以我们可以取$TACLS=0$;
$$TWRPH0 = tWP$$
我们的NAND手册上要求$tWP$最少12ns, 那么取$TWRPH0 =1$, $Duration = HCLK*(TWRPH0+1)=20ns>12ns$,满足要求;
$$TWRPH1 = max(tCLH,tALH)$$
我们的NAND手册上要求$tCLH$、$tALH$最少5ns, 那么取$TWRPH1 =0$, $Duration = HCLK*(TWRPH1+1)=10ns>5ns$,满足要求。
再配置BusWidth总线位宽为8bit;
所以NFCONF寄存器设置如下:
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/*设置Nand Flash的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
2.3 控制寄存器(NFCONT)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFCONF | 0X4E000004 | R/W | Nand Flash控制寄存器 | 0x0384 |
寄存器位信息:
NFCONT | 位 | 描述 | 初始状态 |
保留 | [15:14] | 保留 | 0 |
Lock-tight | [13] |
紧锁配置(Lock-tight) |
0 |
SotLock | [12] |
软件上锁设置 |
1 |
保留 | [11] | 保留 | 0 |
EnbIllegalAccINT | [10] |
非法访问中断控制 |
0 |
EnbRnBINT | [9] |
RnB 状态输入信号传输中断控制 |
0 |
RnB_TransMode |
[8] |
RnB 传输检测配置 |
0 |
保留 |
[7] |
保留 |
0 |
SpareECCLock |
[6] |
锁定备份区域ECC 产生 |
1 |
MainECCLock |
[5] |
锁定主数据区域ECC 生成 |
1 |
InitECC |
[4] |
初始化ECC 编码器/译码器(只写) |
0 |
保留 | [3:2] | 保留 | 00 |
Reg_nCE | [1] |
NAND Flash 存储器nFCE 信号控制 |
1 |
MODE | [0] |
NAND Flash 控制器运行模式 |
0 |
MODE [0]: 设置为1,使能NAND控制器。
Reg_nCE [1]: 设置为1,禁止片选(等要使用的时候再使能片选信号)。
所以NFCONF寄存器设置如下:
/*使能Nand Flash控制器,禁止片选*/
NFCONT = (1<<1) | (1<<0);
2.4 命令寄存器(NFCMMD)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFCMMD | 0X4E000008 | R/W | Nand Flash命令寄存器 | 0x00 |
寄存器位信息:
NFCMMD | 位 | 描述 | 初始状态 |
保留 | [15:8] | 保留 | 0x00 |
NFCMMD | [7:0] | Nand Flash存储器命令值 | 0x00 |
我们可以使用2440上的Nand Flash控制器简化操作,只需要往NFCMMD寄存器写入要传输的命令就可以了,Nand Flash控制器默认把上面复杂的时序发出来。
2.5 地址寄存器(NFADDR)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFADDR | 0X4E00000C | R/W | Nand Flash地址寄存器 | 0x00000XX00 |
寄存器位信息:
NFADDR | 位 | 描述 | 初始状态 |
保留 | [15:8] | 保留 | 0x00 |
NFADDR | [7:0] | Nand Flash存储器地址值 | 0x00 |
发命令后,后面就需要发送地址了,当$nWE$和$ALE$有效的时候,表示锁存的是地址,往NFADDR寄存器中写值就可以了,比如:NFADDR=0x00。
上一节我们得知地址需要用5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址。
① column:列地址A0~A10,就是页内地址,地址范围是从0到2047。(A11用来确定oob的地址,即2048-2111这64个字节的范围)
② page:A12~A28,称作页号,page(row)编号。
2.6 数据寄存器(NFDATA)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFDATA | 0X4E000010 | R/W | Nand Flash数据寄存器 | 0xXXXX |
寄存器位信息:
NFDATA | 位 | 描述 | 初始状态 |
NFDATA | [31:0] | NAND Flash 读取/编程数据给I/O | 0xXXXX |
当命令、地址都发送完后就可以从数据总线上DATA[7:0]获取数据或者写入数据。同样往NFDATA寄存器中写值或者读值就可以了,如unsigned char buf=NFDATA,由于是数据位宽是8位的,所以访问时数据组织形式如下:
从上图可以看出,当word access时,只需一个时钟周期;当byteaccess的时候,需要4个时钟周期,小端模式下第一个时钟周期对应低字节,第四个时钟周期对应高字节。nand_wait_rdle函数等待Nand Flash空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时Nand Flash是空闲的,我们可以通过该位来判断Nand Flash是否繁忙。代码如下:
/* 等待NAND Flash就绪 */ void _nand_wait_idle(void) { int i; while(!(NFSTAT & BUSY)); for(i=0; i<10; i++); }
2.7 状态寄存器(NFSTAT)
寄存器信息:
寄存器 | 地址 | R/W | 描述 | 复位值 |
NFSTAT | 0X4E000020 | R/W | Nand Flash运行状态寄存器 | 0xXX00 |
寄存器位信息:
NFSTAT | 位 | 描述 | 初始状态 |
保留 | [7] | 保留 | 0xXXXX |
保留 | [6:4] |
保留 |
X |
illegalAccess | [3] |
软件锁定或紧锁一次使能。非法访问(编程,擦除)存储器屏蔽此位设置 0:不检测非法访问 1:检测非法访问 |
0 |
$R/\overline{B}$_TransDetect | [2] |
当$R/\overline{B}$由低变高时发生传输,如果使能了此位则设置和发出中断。要清 除此位时对其写入‘1’ 0:不检测$R/\overline{B}$ 传输 1:检测$R/\overline{B}$ 传输 传输配置设置在$R/\overline{B}$_TransMode(NFCONT[8])中 |
0 |
$\overline{CE}$(只读) | [1] | $\overline{CE}$ 输出引脚的状态 | 1 |
$R/\overline{B}$(只读) | [0] |
$R/\overline{B}$ 输入引脚的状态 0:NAND Flash 存储器忙 1:NAND Flash 存储器运行就绪 |
1 |
之前在介绍页读取命令时,我们说过,当发完命令、地址后再进行读数据前我们知道有一段时间$tRR$处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。
三 代码
在前面我们已经介绍了NAND的读写等命令以及时序图,这里就不再单独介绍具体的步骤,下面直接上代码。
3.1 区分nand或者nor启动
我们知道nand启动0x00地址对应片内SRAM,可以像内存一样的写0x00地址;nor启动,0x00地址对应片内nor flash,nor falsh不能像内存一样的写地址, 所以往0x00地址写入数据成功表示nand启动,写不成功表示nor启动。
int is_boot_from_nor_flash(void) { volatile u32 *p = (volatile u32 )0x00; u32 val = *p; /* get value from address 0x00 */ *p = 0x12345678; /* write value to address 0x00 */ /* 写成功, 对应nand启动 */ if(*p == 0x12345678){ *p = val; return 0; } return 1; }
3.2 nand.h
/***************************************************************************************************************** * * FileName : nand.h * Function : Nand Flash设置 * Author : zy * K9F2G08U0C 258M 28位地址 * ****************************************************************************************************************/ #ifndef __NAND_H__ #define __NDND_H__ #include "type.h" #define GSTATUS1 (*(volatile u32 *)0x560000B0) #define BUSY 1 /* page size 2048byte */ #define NAND_PAGE_SIZE 2048 #define NAND_PAGE_MASK_SIZE (NAND_PAGE_SIZE - 1) #define NAND_BLOCK_SIZE (64*NAND_PAGE_SIZE) #define NAND_BLOCK_MASK_SIZE (NAND_BLOCK_SIZE-1) /******************************************************************************************************************/ /* Nand Flash (see S3C2440 manual chapter 6, www.100ask.net) */ #define NFCONF (*(volatile u32 *)0x4e000000) // 配置寄存器 #define NFCONT (*(volatile u32 *)0x4e000004) // 控制寄存器 #define NFCMMD (*(volatile u8 *)0x4e000008) // 命令寄存器 #define NFADDR (*(volatile u8 *)0x4e00000C) // 地址寄存器 #define NFDATA (*(volatile u8 *)0x4e000010) // 数据寄存器 #define NFMECCD0 (*(volatile u32 *)0x4e000014) #define NFMECCD (*(volatile u32 *)0x4e000018) #define NFSECCD (*(volatile u32 *)0x4e00001C) #define NFSTAT (*(volatile u32 *)0x4e000020) #define NFESTAT0 (*(volatile u32 *)0x4e000024) #define NFESTAT1 (*(volatile u32 *)0x4e000028) #define NFMECC0 (*(volatile u32 *)0x4e00002C) #define NFMECC1 (*(volatile u32 *)0x4e000030) #define NFSECC (*(volatile u32 *)0x4e000034) #define NFSBLK (*(volatile u32 *)0x4e000038) #define NFEBLK (*(volatile u32 *)0x4e00003C) /******************************************************************************************************************/ typedef struct { /* 函数指针作为结构体成员 */ /* 组成完整的命令操作的步骤 */ void (*_nand_reset)(void); /* 初始化nand控制器 */ void (*_nand_wait_idle)(void); /* 等待NAND Flash就绪 */ void (*_nand_select_chip)(void); /* 使能片选 */ void (*_nand_deselect_chip)(void); /* 禁止片选 */ void (*_nand_write_cmd)(u32 cmd); /* 发命令 */ void (*_nand_write_addr)(u32 addr); /* 发地址 */ u8 (*_nand_read_data)(void); /* 读数据 */ /* 完整的命令操作 */ int (*nand_read_data)(u8 *buf, u32 start_addr, u32 size); /* 发送读取数据命令, start_addr参数必须按页对齐 success:0 fail:-1 */ void (*nand_read_id)(u8 *buf); /* 读ID,buf length 5*/ int (*nand_block_erase)(u32 start_addr,u32 size); /* 块擦除(block erase),参数必须按块对齐 success:0 fail:-1 */ int (*nand_write_data)(u32 start_addr,u8 *buf, u32 size); /* 发送写数据命令, start_addr参数必须按页对齐 success:0 fail:-1*/ }t_nand_chip; /* 供外部调成员和方法 */ extern t_nand_chip nand_chip; /* nand_init执行后可以使用,extern全局变量避免重复定义 */ extern void nand_init(void); /* 初始化NAND Flash,给nand_chip函数指针成员赋值 */ #endif
3.3 nand.c
/****************************************************************************************************/ #include "nand.h" /* 全局变量 */ t_nand_chip nand_chip; /****************************************************************************************************/ /* 等待NAND Flash就绪 */ void _nand_wait_idle(void) { int i; while(!(NFSTAT & BUSY)); for(i=0; i<10; i++); } /* 发出片选信号 */ void _nand_select_chip(void) { int i; NFCONT &= ~(1<<1); /* set CE=0 */ for(i=0; i<10; i++); } /* 取消片选信号 */ void _nand_deselect_chip(void) { NFCONT |= (1<<1); /* set CE=1 */ } /* 发出命令 */ void _nand_write_cmd(u8 cmd) { int i; NFCMMD = cmd; for(i=0; i<10; i++); } /* 发出地址 */ void _nand_write_addr_byte(u8 addr) { volatile int i; NFADDR = addr; for(i=0; i<10; i++); } /* 发出地址,5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址 */ void _nand_write_addr(u32 addr) { int col, page; col = addr & NAND_PAGE_MASK_SIZE; page = addr / NAND_PAGE_SIZE; /* 块号 */ _nand_write_addr_byte(col & 0xff); /* Column Address A0~A7 */ _nand_write_addr_byte((col >> 8) & 0x0f); /* Column Address A8~A11 */ _nand_write_addr_byte(page & 0xff); /* Row Address A12~A19 */ _nand_write_addr_byte((page >> 8) & 0xff); /* Row Address A20~A27 */ _nand_write_addr_byte((page >> 16) & 0x01); /* Row Address A28 */ } /* 复位 */ void _nand_reset(void) { _nand_select_chip(); _nand_write_cmd(0xff); // 复位命令 _nand_wait_idle(); _nand_deselect_chip(); } /* 读取数据 */ u8 _nand_read_data(void) { return NFDATA; } /* 写入数据 */ void _nand_write_data(u8 data) { int i; NFDATA = data; for(i=0; i<10; i++); } /* Read ID */ void nand_read_id(u8 *buf) { /* 选中芯片 */ _nand_select_chip(); /* write cmd 0x90 */ _nand_write_cmd(0x90); /* write addr 0x00 */ _nand_write_addr(0x00); /* read */ buf[0] = _nand_read_data(); /* 第一个访问周期锁存的数据为marker code,值为0xEC */ buf[1] = _nand_read_data(); /* 第二个访问周期的数据为device code,值为0xDA */ buf[2] = _nand_read_data(); /* 0X10 */ buf[3] = _nand_read_data(); /* 0X95*/ buf[4] = _nand_read_data(); /* 0X44 */ /* 取消片选信号 */ _nand_deselect_chip(); } /* 发送读取数据命令,start_addr参数必须按页对齐 success:0 fail:-1 */ int nand_read_data(u8 *buf, u32 start_addr, u32 size) { int i = 0; int j = 0; if (start_addr & NAND_PAGE_MASK_SIZE ) { return -1; /* 地址或长度不对齐 */ } /* 选中芯片 */ _nand_select_chip(); /* 按页读取 */ while (i < size) { /* 发出READ命令 */ _nand_write_cmd(0); /* write address */ _nand_write_addr(start_addr+i); /* write cmd */ _nand_write_cmd(0x30); /* wait */ _nand_wait_idle(); /* 读取一页数据 */ for(; (j < NAND_PAGE_SIZE) && (i < size); j++) { buf[i++] = _nand_read_data(); } j = 0; } /* 取消片选信号 */ _nand_deselect_chip(); return 0; } /* 块擦除(block erase),参数必须按块对齐 success:0 fail:-1 */ int nand_block_erase(u32 start_addr,u32 size) { int i; int page = start_addr / NAND_PAGE_SIZE; /* 块号 */ if ((start_addr & NAND_BLOCK_MASK_SIZE) || (size & NAND_BLOCK_MASK_SIZE)) { return -1; /* 地址或长度不对齐 */ } /* 选中芯片 */ _nand_select_chip(); for(i=start_addr; i < (start_addr + size);) { page = i / NAND_PAGE_SIZE; /* write cmd 0x60 */ _nand_write_cmd(0x60); /* write row addr */ _nand_write_addr_byte(page & 0xff); /* Row Address A12~A19 */ _nand_write_addr_byte((page >> 8) & 0xff); /* Row Address A20~A27 */ _nand_write_addr_byte((page >> 16) & 0x01); /* Row Address A28 */ /* write cmd 0xD0 */ _nand_write_cmd(0xD0); /* wait */ _nand_wait_idle(); /* write cmd 0x70 */ _nand_write_cmd(0x70); if (_nand_read_data()&0x1) { return -1; } i += NAND_BLOCK_SIZE; } /* 取消片选信号 */ _nand_deselect_chip(); return 0; } /* 发送写数据命令, start_addr参数必须按页对齐 success:0 fail:-1*/ int nand_write_data(u32 start_addr,u8 *buf, u32 size) { int i = 0; int j = 0; if (start_addr & NAND_PAGE_MASK_SIZE ) { return -1; /* 地址或长度不对齐 */ } /* 选中芯片 */ _nand_select_chip(); /* 按页写 */ while (i < size) { /* 发出WRITE命令 */ _nand_write_cmd(0x80); /* write address */ _nand_write_addr(start_addr + i); /* WRITE数据 */ for(; (j < NAND_PAGE_SIZE) && (i<size); j++) { _nand_write_data(buf[i++]); } /* write cmd */ _nand_write_cmd(0x10); /* wait */ _nand_wait_idle(); /* write cmd 0x70 */ _nand_write_cmd(0x70); if (_nand_read_data()&0x1) { return -1; } j = 0; } /* 取消片选信号 */ _nand_deselect_chip(); return 0; } /* 初始化NAND Flash, 给nand_chip函数指针成员赋值*/ void nand_init(void) { #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 nand_chip._nand_reset = _nand_reset; nand_chip._nand_wait_idle = _nand_wait_idle; nand_chip._nand_select_chip = _nand_select_chip; nand_chip._nand_deselect_chip = _nand_deselect_chip; nand_chip._nand_write_cmd = _nand_write_cmd; nand_chip._nand_write_addr = _nand_write_addr; nand_chip._nand_read_data = _nand_read_data; nand_chip.nand_read_data = nand_read_data; nand_chip.nand_read_id = nand_read_id; nand_chip.nand_block_erase = nand_block_erase; nand_chip.nand_write_data = nand_write_data; /* 设置时序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 使能NAND控制器 */ NFCONT = (1<<4)|(1<<1)|(1<<0); /* 复位NAND Flash */ _nand_reset(); }
nand_read_data函数,每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。
nand_write_data函数,每write一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。
nand_block_erase是以block为单位擦除的。
实际上,Nand Flash的型号K9F2G08U0C也支持随机读写,也就是支持单字节读写。这里就不介绍了,具体可以参考Nand Flash实验读ID和随机读写。
需要注意的是:在这段代码中使用了全局变量t_nand_chip nand_chip,nand_chip未初始化,存放在未始化数据段,也就是bss段。
3.4 main.c
#include "led.h" #include "uart.h" #include "printf.h" #include "nand.h" #include "init.h" u32 get_uint() { return NAND_BLOCK_SIZE*10; } void do_erase_nand_flash(void) { unsigned int addr; /* 获得地址 */ addr = get_uint(); printf("erasing ...\n\r"); if(nand_chip.nand_block_erase(addr, 128*1024) == 0) { printf("erasing success\n\r"); } } void do_read_nand_flash(void) { unsigned int addr; volatile u8 *p; int i, j; unsigned char buf[64]; /* 获得地址 */ addr = get_uint(); printf("reading ...\n\r"); nand_chip.nand_read_data(buf, addr, 64); p = (volatile u8 *)buf; printf("Data : \n\r"); /* 长度固定为64 */ for (i = 0; i < 4; i++) { /* 每行打印16个数据 */ for (j = 0; j < 16; j++) { /* 先打印数值 */ printf("%c ", *p++); } printf("\n\r"); } } void do_write_nand_flash(void) { unsigned int addr; u8 str[64]; int i; unsigned int val; /* 获得地址 */ addr = get_uint(); for(i=0;i<64;i++) { str[i] = 'A' + i%26; } printf("writing ...\n\r"); nand_chip.nand_write_data(addr,str, NAND_PAGE_SIZE); } void do_read_nand_id() { unsigned char buf[5]={0}; nand_chip.nand_read_id(buf); printf("maker id = 0x%x\n\r",buf[0]); // 0xEC printf("device id = 0x%x\n\r",buf[1]); // 0xDA printf("3rd byte = 0x%x\n\r",buf[2]); // 0x10 printf("4th byte = 0x%x\n\r",buf[3]); // 0x95 printf("page size = %d kb\n\r",1 << (buf[3] & 0x03)); // 2kb printf("block size = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03)); // 128kb printf("5th byte = 0x%x\n\r",buf[4]); // 0x44 } int main() { led_init(); // 串口初始化以及接收中断 uart_init(); nand_init(); int flag = is_boot_from_nor_flash(); do_write_nand_flash(); do_read_nand_flash(); do_erase_nand_flash(); do_read_nand_id(); if(flag){ led_on(LED2); } while(1) { led_turn(LED1); delay_ms(4000); } return 0; }
3.5 编译下载
运行结果通过串口输出如下:
四、代码下载
Young / s3c2440_project【6.nand_flash】
这个代码已经远超过4kb,并且没有将代码从NAND拷贝到SDRAM运行,只可以下载到SDRAM 0x30000000处运行。
参考文章: