基于LPC2400系列ARM的NOR FALSH (SST39VF6401B)的文件系统的设计

ARM 113浏览

基于LPC2400系列ARMNOR FALSH SST39VF6401B)的文件系统的设计

 

   LPC2400系列的ARM外部存储器控制器(EMC),支持异步静态存储器设备(如RAMROMFLASH)和动态存储器(如SDRAM)。支持8位,16位,32位宽的静态存储器,4个独立的静态存储器片选,CSn0-3

   本文主要讲的是16位宽,片选信号CSn0的的静态存储器NOR FLASHLPC2400系列的ARM操作16位存储器时,就需要LPC2400系列的ARM的地址线A1连接到存储器的地址A0处,固文件系统的头文件里NOR FLASH的地址定义为:

#define  FLASH_ADDR        0x80000000                  //CS0

// 转换地址。将要发送给SST39VF640的地址值进行转换,以便于LPC2478输出。

// 由于SST39VF640A0是与LPC2478A1相连,所以addr要左移1位。

#define  GetAddr(addr)    (volatile uint16  *)(FLASH_ADDR|(addr<<1))

 startup.s文件里,初始化静态存储器配置寄存器(EMCStaticConfig0-3),相应位如下:

3121

20

19

189

8

7

6

54

3

2

1

保留

