上一篇文章主要学习了NAND Falsh的基础知识,今天则来总结NAND Flash的初始化,以及块擦除,页的读写操作,以及芯片id号的获取。
6. NAND FLASH 的操作接口
我们知道,函数在调用时需要使用栈,当项目的函数调用关系复杂时,尤其是存在多级
函数调用时,容易导致栈溢出,此处函数调用的开销也会逐渐加大。
为了更好的解决上述问题,一般使用宏的形式来实现规模较小的函数,因为宏调用时在
预处理阶段,由预处理器对源程序中的宏进行展开,所以宏展开不占用运行时间。
因为每一次宏调用都需要进行宏展开,所以会加大程序的代码量,因此规模较大的函数
不宜使用宏的形式来实现
① NAND FLASH 使能函数
#define NF_Enable() {rNFCONT &=~(1<<1);} //选中芯片
② NAND FLASH 失能函数
#define NF_Disable() {rNFCONT |=(1<<1);}
③ NAND FLASH 发送命令函数
#define NF_Send_Cmd(cmd) {rNFCMD = (cmd);}
④ NAND FLASH 发送地址函数
#define NF_Send_Addr(addr) {rNFADDR =addr;}
⑤ NAND FLASH 发送数据函数
#define NF_Send_Data(data) {rNFDATA8 =(data);}
⑥ NAND FLASH 使能忙检测函数
#define NF_Enable_RB() {rNFSTAT |= (1<< 4);}
⑦ NAND FLASH 判忙函数
#define NF_Check_Busy() {while(!(rNFSTAT& (1 << 0)));} // 0 busy ,1 ready
⑧ NAND FLASH 读字节函数
#define NF_Read_Byte() (rNFDATA8)
7. 怎样对 NAND 进行复位?
① 选中 NAND FLASH
② 使能 NAND 判忙检测
③ 发送复位命令(0xff)
④ 等待复位完成
⑤ 取消选中 NAND FLASH
8. 怎样对 NAND 进行初始化?
① 配置 GPA 相关 IO 口
② 设置相关时间参数
③ 关闭 NAND 中断,打开 NAND 控制器
④ NAND 相关状态清零
⑤ 复位 NAND
9. 怎样擦除一个块?
① 复位 NAND
② 选中 NAND FLASH
③ 使能 NAND 判忙检测
④ 发送块擦除发起命令(0x60)
⑤ 发送块地址
⑥ 发送块擦除确认命令(0xd0)
⑦ 等待擦除完成
⑧ 取消选中芯片
10. 怎样写一页?
① 复位 NAND FLASH
② 选中 NAND FLASH
③ 使能 NAND 判忙检测
④ 发送写页发起命令(0x80)
⑤ 发送页地址
⑥ 发送 2048 个数据
⑦ 发送写页确认命令(0x10)
⑧ 等待写完成
⑨ 取消选中芯片
11. 怎样读一页
① 复位 NAND FLASH
② 选中 NAND FLASH
③ 发送页读取发起命令(0x00)
④ 发送读地址
⑤ 发送页读取确认命令(0x30)
⑥ 等待命令完成
⑦ 读取 2048 个命令
⑧ 取消选中芯片
12.NAND FLASH 的地址模式
源代码分析(代码编译环境:win8,keil4):
宏定义,具体参数查DATASHEET,不再赘叙。
#define rNFCONF (*(volatile unsigned long *)(0x4e000000))
#define rNFCONT (*(volatile unsigned long *)(0x4e000004))
#define rNFCMD (*(volatile unsigned long *)(0x4e000008))
#define rNFADDR (*(volatile unsigned long *)(0x4e00000c))
#define rNFDATA8 (*(volatile unsigned char *)(0x4e000010))
#define rNFSTAT (*(volatile unsigned long *)(0x4e000028))
#define rGPACON (*(volatile unsigned long *)(0x56000000))
//NAND FLASH 使能函数
#define NF_Enable() {rNFCONT &= ~(1<<1);} //选中芯片
//NAND FLASH 失能函数
#define NF_Disable() {rNFCONT |= (1<<1);}
//NAND FLASH 发送命令函数
#define NF_Send_Cmd(cmd) {rNFCMD = (cmd);}
//NAND FLASH 发送地址函数
#define NF_Send_Addr(addr) {rNFADDR = addr;}
//NAND FLASH 发送数据函数
#define NF_Send_Data(data) {rNFDATA8 = (data);}
//NAND FLASH 使能忙检测函数
#define NF_Enable_RB() {rNFSTAT |= (1 << 4);}
//NAND FLASH 判忙函数
#define NF_Check_Busy() {while(!(rNFSTAT & (1 << 0)));} // 0 busy ,1 ready
//NAND FLASH 读字节函数
#define NF_Read_Byte() (rNFDATA8) //因为需要赋值操作,所以不加;
一些操作命令
注意:某些命令需要发送两次,操作前后各发送一次,成对出现,第二次发送的是确认命令。
NAND Flash复位函数
#define NF_CMD_RESET 0xff
void nand_reset(void) //nand复位函数
{
NF_Enable(); //选中芯片
NF_Check_RB(); //忙检测
NF_Send_Cmd(NF_CMD_RESET); //发送复位命令
NF_Check_Busy(); //忙等待
NF_Disable(); //取消选中芯片
}
NAND Flash初始化函数
#define TACLS 1
#define TWRPH0 4
#define TAWTH1 1
#define NAND_INT_DIABLE 0
#define NAND_ENABLE 1
void nand_init(void)
{
rGPACON &= ~(0x3f<<17); //未操作将操作的位置清零
rGPACON |= (0x3f<<17); //初始化GPA
rNFCONT = (TACLS<<12)|(TWRPH0<<8)|(TAWTH1<<4);
rNFCONT = (NAND_INT_DIABLE<<12)|(NAND_ENABLE<<0);
rNFSTAT = 0x0; //NAND相关状态清零
nand_reset(); //复位
}
擦除一块内容
#define NF_CMD_ERASE1 0x60
#define NF_CMD_ERASE2 0xd0
void nand_erase(unsigned int block) //块擦除
{
unsigned int blocknum = (block<<6); //左移6位(A12-A17)到A18
nand_reset();
NF_Enable();
NF_Check_RB();
NF_Send_Cmd(NF_CMD_ERASE1); //发送块擦除指令
NF_Send_Addr(blocknum & 0xff); //&0xff是为了擦除高百位 第一次发送实际只发送A18 A19两位
NF_Send_Addr((blocknum>>8) & 0xff); //发送接下来的8位地址
NF_Send_Addr((blocknum>>16) & 0xff);
NF_Send_Cmd(NF_CMD_ERASE2); //发送确认块擦除指令
NF_Check_Busy(); //忙等待
NF_Disable(); //取消选中芯片
}
A0-A11用来页内寻址
A18-A28用来块寻址
A12-A17用来表示某块中的第几页
NAND寻址需要发送5次,一次发送8位数据(8个I/O口)。
block<<6,block左移6位,原本第三次(因为发送了指令,根据NAND内部机制,直接从第三次开始发送)A12的数据成了A18的数据,而A18-A28用来块寻址。
blocknum>>8,blocknum右移8位,即将block左移的6位再加上第三次发送的2位地址。
向NAND写入一页内容
#define NF_CMD_WRITE1 0x80
#define NF_CMD_WRITE2 0x10
void nand_write_page(unsigned int block,unsigned int page,unsigned char * str) //写一页
{
unsigned int pagenum = (block<<6) + page;
unsigned int i;
nand_reset(); //nand复位
NF_Enable(); //选中芯片
NF_Check_RB(); //忙检测
NF_Send_Cmd(NF_CMD_WRITE1); //发送读操作指令
NF_Send_Addr(0x0);
NF_Send_Addr(0x0);
NF_Send_Addr(pagenum & 0xff);
NF_Send_Addr((pagenum>>8) & 0xff);
NF_Send_Addr((pagenum>>16) & 0xff);
for(i=0; i<2048; i++)
{
NF_Send_Data(str[i]); //发送数据
}
NF_Send_Cmd(NF_CMD_WRITE2); //发送读操作确认命令
NF_Check_Busy(); //忙等待
NF_Disable(); //取消选中芯片
}
读取一页内容
#define NF_CMD_READ1 0x00
#define NF_CMD_READ2 0x30
void nand_read_page(unsigned int block,unsigned int page,unsigned char * str) //读一页
{
unsigned int pagenum = (block<<6) + page;
unsigned int i;
nand_reset(); //nand复位
NF_Enable(); //选中芯片
NF_Check_RB(); //忙检测
NF_Send_Cmd(NF_CMD_READ1); //发送读操作指令
NF_Send_Addr(0x0);
NF_Send_Addr(0x0);
NF_Send_Addr(pagenum & 0xff);
NF_Send_Addr((pagenum>>8) & 0xff);
NF_Send_Addr((pagenum>>16) & 0xff);
NF_Send_Cmd(NF_CMD_READ2);
NF_Check_Busy(); //忙等待
for(i=0; i<2048; i++)
{
str[i] = NF_Read_Byte(); //读字节
}
NF_Disable(); //取消选中芯片
}
读取NAND Flash的ID号
#define NF_CMD_READ_ID 0x90
void nand_read_ID(unsigned char * str) //读取nand flash芯片id
{
unsigned int i;
NF_Enable(); //选中nand flash
NF_Check_RB(); //忙检测
NF_Send_Cmd(NF_CMD_READ_ID); //发送读取芯片id命令
NF_Send_Addr(0x0);
NF_Check_Busy(); //忙等待
for(i=0; i<2048; i++)
{
str[i] = NF_Read_Byte(); //读字节
}
NF_Disable(); //取消选中芯片
}