分类: 嵌入式
2015-12-10 23:51:18
原文地址:NOR-FLASH驱动文档(SST39VF1601) 作者:
SST39VF160中文datasheet.pdf SST39VF1601.pdf SST39VF160x Driver.txt
NOR-FLASH是最早出现的Flash Memory,目前仍是多数供应商支持的技术架构. NOR-FLASH在擦除和编程操作较少而直接执行代码的场合,尤其是纯代码存储的应用中广泛使用,但是由于NOR-FLASH只支持块擦除,其擦除和编程速度较慢,而块尺寸又较大,导致擦除和编程操作所花费的时间很长,所以在纯数据存储和文件存储的应用中显得力不从心.
NOR-FLASH的特点是:
1. 程序和数据可存放在同一芯片上,FLASH芯片拥有独立的数据总线和地址总线,能快速随机读取,并且允许系统直接从Flash中读取代码执行,而无需先将代码下载至RAM中再执行;
2. 可以单字节或单字读取,但不能单字节擦除,必须以部分或块为单位或对整片执行擦除操作,在执行写操作之前,必需先根据需要对部分,块或整片进行擦除,然后才能写入数据。
以SST系列NOR-FLASH芯片为例介绍FLASH的使用方法及驱动.
首先,在驱动的头文件中,要根据芯片的具体情况和项目的要求作如下定义:
1. 定义操作的单位,如
typedef unsigned char BYTE; // BYTE is 8-bit in length
typedef unsigned short int WORD; // WORD is 16-bit in length
typedef unsigned long int Uint32; // Uint32 is 32-bit in length
在这里地址多是32位的,芯片写操作的最小数据单位为WORD,定义为16位,芯片读操作的最小数据单位是BYTE,定义为8位.
2. 因为芯片分为16位和32位的,所以对芯片的命令操作也分为16位操作和32位操作(命令操作在介绍具体的读写过程中将详细介绍).
#ifdef GE01
/*宏NorFlash_32Bit,若定义了为32位NorFlash,否则为16位NorFlash*/
#define NorFlash_32Bit
#endif
3. 根据芯片的情况,定义部分(段)和块的大小.
#define SECTOR_SIZE 2048 // Must be 2048 words for 39VF160X
#define BLOCK_SIZE 32768 // Must be 32K words for 39VF160X
/*其中words根据上述定义,指的是16位的数据*/
其余的参数可以不用改。
下面来看具体的操作。
1. 读操作
NOR-FLASH可以随机读取数据,速度快,可以在FLASH的任意地址读取且读数据的最小单位控制在8位。
如下是驱动中的读操作,该段代码的作用是从Flash中偏移地址为Dst地址处读取一个长为length的数据到源地址 SrcWord地址处(length的单位是字节):
void NorFlash_Read(U8 *SrcWord, Uint32 Dst, int length)
{
Uint32 DestBuf = Dst;
U8 *SourceBuf = SrcWord;
int Index;
for(Index = 0; Index < length; Index++)
{
ReadOneWord(SourceBuf, DestBuf); //读一个字节的函数
SourceBuf++;
DestBuf++;
}
}
void ReadOneWord(U8 *SrcWord, Uint32 Dst)
{
Uint32 DestBuf = Dst;
U8 *SourceBuf = (U8 *)SrcWord;
*SourceBuf = *(volatile U8*)(system_base|DestBuf); //system_base是FLASH的基址,
在驱动中可以自行定义,另外
读操作不需要发送命令,直接
读取即可
}
用法:若需从FLASH的地址为0x20001000处读一段10个字节的数据至SDRAM的地址0x30001000中,代码应写成:NorFlash_Read((U8 *)0x30001000, 0x1000, 10); //FLASH的基址是0x20000000,所以偏移地址只需写成0x1000即可。
2. 擦除操作
擦除操作分为3种,一是部分擦除,即擦除一个sector大小的空间,二是块擦除,三是整片擦除。
部分擦除的函数,除了读操作,其余对FLASH的操作都必须向FLASH发送相应的命令。而具体发送什么样的命令,由FLASH芯片的型号决定,用时请查阅SST39VF1601芯片说明书
int EraseOneSector(Uint32 Dst)
{
Uint32 DestBuf = Dst;
int ReturnStatus;
// 39VF160X系列FLASH的命令,对照下表来写命令
*sysAddress(0x5555) = 0x00AA; // write data 0x00AA to device addr 0x5555
*sysAddress(0x2AAA) = 0x0055; // write data 0x0055 to device addr 0x2AAA
*sysAddress(0x5555) = 0x0080; // write data 0x0080 to device addr 0x5555
*sysAddress(0x5555) = 0x00AA; // write data 0x00AA to device addr 0x5555
*sysAddress(0x2AAA) = 0x0055; // write data 0x0055 to device addr 0x2AAA
*sysAddress(DestBuf) = 0x0030; // write data 0x0030 to device sector addr
ReturnStatus = CheckToggleReady(DestBuf); //检查Flash中偏移为DstBuf地址处是否正在进行写或者擦除操作。 返回1:目的地址处不在进行写和擦除操作;0:目的地址处正在进行写或擦除操作
return ReturnStatus;
}
对应的中文
擦除一个块和擦除整片的驱动代码与上述类似,但是命令有所区别,可根据上表Block-Erase和Chip-Erase的命令修改。擦除操作具体的应用在写操作中可以看到。
3. 写操作
写操作的速度较慢,不要频繁使用
int NorFlash_Write(WORD *Src, U32 Dst, int length) //将源地址 Src地址处将一个长为length的数据写到Flash中偏移地址为Dst地址处,length的单位是字节
{
WORD *SourceBuf;
Uint32 DestBuf;
int Index, ReturnStatus;
WORD tempBuf[SECTOR_SIZE];
SourceBuf = Src;
DestBuf = Dst;
length = (length + 1) / 2; //字节数转半字数
//在写数据之前,需要先擦除。所以先要计算出要写的数据占多少块零多少个部分然后分别将其擦除
Index = length / BLOCK_SIZE; //计算所占块的个数,不到一块的就不算
for(Index = Index - 1; Index >= 0; Index--)
{
//将目的地址变为偏移地址
DestBuf = (DestBuf + Index * BLOCK_SIZE * 2) - system_base;
//因为该FLASH以半字为单位,所以将偏移地址DestBuf除以2。例如一个块大小为0x8000,单位为16位的word,实际占用的存储空间为0x10000。存储地址是以8位字节为单位的,而函数EraseOneBlock(Dest)是以word为单位的。擦除块时Dest是块的首地址的偏移地址,擦除第二块时,首地址的偏移地址为0x10000,而对于EraseOneBlock(Dest)来说,第二块的首地址应是0x8000。所以需对偏移地址作移位处理,左移一位。
ReturnStatus = EraseOneBlock(DestBuf >> 1);
if (!ReturnStatus)
return ReturnStatus;
}
Index = (length % BLOCK_SIZE) / SECTOR_SIZE; //计算剩下未擦除的数据占用多少个部分,不足一部分的按照一部分计算
DestBuf = Dst;
for(Index; Index >= 0; Index--)
{
DestBuf = (DestBuf + (length / BLOCK_SIZE) * BLOCK_SIZE * 2 + Index *
SECTOR_SIZE * 2) - system_base; //将目的地址变为偏移地址
ReturnStatus = EraseOneSector(DestBuf >> 1);
if (!ReturnStatus)
return ReturnStatus;
}
for (Index = 0; Index < length; Index++) //全部擦除完以后开始写数据
{
ReturnStatus = ProgramOneWord( SourceBuf, DestBuf); //写一个字
DestBuf = DestBuf + 2;
++SourceBuf;
if (!ReturnStatus)
return ReturnStatus;
}
return ReturnStatus; //若擦除和写操作其中的任意一个出错,返回的状态就为0
}
int ProgramOneWord (WORD *SrcWord, Uint32 Dst) //从源地址SrcWord地址处取一个WORD(16位)写到Flash中偏移地址为Dst地址处
{
Uint32 DestBuf = Dst;
WORD *SourceBuf = SrcWord;
int ReturnStatus;
//写一个word的命令见上述命令表的word-program
*sysAddress(0x5555) = 0xAAAA; // write data 0x00AA to device addr 0x5555
*sysAddress(0x2AAA) = 0x5555; // write data 0x0055 to device addr 0x2AAA
*sysAddress(0x5555) = 0xA0A0; // write data 0x00A0 to device addr 0x5555
*(volatile U16*)(system_base|DestBuf) = *SourceBuf; // 把起始地址是SourceBuf的数据写到FLASH的地址
(system_base|DestBuf)中
ReturnStatus = CheckToggleReady(DestBuf);
return ReturnStatus;
}
用法:若需将SDRAM的地址为0x30000000处的一段10个字节的数据写至FLASH的地址0x20001000中,代码应写成:NorFlash_Write((U8 *)0x30000000, 0x1000, 10);
写操作需要注意的是:源数据地址一定是偶数,否则会出错,Flash中的目的地址也一定为偶数(否则会出现第一个字节丢失的错误)。