写保护(P

缓冲区使能(B

保留

延长等待(EW

字节定位状态(PB

片选极性(PC

保留

页模式(PM

保留

存储器宽度(MW

 

0:写操作不被保护

1:写操作被保护

0:写缓冲区禁能

1:写缓冲区使能

 

0:禁止延长

1:使能延长

使能连接到不同类型的存储器

0:对于8位字节宽的静态存储器

116/32字节宽的静态存储器

0:片选被驱动为低电平有效

1:片选被驱动为高电平有效

 

0:禁能

1:异步页模式使能

 

008

0116

1032

11:保留

WAITWEN0           EQU         0x00   ;/* 配置EMCStaticWaitWen0       */

WAITOEN0            EQU         0x01     ;/* 配置EMCStaticWaitOen0       */

WAITRD0             EQU         0x04     ;/* 配置EMCStaticWaitRd0        */

WAITPAGE0           EQU         0x1f  ;/* 配置EMCStaticWaitPage0      */

WAITWR0             EQU         0x02    ;/* 配置EMCStaticWaitWr0        */

WAITTURN0           EQU         0x0f   ;/* 配置EMCStaticWaitTurn0      */

。。。。。。。。。。。。。。。。。。。。

STATICCFG0             EQU    0x00000081

。。。。。。。。。。。。。。。。。。。。。

LDR     R1, =STATICCFG0         ;/* 配置EMCStaticConfig0        */

        LDR     R2, =WAITWEN0    ;/* 配置EMCStaticWaitWen0       */

        LDR     R2, =WAITWEN0    ;/* 配置EMCStaticWaitWen0       */

        LDR     R3, =WAITOEN0     ;/* 配置EMCStaticWaitOen0       */

        LDR     R4, =WAITRD0       ;/* 配置EMCStaticWaitRd0        */

        LDR     R5, =WAITPAGE0   ;/* 配置EMCStaticWaitPage0      */

        LDR     R6, =WAITWR0        ;/* 配置EMCStaticWaitWr0       */          

        LDR     R7, =WAITTURN0   ;/* 配置EMCStaticWaitTurn0      */

        LDR           R0, =EMCStaticConfig0  

        STMIA   R0!, {R1-R7} 

。。。。。。。。。。。。。。。。。。。。。。。。

BL      TargetResetInit        ;/*  目标板基本初始化 

EMCStaticConfig0  = 0x00000081;//EMC设置为16

  

 void  TargetResetInit(void)  /* 使能外部存储器总线连接 */

{        

    PINSEL6   |= 0x55555555;  // 外部存储器数据使能

    PINSEL8   |= 0x55555555;  //外部存储器地址线低16位使能

    PINSEL9   |= 0x50555555;     //外部存储器地址线高8位使能

      EMCControl = 0x00000001;   //使能EMC,正常存储器映射

}

驱动程序相关函数:

参照SST39VF6401B的文档填写读写驱动周期和地址

 /****************************************************************************

* 名称:WordProgram()

* 功能:半字(16)数据编程。

* 入口参数:Addr              编程地址(SST39VF160内部地址)     

*          Data         编程数据

* 出口参数:返回TRUE表示操作成功,返回FALSE表示操作失败

****************************************************************************/

static uint8  WordProgram(uint32 Addr, uint16 Data)

{  volatile uint16  *ip;

   uint16  temp1,temp2,dat;

   dat = Data;

   ip = GetAddr(0x5555);    // 转换地址0x5555

   ip[0] = 0xaaaa;                // 第一个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第二个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(0x5555);

   ip[0] = 0xa0a0;               // 第三个写周期,地址0x5555,数据0xA0

   ip = GetAddr(Addr);

   *ip = dat;                       // 第四个写周期,地址Addr,数据Data

   for(temp1=0;temp1<2;temp1++);

   while(1)          

  {         // 等待操作完成 (若编程操作没有完成,每次读操作DQ6会跳变)

   temp1 = *ip;                  

   temp2 = *ip;

   if (temp1 == temp2)

    {

    if (temp1 != dat)

        return(FALSE);

    else

        return(TRUE);

       }

  }

}

 

/****************************************************************************

* 名称:ChipErase()

* 功能:芯片全片擦除。

* 入口参数:无

* 出口参数:返回TRUE表示操作成功,返回FALSE表示操作失败

****************************************************************************/

static uint8  ChipErase(void)

{  volatile uint16  *ip;

   uint16  temp1,temp2;

 

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第一个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第二个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(0x5555);

   ip[0] = 0x8080;               // 第三个写周期,地址0x5555,数据0x80

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第四个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第五个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(0x5555);

   ip[0] = 0x1010;               // 第六个写周期,地址0x5555,数据0x10

   for(temp1=0;temp1<6;temp1++);

   while(1)                 

     {     // 等待操作完成 (若擦除操作没有完成,每次读操作DQ6会跳变)

   temp1 = *ip;

   temp2 = *ip;

   if (temp1 == temp2)

    {

      if (temp1 != 0xffff)

         return(FALSE);

      else

         return(TRUE);

         }

       }

}    

 

/****************************************************************************

* 名称:BAX_ChipErase()

* 功能:芯片块擦除。

* 入口参数:Bax

* 出口参数:返回TRUE表示操作成功,返回FALSE表示操作失败

****************************************************************************/

uint8  Bax_ChipErase(uint32 bax)

{  volatile uint16  *ip;

   uint16  temp1,temp2;

 

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第一个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第二个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(0x5555);

   ip[0] = 0x8080;               // 第三个写周期,地址0x5555,数据0x80

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第四个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第五个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(bax);

   //ip[0] = 0x5050;                    // 第六个写周期,地址0x5555,数据0x10

   ip[0] = 0x3030;

   for(temp1=0;temp1<6;temp1++);

   while(1)                 

     {     // 等待操作完成 (若擦除操作没有完成,每次读操作DQ6会跳变)

   temp1 = *ip;

   temp2 = *ip;

   if (temp1 == temp2)

     {

      if (temp1 != 0xffff)

         return(FALSE);

      else

         return(TRUE);

         }

       }

}

/****************************************************************************

* 名称:SAX_ChipErase()

* 功能:芯片扇区擦除。

* 入口参数:sax

* 出口参数:返回TRUE表示操作成功,返回FALSE表示操作失败

****************************************************************************/

static uint8  Sax_ChipErase(uint32 sax)

{  volatile uint16  *ip;

   uint16  temp1,temp2;

 

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第一个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第二个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(0x5555);

   ip[0] = 0x8080;               // 第三个写周期,地址0x5555,数据0x80

   ip = GetAddr(0x5555);

   ip[0] = 0xaaaa;                // 第四个写周期,地址0x5555,数据0xAA

   ip = GetAddr(0x2aaa);

   ip[0] = 0x5555;               // 第五个写周期,地址0x2aaa,数据0x55

   ip = GetAddr(sax);

  // ip[0] = 0x3030;                    // 第六个写周期,地址0x5555,数据0x10

   ip[0] = 0x5050;

   for(temp1=0;temp1<6;temp1++);

   while(1)                 

     {     // 等待操作完成 (若擦除操作没有完成,每次读操作DQ6会跳变)

   temp1 = *ip;

   temp2 = *ip;

   if (temp1 == temp2)

    {

      if (temp1 != 0xffff)

         return(FALSE);

      else

         return(TRUE);

         }

       }

}

在通过测试后,接着就是做NOR FLASH的简易的文件系统(既FAT系统)。简易的文件系统包括以下几个部分:(本文NOR FLASH 8M,分2048

一.FAT分配表设计(4K空间,1簇)

1)FAT分配表文件分配表存放文件所占用的存储空间簇链以及Flash存储器的占用和空闲空间的情况,非常重要。系统对FAT表的访问原理如下:访问文件时先从要目录中找到该文件的目录项,从中读出首簇号。然后,目录中找到该文件的目录项,从中读出首簇号。然后在FAT中找到从该首簇号开始的簇链,簇链上的簇号即为文件在逻辑扇区中占用的扇区号链,这样便可以进行数据读写了。

二.FDT分配表设计(4K空间,0簇)

本文将从FAT表之前的4K存储空间为根目录区FDT表,可以保存128个目录项。每个目录项记录了该文件的文件名、文件属性、文件大小、读写FDT表指针偏移量以及文件在数据区中所占的首簇号,即该文件在FAT表中的入口等数据。其头文件里结构体定义:

// flash目录表数据结构

typedef struct _FLASH_FDT

{

    uint16  Name[11];           //短文件名主文件名

    uint16  FstClus;            //文件首簇号

    uint32  FileSize;           //文件大小

    uint16  FileNo;             //文件编号

    uint16  Flg;                //一些标记

    uint16  FileTotalClus;      //每个文件占用簇数 

    uint16  Offset;         // 读写FDT表指针偏移量

} FLASHFDT;

 三.数据区设计(2-2048簇)

2)数据区存放文件的数据内容。文件系统对数据区的存储空间是按簇进行划分和管理的。

 头文件:

FlashFileSystem.h

/ 磁盘信息

#define f_DiskSize       0x800000                    //flash的尺寸大小单位为B

#define f_CLUSSize     4096 >> 1                            //簇的大小单位为B

#define f_NumsFAT           1                                         //fat表数目    文件分配表

#define f_NumsFDT          1                                         //fdt表数目    文件目录表

#define f_CLUSPerDisk   2048                                    //flash盘包括的簇数量 f_DiskSize / (f_CLUSSize  <<1) 计算得到

#define f_DataStartCLUS 2                                          //数据区开始的簇号

// 函数返回值                                                             

#define RTN_OK                      0x00    // 操作成功       

#define RTN_FAILED                  0xFF    /// 操作成功        

#define NOT_FIND_DISK               0x01    /// 逻辑盘不存在    

#define fDISK_FULL                  0xFFF7  //   逻辑盘满        

#define NOT_FIND_FDT                0x07    /// 没有发现文件(目录)

#define FDT_FULL                                   0xFFF8  //   FDT表满         

#define FAT_FULL                             0x09    /// FAT表满         

#define END_CLUS                                   0xFFFF 

#define fBAD_CLUS                                  0xFFF9 

#define  FLASH_ADDR        0x80000000                  //CS0

// 转换地址。将要发送给SST39VF640的地址值进行转换,以便于LPC2478输出。

// 由于SST39VF640A0是与LPC2478A1相连,所以addr要左移1位。

#define  GetAddr(addr)    (volatile uint16  *)(FLASH_ADDR|(addr<<1))

 

/*接口函数*/

uint8 f_DiskInit(void); //对磁盘进行格式化写入

uint16 FileWriteToFlash(char *p,uint32 Size,char FileName[]);//将文件写入FLASH

uint32 FileReadFromFlash(char *buf,uint16 NO); //将文件读出FLASH

uint8  FlashFileDel(uint16 NO);//删除FLASH文件