2008-04-29 18:12:08 阅读7 评论0 字号:大中小
{dy}章 存储器
概述
存储器通常分为两类型,即随机存取的 RAM 与只读的 ROM 。随着存储器技术的发展和嵌入式系统的要求一些新的混合型的存储器迅速发展起来,本章就目前广泛应用于嵌入式系统中的新一代存储器 Nand-Flash 、 Nor-Flash 及 SDRAM 的基础知识进行介绍。
Flash Memory 内部构架和实现技术可以分为 AND 、 NAND 、 NOR 和 DiNOR 等几种,但目前以 NAND 和 NOR 为主流。 NOR 技术是由 Intel 公司 1988 年首先推出, NAND 技术是由东芝公司 1989 年发明。 NAND 技术在设计之初是为了数据存储应用, NOR 则是为了满足程序代码的高速访问,并且支持程序在芯片内部运行。目前关于两种技术的发展前景讨论很激烈,各种观点很多。客观来看,二者各有优势和不足。 NOR 工作电压低、随机读取快、功耗低、稳定性高;而 NAND 则写回速度快、芯片面积小,特别是容量大有很大优势
由于 NOR 和 NAND 在存储单元组织上的差异,二者的寻址方式差异较大。 NAND 的地址分为三部分 : 块号、块内页号、页内字节号。一次数据访问, NAND 一般是经过三次寻址,先后确定块号、块内页号和页内字节号,至少占用三个时钟周期,因此随机字节读写速度慢。而 NOR 的地址线足够多,且存储单元是并列排布,可以实现一次性的直接寻址。另外,由于 NOR 的数据线宽度很大,即使容量增大,它的数据寻址时间基本上是一个常量,而 NAND 则比较困难。总之在数据传输速度上, NOR 无论是在随机读取还是连续传输速度上都比 NAND 快,但相对而言,在连续大数据传输速度上,二者差异较小。从产品成本来看, NOR Flash 的平均每 MB 成本是 NAND Flash 成本的三到四倍。成本价格的巨大差异,导致 NOR 容量增长比 NAND 困难。 NOR Flash 适用代码高速访问与执行,因此移动电话是 NOR 最常见的应用环境。
随机存取存储器 RAM ( Random Access Memory )是易失性的存储器,在掉电以后数据即消失,不能够长久保存。但与 ROM 器件不同的是,它的随机读写速度非常快,写入数据之前也不需要进行擦除,这些特性使它成为嵌入式系统中必不可少的存储设备之一。在嵌入式系统中,通常都将数据区和堆栈区放在 RAM 中,供快速的读写。常用的 RAM 分为 SRAM (静态 RAM )的 DRAM (动态 RAM )两种类型。 SRAM 中的位单元被赋予 0 或 1 的状态之后,它会保持这个状态直到下次被赋予新的状态,或者断电之后才会更改或消失。 SRAM 的速度相对比较快,而且比较省电,但是存储 1 位的信息需要 4 ~ 6 只晶体管,制造成本太高。 DRAM 与 SRAM 不同,存储一个位的信息只需要 1 只晶体管,但是需要周期性地充电,才能使保存的信息不消失。 DRAM 存储一个位的信息需要的晶体管比 RAM 要少得多,易于做成大容量,制造成本低。 SDRAM 即同步动态 RAM ,存储位单元的基本原理同前面提到的 DRAM 基本一样,但是这些存储位单元的组织和控制与 DRAM 就具有相当大的差别了。下面将分别介绍 Nand-Flash 、 Nor-Flash 及 SDRAM 存储器件。
1 、 Nand-Flash 存储器
1 . 1 概述
NOR 和 NAND 是目前市场上两种主要的非易失闪存技术。 Nor-flash 存储器的容量较小、写入速度较慢,但因其随机读取速度快,因此在嵌入式系统中,常应用在程序代码的存储中。 Nor-flash 存储器的内部结构决定它不适合朝大容量发展;而 NAND-flash 存储器结构则能提供极高的单元密度,可以达到很大的存储容量,并且写入和擦除的速度也很快。但 NAND-flash 存储器需要特殊的接口来操作,因此它的随机读取速度不及 Nor-flash 存储器。二者以其各自的特点,在不同场合发挥着各自的作用。
NAND-flash 存储器是 flash 存储器的一种技术规格,其内部采用非线性宏单元模式,为固态大容量存储器的实现提供了廉价有效的解决方案,因而现在得到了越来越广泛的应用。例如体积小巧的 U 盘就是采用 NAND-flash 存储器的嵌入式产品。
由于 NAND-flash (非线性 flash )存储器内部结构不同于 Nor-flash (线性 flash )存储器,因此对它的读写操作也有较大的区别。 BF533 中没有像支持 SDRAM 一样提供直接与 NAND-flash 存储器的接口,读写的过程要靠软件编程来完成。本实验将以东芝公司的 TC58DVM82A1FT 芯片为例,介绍 NAND-flash 存储器芯片的读写流程和时序。
1 . 2 实验内容和目标
包括以下几点。
编写程序,读出器件的识别码( ID )。
对 NAND-flash 的一个或若干个块进行擦除操作。
在被擦除的一个或若干个块写入数据。
将写入的数据读出并进行验证。
查找坏块。
1 . 3 NAND-flash 介绍及编程指导
NAND-flash 存储器中的宏单元彼此相连,仅{dy}个和{zh1}一个 Cell 分别与 Work Line 和 BIT Line 相连,因此 NAND-flash 架构的存储容量较 Nor-flash 架构的高。 NAND-flash 存储器的容量较大,改写速度快,主要应用在大量资料的存储,如嵌入式产品中,包括数码相机、 MP3 随身听记忆卡等。
1 .器件特性及引脚
器件特性如下:
供电电源 2.7~3.6V
存储空间组织 (32M+1024K)b × 8
数据寄存器 (512+16)b × 8
自动编程和擦除
页编程 每页 (512+16)B
块擦除 每块 (16K+512)B
页读操作
任意操作时间 10s
串行页操作 50ns
快速写周期时间
写入时间 200s
块擦除时间 2ms
命令、地址、数据复用 I/O 口。
硬件数据保护 当电源波动时,写入 / 擦除操作自动中止。
可靠性
耐久力 可经受 100k 次擦写;
数据保存 10 年
命令寄存器操作。
智能回拷贝。
采用{wy} ID 号保护版权。
封装 48 脚 TSOP I 。
引脚图
各引脚功能描述如下表所示:
2 .器件内部组织结构
TC58DVM82A1FT 的容量为 264Mb ,存储空间的组织是由 65536 个页(行)、每页中 528 个字节(列)的方式构成。备用的 16 列位于地址 512 ~ 527 。 TC58DVM82A1FT 还将存储空间分为块( block ),每一块由 32 个页构成。因此 TC58DVM82A1FT 中一共有 2048 个块。这种“块—页”的结构,恰好能满足文件系统中划分簇和扇区的结构要求。
TC58DVM82A1FT 的内部结构及地址形式如图所示。
读和写都以页为单位进行操作。擦除是基于块进行操作的。
TC58DVM82A1FT 的地址通过 8 位端口传送,这样,有效地节省了引脚的数量,并能够保持不同密度器件引脚的一致性,允许系统可以在电路不做改动的情况下升级为高容量存储器件。
由 TC58DVM82A1FT 的 CLE 和 ALE 信号线实现 I/O 口上指令和地址的复用。指令、地址和数据都通过拉低和从 I/O 口写入器件中。有一些指令只需要一个总线周期完成,例如,复位指令、读指令和状态读指令等;另外一些指令,例如页写入和块擦除,则需要 2 个周期,其中一个周期用来启动,而另一个周期用来执行。表中显示了 TC58DVM82A1FT 具备的指令。
3 .页读操作
在初始上电时,器件进入缺省的“读方式 1 模式”。在这一模式下,页读操作通过将 00h 指令写入指令寄存器,接着写入三个地址( 1 个列地址, 2 个行地址)来启动。一旦页读指令被器件锁存,下面的页读操作就不需要再重复写入指令了。
写入指令和地址后,处理器可以通过对信号线 R/ 的分析来判断该操作是否完成。如果信号为低电平,表示器件正“忙”;为高电平,说明器件内部操作完成,要读取的数据被送入了数据寄存器。外部控制器可以在以 50ns 为周期的连续脉冲信号的控制下,从 I/O 口依次读出数据。
连续页读操作中,输出的数据是从指定的列地址开始,直到该页的{zh1}一个列地址的数据为止。
读取流程:
读取操作示意图:
代码示例:
bool Read_NANDFlash(unsigned short Block_Address,unsigned short Page_Address)
{
int i;
NAND_Address_Table.Value = (Block_Address << 13)|(Page_Address << 8);
// 块地址和页地址作为参数传入函数,又分别左移 13 位和 8 位赋给共用体中的 Value 这样实际是将地址赋给了 A0 ~ A24
Write_Command(0x00); // 写入命令 00h
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A0_A7,
NAND_Address_Table.NAND_Address.NAND_Address_A9_A16,
NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0x03);
while(!Wait_NAND_RADY()); // 等待 NANDFlash 准备完毕
for(i = 0;i<512;i++) // 读取一页的 512 字节
{
Verify_Table[i] = *pNAND_BaseaAddr;
delay(100);
}
NAND_Finish();
return true;
}
4 .写入操作
TC58DVM82A1FT 的写入操作也以页为单位。写入之前必须先擦除,否则写入将出错。
页写入周期总共包括 3 个步骤:写入串行数据输入指令( 80h ),然后写入三个字节的地址信息,{zh1}串行写入数据。
串行写入的数据最多为 528 字节,它们首先被写入器件内的页寄存器,接着器件进入一个内部写入过程,将数据从页寄存器写入存储宏单元。
串行数据写入完成后,需要写入“页写入确认”指令 10h ,这条指令将初始化器件的内部写入操作。如果单独写入 10h 而没有前面的步骤,则 10h 不起作用。 10h 写入之后, TC58DVM82A1FT 的内部写控制器将自动执行内部写入和校验中必要的算法和时序,这是系统控制器就可以去做别的事了。
内部写入操作开始后,器件自动进入“读状态寄存器”模式,在这一模式下,当、为低电平时,系统就可以读取状态寄存器。系统可以通过检测 R/ 的输出,或读状态寄存器的状态位( I/O7 )来判断内部写入是否结束。在器件进行内部写入操作时,只有读状态寄存器指令和复位指令会被响应。当页写入操作完成,应该检测写状态位( I/O1 )的电平。
内部写校验只对 1 没有成功地写为 0 的情况进行检测。指令寄存器始终保持着读状态寄存器模式,直到其它有效的指令写入指令寄存器为止。
写入流程:
写入操作示意图
代码示例:
bool Page_Program(unsigned short Block_Address,unsigned short Page_Address)
{
int i;
unsigned char NAND_Data;
NAND_Address_Table.Value = (Block_Address << 13)|(Page_Address << 8);
// 块地址和页地址作为参数传入函数,又分别左移 13 位和 8 位赋给共用体中的 Value 这样实际是将地址赋给了 A0 ~ A24
Write_Command(0x80); // 写入命令 80h
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A0_A7,
NAND_Address_Table.NAND_Address.NAND_Address_A9_A16,
NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0x03);
delay(100);
for(i = 0;i<512;i++) // 向一页中写入 512 字节
{
*pNAND_BaseaAddr = Test_Data_Table[i];
delay(100);
}
Write_Command(0x10); // 写入命令 10h
delay(100);
while(!Wait_NAND_RADY()); // 等待 NANDFlash 准备完毕
delay(100);
Write_Command(0x70); // 写入命令 70h (用于读状态寄存器)
delay(100);
NAND_Data = *pNAND_BaseaAddr; // 读取 NANDFlash 中状态寄存器的值
NAND_Finish();
if(NAND_Data != 0xc0)
// 验证读取的值是否为 C0 ,即 I/O1 为 0 表示操作成功, I/O7 为 1 表示芯片忙状态结束
{
return false;
}
else
{
return true;
}
}
5 .块擦除
擦除操作是以块为单位进行的。擦除的启动指令为 60h ,随后,块地址的输入通过两个时钟周期完成。这时只有地址位 A14 到 A24 是有效的, A9 到 A13 则被忽略。块地址载入之后是擦除确认指令 D0h ,它用来初始化内部擦除操作。擦除确认命令用来防止外部干扰产生擦除操作的意外情况。器件检测到擦除确认命令输入后,在的上升沿启动内部写控制器开始执行擦除和擦除校验。内部擦除操作完成后,应该检测一下写状态位( I/O1 ),从而了解擦除操作是否有错误发生。
块擦除流程:
块擦除操作示意图:
代码示例:
bool Block_Erase(unsigned short Block_Address)
{
unsigned char NAND_Data;
NAND_Address_Table.Value = (Block_Address << 13);
// 块地址作为参数传入函数,左 ( 向高位)移 13 位赋给共用体中的 Value ,这样实际是将地址赋给了 A13 ~ A23
Write_Command(0x60); // 写入命令 90h
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A9_A16, NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0, 0x02);
Write_Command(0xd0); // 写入命令 d0h
delay(100);
while(!Wait_NAND_RADY()); // 等待 NANDFlash 准备完毕
Write_Command(0x70); // 写入命令 70h (用于读状态寄存器)
delay(100);
NAND_Data = *pNAND_BaseaAddr; // 读取 NANDFlash 中状态寄存器的值
NAND_Finish();
if(NAND_Data != 0xc0)
// 验证读取的值是否为 C0 ,即 I/O1 为 0 表示操作成功, I/O7 为 1 表示芯片忙状态结束
{
return false;
}
else
{
return true;
}
}
6 .读状态寄存器
TC58DVM82A1FT 包含一个状态寄存器,该寄存器反映了写入或擦除操作是否完成,或写入和擦除操作是否无错。写入 70h 指令,启动读状态寄存器周期。状态寄存器的内容将在或的下降沿处送出至 I/O 端口。下表显示了状态寄存器中每位的定义。
器件一旦接收到读状态寄存器的指令,它就将保持在状态寄存器读状态,直到有其它的指令输入。因此,如果在任意读操作中采用了状态寄存器读操作,则在连续页读的过程中,必须重发 00h 或 50h 指令。
读状态示意图:
代码示例:
bool Read_State(void)
{
Write_Command(0x70); // 写入命令 70h
delay(100);
NAND_Data = *pNAND_BaseaAddr; // 读取 NANDFlash 中状态寄存器的值
NAND_Finish();
if(NAND_Data != 0xc0)
// 验证读取的值是否为 C0 ,即 I/O1 为 0 表示操作成功, I/O7 为 1 表示芯片忙状态结束
{
return false;
}
else
{
return true;
}}
7 .读器件 ID
TC58DVM82A1FT 器件具有一个产品鉴定识别码( ID ),系统控制器可以读出这个 ID ,从而起到识别器件的作用。读 ID 的步骤是:写入 90 指令,然后写入一个地址 00h 。在两个读周期下,厂商代码和器件代码将被连续输出至 I/O 口。
同样,一旦进入这种命令模式,器件将保持这种命令状态,直到接收到其它的指令为止。
读器件 ID 示意图:
其中制造商代码为 0x98 ,器件代码为 0x75 ,因此, TC58DVM82A1FT 的 ID 为 0x9875 。相类似的,容量为 64MB 的 TC58DVM92A1FT 的 ID 则为 0x9876 。
代码示例:
bool Read_Chip_ID(void)
{
Write_Command(0x90); // 写入命令 90h
Write_Address(0x00,0x00,0x00,0x01); // 写入地址 0x00
delay(1000);
Chip_ID0 = *pNAND_BaseaAddr; // 读取 NANDFlash 一个周期输出的值 ( 制造商代码)
delay(100);
Chip_ID1 = *pNAND_BaseaAddr; // 读取 NANDFlash 另一周期输出的值(设备代码)
delay(100);
NAND_Finish(); // 结束 NANDFlash 操作
if(Chip_ID0 != 0xec) // 验证制造商代码
{
return false;
}
else if(Chip_ID1 != 0x75) // 验证设备代码
{
return false;
}
else
{
return true;
}
}
8 .复位
器件提供一个复位指令( RESET )指令,通过向指令寄存器写入 FFh 来完成对器件的复位。当器件处于任意读模式、写入或擦除模式的忙状态时,发送复位指令可以使器件中止当前的操作,正在被修改的存储器宏单元的内容不在有效,指令寄存器被清零并等待下一条指令的到来。当为高时,状态寄存器被清为 C0h 。
代码示例:
void Reset_NAND(void)
{
Write_Command(0xff);
while(!Wait_NAND_RADY());
NAND_Finish();
}
1 . 4 电路连接原理图
电路连接
其中 I/O1 ~ I/O8 连接 BF533 的 D0 ~ D7 。
1 . 5 程序代码
void Init_EBIU(void) // 初始化外部总线
{
*pEBIU_AMBCTL0 = 0x7bb07bb0;
*pEBIU_AMBCTL1 = 0xfffcfff0;
*pEBIU_AMGCTL = 0x000f;
}
void Init_SDRAM(void) // 初始化 SDRAM
{
//SDRAM Refresh Rate Control Register
*pEBIU_SDRRC = 0x00000817;
//SDRAM Memory Bank Control Register
*pEBIU_SDBCTL = 0x00000013;
//SDRAM Memory Global Control Register
*pEBIU_SDGCTL = 0x0091998d;
}
////////////////////////////////////////////////////////
// 写命令
////////////////////////////////////////////////////////
void Write_Command(unsigned char command)
{
Set_CE(0);
Set_CLE(1);
if(command == 0x10)
{
Write_NAND_FLASH(command);
}
else
{
Write_NAND_FLASH(command);
Set_CLE(0);
}
}
////////////////////////////////////////////////////////
// 写地址
////////////////////////////////////////////////////////
void Write_Address(unsigned char NAND_Address0,unsigned char NAND_Address1,
unsigned char NAND_Address2,
unsigned char Valid_Address)
{
Set_ALE(1);
if(Valid_Address == 1)
{
Write_NAND_FLASH(NAND_Address0);
}
else if(Valid_Address == 2)
{
Write_NAND_FLASH(NAND_Address0);
Write_NAND_FLASH(NAND_Address1);
}
else
{
Write_NAND_FLASH(NAND_Address0);
Write_NAND_FLASH(NAND_Address1);
Write_NAND_FLASH(NAND_Address2);
}
Set_ALE(0);
}
////////////////////////////////////////////////////////
// 等待 NANDFlash 准备完毕
////////////////////////////////////////////////////////
bool Wait_NAND_RADY(void)
{
while(*pFIO_FLAG_D != 0x0200)
{
*pFIO_INEN = 0x0000;
ssync();
*pFIO_FLAG_C = 0;
ssync();
*pFIO_INEN = 0x0200;
}
return true;
}
////////////////////////////////////////////////////////
// 结束 NANDFlash 操作
////////////////////////////////////////////////////////
void NAND_Finish(void)
{
Set_CE(1);
Set_CLE(0);
Set_ALE(0);
Command_Status = 0;
}
////////////////////////////////////////////////////////
// 写 NANDFlash( 本程序中用于地址写入)
////////////////////////////////////////////////////////
void Write_NAND_FLASH(unsigned char Write_Data)
{
*pNAND_BaseaAddr = Write_Data;
delay(100);
}
////////////////////////////////////////////////////////
// 设置 NANDFlash 片选信号 CE
////////////////////////////////////////////////////////
void Set_CE(bool state)
{
if(!state)
{
Command_Status = Command_Status | CLR_CE1;
*pNAND_CtrlBaseaAddr = Command_Status;
}
else
{
Command_Status = Command_Status & SET_CE1;
*pNAND_CtrlBaseaAddr = Command_Status;
}
delay(100);
}
////////////////////////////////////////////////////////
// 设置 NANDFlash 命令锁存使能信号 CLE
////////////////////////////////////////////////////////
void Set_CLE(bool state)
{
delay(100);
if(!state)
{
Command_Status = Command_Status & CLR_CLE;
*pNAND_CtrlBaseaAddr = Command_Status;
}
if(state)
{
Command_Status = Command_Status | SET_CLE;
*pNAND_CtrlBaseaAddr = Command_Status;
}
delay(100);
}
////////////////////////////////////////////////////////
// 设置 NANDFlash 地址锁存使能信号 ALE
////////////////////////////////////////////////////////
void Set_ALE(bool state)
{
delay(100);
if(!state)
{
Command_Status = Command_Status & CLR_ALE;
*pNAND_CtrlBaseaAddr = Command_Status;
}
if(state)
{
Command_Status = Command_Status | SET_ALE;
*pNAND_CtrlBaseaAddr = Command_Status;
}
delay(100);
}
///////////////////////////////////////////////////////////
//Main()
///////////////////////////////////////////////////////////
void main(void)
{
int i;
//Init_PLL(); // 初始化锁相环
Init_EBIU(); // 初始化总线
Init_SDRAM(); // 初始化 SDRAM
//Init_TEST_DATA_BUFFER(); // 初始化测试数据
Reset_NAND(); // NAND Flash 复位
LedMode1(); // 用 LED 指示程序开始运行
NAND_Address_Table.Value = 0x0; // 初始化 NAND Flash 的写入地址
if(!Read_Chip_ID()) // 读取芯片 ID
{
while(1);
}
/*
for(i=0; i<0x3F480; i++) // <--| 填充 SDRAM
{ // | 更改循环变量可以改变要填充的
*(pSdramStartAdr+i) = 0xAAAA; // | 起始地址和数量
}
*/
//ReadFromSDRAM(0x00,256); // <--| 将 SDRAM 中的数据写入 NAND Flash
// | 参数:(起始块,块数量)
/*
for(i=0; i<32; i++) // <--| 擦除连续的块
{ // | 更改循环变量可以改变要擦除的
Block_Erase(i); // | 起始块和块数量
}
*/
WriteToSDRAM(0x00,256); // <--| 将 NAND Flash 中的数据写入 SDRAM
// | 参数:(起始块,块数量)
BGRtoRGB(); // 调整 BGR 为 RGB
//CreatInvalidBlockTable(); // <--| 查找坏块,结果写入数组
// | NandInvalidTable[] ,数组中某元素值为 1
// | 则说明对应的块为坏块
delay(1000);
LedMode2(); // 用七段数码管指示程序结束运行
while(1);
}
////////////////////////////////////////////////////////
// 以下为基本函数
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// NAND Flash 复位
////////////////////////////////////////////////////////
void Reset_NAND(void)
{
Write_Command(0xff);
while(!Wait_NAND_RADY());
NAND_Finish();
}
////////////////////////////////////////////////////////
// 读取芯片 ID
////////////////////////////////////////////////////////
bool Read_Chip_ID(void)
{
Write_Command(0x90); // 写入命令 90h
Write_Address(0x00,0x00,0x00,0x01); // 写入地址 0x00
delay(1000);
Chip_ID0 = *pNAND_BaseaAddr; // 读取 NANDFlash 一个周期输出的值 ( 制造商代码)
delay(100);
Chip_ID1 = *pNAND_BaseaAddr; // 读取 NANDFlash 另一周期输出的值(设备代码)
delay(100);
NAND_Finish(); // 结束 NANDFlash 操作
if(Chip_ID0 != 0xec) // 验证制造商代码
{
return false;
}
else if(Chip_ID1 != 0x75) // 验证设备代码
{
return false;
}
else
{
return true;
}
}
////////////////////////////////////////////////////////
// 块擦除
////////////////////////////////////////////////////////
bool Block_Erase(unsigned short Block_Address)
{
unsigned char NAND_Data;
NAND_Address_Table.Value = (Block_Address << 13);
// 块地址作为参数传入函数,左 ( 向高位)移 13 位赋给共用体中的 Value ,这样实际是将地址赋给了 A13 ~ A23
Write_Command(0x60); // 写入命令 60h
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A9_A16, NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0, 0x02);
Write_Command(0xd0); // 写入命令 d0h
delay(100);
while(!Wait_NAND_RADY()); // 等待 NANDFlash 准备完毕
Write_Command(0x70); // 写入命令 70h (用于读状态寄存器)
delay(100);
NAND_Data = *pNAND_BaseaAddr; // 读取 NANDFlash 中状态寄存器的值
NAND_Finish();
if(NAND_Data != 0xc0)
// 验证读取的值是否为 C0 ,即 I/O0 为 0 表示成功擦除, I/O6 为 1 表示芯片忙状态结束
{
return false;
}
else
{
return true;
}
}
////////////////////////////////////////////////////////
// 页写入
////////////////////////////////////////////////////////
bool Page_Program(unsigned short Block_Address,unsigned short Page_Address)
{
int i;
unsigned char NAND_Data;
NAND_Address_Table.Value = (Block_Address << 13)|(Page_Address << 8);
// 块地址和页地址作为参数传入函数,又分别左移 13 位和 8 位赋给共用体中的 Value 这样实际是将地址赋给了 A0 ~ A24
Write_Command(0x80);
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A0_A7,
NAND_Address_Table.NAND_Address.NAND_Address_A9_A16,
NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0x03);
delay(100);
for(i = 0;i<512;i++) // 向一页中写入 512 字节
{
*pNAND_BaseaAddr = Test_Data_Table[i];
delay(100);
}
Write_Command(0x10);
delay(100);
while(!Wait_NAND_RADY());
delay(100);
Write_Command(0x70);
delay(100);
NAND_Data = *pNAND_BaseaAddr;
NAND_Finish();
if(NAND_Data != 0xc0)
{
return false;
}
else
{
return true;
}
}
////////////////////////////////////////////////////////
// 页读取
////////////////////////////////////////////////////////
bool Read_NANDFlash(unsigned short Block_Address,unsigned short Page_Address)
{
int i;
NAND_Address_Table.Value = (Block_Address << 13)|(Page_Address << 8);
Write_Command(0x00);
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A0_A7,
NAND_Address_Table.NAND_Address.NAND_Address_A9_A16,
NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0x03);
while(!Wait_NAND_RADY());
for(i = 0;i<512;i++) // 读取一页的 512 字节
{
Verify_Table[i] = *pNAND_BaseaAddr;
delay(100);
}
NAND_Finish();
return true;
}
////////////////////////////////////////////////////////
// 读取页高 16 字节
////////////////////////////////////////////////////////
bool Read_SpareField(unsigned short Block_Address,unsigned short Page_Address)
{
int i;
NAND_Address_Table.Value = (Block_Address << 13)|(Page_Address << 8);
Write_Command(0x50);
Write_Address(NAND_Address_Table.NAND_Address.NAND_Address_A0_A7,
NAND_Address_Table.NAND_Address.NAND_Address_A9_A16,
NAND_Address_Table.NAND_Address.NAND_Address_A17_A24,
0x03);
while(!Wait_NAND_RADY());
for(i = 0;i<16;i++)
{
SpareField[i] = *pNAND_BaseaAddr;
delay(100);
}
NAND_Finish();
return true;
}
////////////////////////////////////////////////////////
// 读取 SDRAM 中的数据,写入 NAND Flash
// 参数:(要读取的起始块地址,要读取的块的数量)
////////////////////////////////////////////////////////
bool ReadFromSDRAM(unsigned short NANDStartBlock,unsigned short NumOfBlock)
{
int block, page, k;
unsigned short Block_n_Address;
unsigned short Page_n_Address = 0;
unsigned char Column_n_Address = 0;
for(block=0; block<NumOfBlock; block++) // 一共要写入 NumOfBlock 块
{
Page_n_Address = 0;
Block_n_Address = block + NANDStartBlock;
if(!Block_Erase(Block_n_Address)) // 写之前先擦除块
//(BlockAddress)
{
while(1);
}
for(page=0; page<32; page++) // 每块 32 页
{
Page_n_Address = page;
for(k=0; k<256; k++) // 数据首先从 SDRAM 存入数组,每次存 512 字节,因选用 16 位寻址,格式上做了一些调整
{
Test_Data_Table[2*k+1] = *(pSdramStartAdr+k+(page*256)+(block*8192));
Test_Data_Table[2*k] = (*(pSdramStartAdr+k+(page*256)+(block*8192)))>>8;
}
if(!Page_Program(Block_n_Address,Page_n_Address)) // 将数组中的数据写入 NANDFlash
{
while(1);
}
}
}
return true;
}
////////////////////////////////////////////////////////
// 将 NAND Flash 中的数据写入 SDRAM
////////////////////////////////////////////////////////
bool WriteToSDRAM(unsigned short NANDStartBlock,unsigned short NumOfBlock)
{
int block, page, k;
unsigned short Block_n_Address;
unsigned short Page_n_Address = 0;
unsigned char Column_n_Address = 0;
unsigned short tempH = 0, tempL = 0;
for(block=0; block<NumOfBlock; block++) // 一共要读取 NumOfBlock 块
{
Page_n_Address = 0;
Block_n_Address = block + NANDStartBlock;
for(page=0; page<32; page++) // 每块 32 页
{
Page_n_Address = page;
if(!Read_NANDFlash(Block_n_Address,Page_n_Address)) // 从 NANDFlash 读出数据存入数组
//(BlockAddress,PageAddress)
{
while(1);
}
for(k=0; k<256; k++) // 将数组中的数据写入 SDRAM
{
tempL = Verify_Table[2*k+1];
tempH = Verify_Table[2*k];
*(pSdramStartAdr+k+(page*256)+(block*8192)) = tempH*0x100 + tempL;
}
}
}
return true;
}
////////////////////////////////////////////////////////
// 查找坏块
////////////////////////////////////////////////////////
void CreatInvalidBlockTable(void)
{
unsigned int i;
unsigned char InPage1, InPage2;
for(i=0 ;i<2048 ; i++)
{
Read_SpareField(i,1); // 读取第 i 块中的第 1 页中的高 16 字节
InPage1 = SpareField[5]; // 读取高 16 字节中的第 6 位
Read_SpareField(i,2); // 读取第 i 块中的第 2 页中的高 16 字节
InPage2 = SpareField[5]; // 读取高 16 字节中的第 6 位
if(InPage1 != 0xFF || InPage2 != 0xFF) // 若都为 FF 则说明是可用块,否则是坏块
{
NandInvalidTable[i] = 1; // 记录坏块
}
else
NandInvalidTable[i] = 0;
delay(100);
//NAND_Address_Table.Value += NandFLASHBlockIncrease;
delay(100);
}
delay(100);
}
////////////////////////////////////////////////////////
// 延时
////////////////////////////////////////////////////////
void delay(unsigned int DelayValue)
{
int i;
for(i=0;i<DelayValue;i++)
{
;
}
}
////////////////////////////////////////////////////////
//BGR 转换成 RGB
////////////////////////////////////////////////////////
void BGRtoRGB(void)
{
unsigned short data1, data2, data3;
unsigned char R1, G1, B1, R2, G2, B2;
int i;
for(i= 0; i<0x3F480; i+=3)
{
data1 = *(pSdramStartAdr + i);
data2 = *(pSdramStartAdr + i + 1);
data3 = *(pSdramStartAdr + i + 2);
B1 = data1;
G1 = data1 >> 8;
R1 = data2;
B2 = data2 >> 8;
G2 = data3;
R2 = data3 >> 8;
data1 = G1;
data1 = (data1 << 8) | R1;
data2 = R2;
data2 = (data2 << 8) | B1;
data3 = B2;
data3 = (data3 << 8) | G2;
*(pSdramStartAdr + i) = data1;
*(pSdramStartAdr + i + 1) = data2;
*(pSdramStartAdr + i + 2) = data3;
}
}
程序功能:
读取芯片 ID ;
读任意块的任意页的主数据区;
读任意块的任意页的附加数据区;
写任意块的任意页的主数据区;
坏块查找;
连续页的读取与存储;
将 NANDFlash 中的数据写入 SDRAM 中;
将 SDRAM 中的数据写入 NANDFlash 中;
将 SDRAM 中的 BGR 数据转换成 RGB 数据。