amazing030 发表于 2011-2-11 16:21:03

基于虚拟扇区的Flash存储管理技术

早上在公司整理办公室时翻出几本老杂志,看到一翻《基于虚拟扇区的Flash存储管理技术》,觉得很不错而且刚好有需要,于是认真看了看,一边编写了代码调试下。现在已经实现了所述功能,下面的代码是我将25X80改成256Byte的虚拟扇区使用,经测试OK,后期想再优化下并加入“内存“整理功能,也就是把不同块上的无效扇区移出并合并有效扇区。


#include "Cpu.h"
#include "flash.h"
#include "VisualSys.h"

#define IsFree(vat)   ((((vat) & VSS_MASK) == VSS_FREE)    ? 1 : 0)// 检查本STA单元属性是否未使用
#define IsValid(vat)    ((((vat) & VSS_MASK) == VSS_VALID)   ? 1 : 0)// 检查本STA单元属性是否有效
#define IsInvalid(vat)((((vat) & VSS_MASK) == VSS_INVALID) ? 1 : 0)// 检查本STA单元属性是否无效

word VSS_Table;          // 用于记录Flash中各个block的使用情况 某位为1表示相应sector未使用
byte BlockID, SatID;                //

// 擦除所占用Flash区域
void VSS_Format(void)
{
    byteblock;
   
    Flash_Enable(VSS_START_ADDR);
    for(block = 0; block < MAX_BLOCK; block++)
    {
      Flash_ErasePage(VSS_START_ADDR + (block << 14));   
      Cpu_Delay100US(50);      
    }
    Flash_Disable(VSS_START_ADDR);
}

// 对VSS管理系统参数进行初始化
void VSS_Init(void)
{
    byte i;

    memset(VSS_Table, 0x00, sizeof(VSS_Table));   
    for(i = 0; i < MAX_BLOCK; i++)
      Scan_SAT(i);    // 填充各块VSS_Table表
}

// 读取块号为blockID的SAT
word *ReadSat(byte blockID)
{
    dword addr;
    word psat;
   
    addr = VSS_START_ADDR + (blockID << 14) + SAT_OFFSET;
    Flash_Read(addr, (byte *)(&psat), SAT_SIZE);   
    return (word *)psat;
}

// 设置vss相应的SAT单元属性值std和VSS ID号
void SAT_SetState(byte vss, SAT_STD std)
{
    dword addr;
    wordsatval;
   
    //Find_VSS(vss);
    addr   = VSS_START_ADDR + (BlockID << 14) + SAT_OFFSET + SatID*2;
    satval = (word)((std << 14) | vss);       // SAT属性值及VSS ID号
   
    Flash_Enable(addr);
    Flash_Write(addr, (byte *)(&satval), 2);
    Flash_Disable(addr);   
}

// 整理块号为blockID的SAT 填充VSS_Table
void Scan_SAT(byte blockID)
{
    word *psat;
    word sat;   
    byte i;

    psat = ReadSat(blockID);      
    for(i = 0; i < MAX_SI; i++)
    {
      sat = *psat++;
      if(IsFree(sat))
            VSS_Table |= 1 << i;   
    }
}

// 查找VSS所在的BlockID及分割号SatID
byte Find_VSS(word vss)
{
    word *psat;
    word sat;
    byte i, j;
   
    for(i = 0; i < MAX_BLOCK; i++)
    {
      psat = ReadSat(i);
      for(j = 0; j < MAX_SI; j++)
      {
            sat = *psat++;
            if(IsValid(sat))   // 是否使能
            {
                if((sat & (~VSS_MASK)) == vss)   // VSS ID号是否相同
                {
                  BlockID = i;
                  SatID   = j;   
                  return 1;
                }
            }
      }
    }
    return 0;
}

// 从最低地址开始搜索空闲的VSS
byte Get_FreeVSS(void)
{
    word *psat;
    word sat;
    byte i, j;
   
    for(i = 0; i < MAX_BLOCK; i++)
    {
      psat = ReadSat(i);
      for(j = 0; j < MAX_SI; j++)
      {
            sat = *psat++;
            if(IsFree(sat))   // 是否使能
            {
                BlockID = i;
                SatID   = j;   
                return 1;
            }
      }
    }
    return 0;
}

// 获取VSS所在的物理地址
dword Get_VSSAddr(word vss)
{
    dword addr;
   
    if(Find_VSS(vss))
      addr = VSS_START_ADDR + BlockID*BLOCK_SIZE + SatID*SAT_SIZE;    // 对应VSS ID的物理地址
    else
      addr = VSS_START_ADDR;
    return addr;
}

void Read_VSS(word vss)
{
    dword addr;
   
    addr = Get_VSSAddr(vss);   
    ...
}
....
还找到了原始文章的pdf文件
点击此处下载 ourdev_615447IZPRV7.PDF(文件大小:159K) (原文件名:基于虚拟扇区的Flash存储管理技术.PDF)

amazing030 发表于 2011-2-11 16:22:54

差点忘了头文件的几个宏定义


#ifndef _VISUALSYS_H
#define _VISUALSYS_H

#define BLOCK_SIZE      4*1024      // 可擦除块大小为4K
#define SECTOR_SIZE   256         // 扇区大小 256Byte
#define MAX_BLOCK       32          // 可擦除块个数
#define MAX_SI          15          // 每个可擦除块中有效SI个数
#define SAT_SIZE      30          // 扇区分配表大小
#define VSS_MASK      0xC000      // VSS属性屏蔽值
#define VSS_FREE      0xC000      // VSS为未使用的属性值
#define VSS_VALID       0x4000      // VSS为有效的属性值
#define VSS_INVALID   0x0000      // VSS为无效的属性值

#define VSS_START_ADDR0x0000      // VSS起始地址(扇区)
#define SAT_OFFSET      0x0F00      // SAT表偏移地址 位于4K的最后256Byte

typedef enum
{
    VSS_Free    = 3,
    VSS_Valid   = 1,
    VSS_Invalid = 0      
}SAT_STD;

extern word VSS_Table;          // 用于记录Flash中各个block的使用情况 某位为1表示相应sector未使用
extern byte BlockID, SatID;                //


extern void VSS_Format(void);
extern void VSS_Init(void);
extern void Scan_SAT(byte blockID);
extern byte Find_VSS(word vss);
extern byte Get_FreeVSS(void);
extern word *ReadSat(byte blockID);
extern void SAT_SetState(byte vss, SAT_STD std);

#endif

aahui 发表于 2011-2-11 16:29:27

好东西,顶一下

amazing030 发表于 2011-2-11 16:31:23

几个基本函数我写成和文章中描述的一样,方便理解,另外如加入了查找空闲扇区的函数Get_FreeVSS()等

索性把测试代码也贴出来

#include "Cpu.h"
#include "Uart.h"
#include "Flash.h"
#include "VisualSys.h"
#include "protocol.h"
#include "ployupd.h"

str_ploymode PloyMode;

// 下载策略
void Ploy_Download(void)   
{
    word len;
    byte vss;
   
    len = (word)strlen(Uart_InpBuffer);
    vss = (byte)(Uart_InpBuffer - DATA_OFFSET);
    Add_Ploy(vss, Uart_InpBuffer, len);
    memset(Uart_InpBuffer, 0x00, sizeof(Uart_InpBuffer));
    Protocol_SendAck();         // 操作完成 返回确认字符   
}

// 往vss虚拟扇区中写入策略
void Add_Ploy(byte vss, byte *buf, word len)
{
    dword addr;
    //byte buf;

    if(Find_VSS(vss))       // 如果VSS记录号已使用则禁用之前的SAT并重新生成VSS
    {                     // 同时获得块号BlockID和分割号SatID
      SAT_SetState(vss, VSS_Invalid);   // 禁用之前SAT
      VSS_Table &= ~(1 << SatID);   // 更新VSS_Table表
      if(Get_FreeVSS())   // 重新申请空闲VSS
      {
            addr = VSS_START_ADDR + BlockID*BLOCK_SIZE + SatID*SAT_SIZE;    // 对应VSS ID的物理地址
            //Flash_Enable(addr);
            //Flash_Write(addr, buf, len);
            //Flash_Disable(addr);
            SAT_SetState(vss, VSS_Valid);   
            VSS_Table &= ~(1 << SatID);   // 更新VSS_Table表
      }            
    }
    else                  // 如果未使用
    {
      if(Get_FreeVSS())   // 申请到空闲VSS并获得块号BlockID和分割号SatID
      {
            addr = VSS_START_ADDR + BlockID*BLOCK_SIZE + SatID*SAT_SIZE;    // 对应VSS ID的物理地址
            //Flash_Enable(addr);
            //Flash_Write(addr, buf, len);
            //Flash_Disable(addr);
            SAT_SetState(vss, VSS_Valid);   
            VSS_Table &= ~(1 << SatID);   // 更新VSS_Table表
      }      
    }
}

void Read_Ploy(byte vss)
{
    vss = vss;
   
}

void Ploy_Adjust(void)
{
   
}



使用 void Add_Ploy(byte vss, byte *buf, word len) 函数往指定虚拟扇区中写入数据,当然,一次最多写256个字节,不足256也占用一扇区。
像main()...什么的就别找我要了,Flash的驱动也....上面才是关键代码

aahui 发表于 2011-2-11 16:50:57

楼主, 你那个仿nano转盘按键的MP3做了没有 ?

amazing030 发表于 2011-2-11 16:56:06

./emotion/em010.gif没啊,那些座子别人卖我10块钱一个,屏也要一次买几百个才行....
那转盘所用芯片是SPI接口的,用分析仪抓下来应该能把各动作指令解析出来的,只是那种操作手感就...

amazing030 发表于 2011-2-11 16:58:35

你做MP3就用五维的开关不错

amazing030 发表于 2011-2-14 11:34:54

内存整理程序也写好了,调试时检测无误。我的方法是划一块4K的空间作为缓冲区,然后从低到高按地址检测VSS属性值,有效则压入缓冲区中,缓冲区满则格式化已检测完的区域(多种情况)。有几个地方有些冗余,像缓冲区的SAT属性可不写,而且上面的程序为和文档中匹配,这时有几个变量值就传递有些麻烦。加个进度条显示整理速度,再加个饼图还能显示已用和剩余空间大小及碎片(无效VSS)大小,嘿嘿,想想都不错。

// 读取块号为Mem缓冲区的SAT
word *Read_MemSAT(void)
{
    dword addr;
    word psat;
   
    addr = MEMERY_ADDR + SAT_OFFSET;
    Flash_Read(addr, (byte *)(&psat), SAT_SIZE);   
    return (word *)psat;
}

// 读取缓冲区分割号SatID缓冲区只有一个块大小
byte Get_MemVSS(void)
{
    word *psat;
    word sat;
    byte i;
   
    psat = Read_MemSAT();
    for(i = 0; i < MAX_SI; i++)
    {
      sat = *psat++;
      if(IsFree(sat))   // 是否使能
            return i;
    }
}

// 设置vss相应的SAT单元属性值std和VSS ID号
void MEM_SetSATStd(byte vss, byte sat, SAT_STD std)
{
    dword addr;
    wordsatval;
   
    addr   = VSS_START_ADDR + SAT_OFFSET + sat*2;
    satval = (word)((std << 14) | vss);       // SAT属性值及VSS ID号
   
    Flash_Enable(addr);
    Flash_Write(addr, (byte *)(&satval), 2);
    Flash_Disable(addr);   
}

// 内存整理函数
void Memery_Releaze(void)
{
    bytebuf;//
    dword addr;
    word*psat;
    wordsat;
    bytei, j;
    bytevss;
    bytememsat;
    bytememvss;
    bytecnt   = 0;    // 转入缓冲区的VSS数
    byterenew = 0;    // 已更新的块数
   
    Flash_Enable(MEMERY_ADDR);
    Flash_ErasePage(MEMERY_ADDR);
    Flash_Disable(MEMERY_ADDR);
    for(i = 0; i < MAX_BLOCK; i++)
    {
      Cpu_Delay100US(800);
      psat = ReadSat(i);
      for(j = 0; j < MAX_SI; j++)
      {
            sat = *psat++;
            if(IsValid(sat))   // 是否有效
            {
                vss = (byte)(sat & (~VSS_MASK));
                memsat = Get_MemVSS();

                addr = Get_VSSAddr(vss);
                memset(buf, 0xFF, sizeof(buf));
                Flash_Read(addr, buf, SECTOR_SIZE);            
                addr = MEMERY_ADDR + memsat*SECTOR_SIZE;      // 对应VSS ID的物理地址                  
                //Flash_Enable(addr);
                //Flash_Write(addr, buf, SECTOR_SIZE);
                //Flash_Disable(addr);
                MEM_SetSATStd(vss, memsat, VSS_Valid);          // 更改缓冲区中属性
                Cpu_Delay100US(100);
                SAT_SetState(vss, VSS_Invalid);               // 同时修改原SAT属性为无效
                Cpu_Delay100US(100);               
                if(++cnt == MAX_SI)                           // 如果缓冲区满则检测各块VSS使用情况
                {
                  cnt = 0;
                  addr = VSS_START_ADDR + (renew << 14);      // 格式化已检测完的块并重新写入整理后VSS数据
                  Flash_Enable(addr);
                  Flash_ErasePage(addr);   
                  Flash_Disable(addr);                                       
                  for(memvss = 0; memvss < MAX_SI; memvss++)// 将缓冲区中VSS更新至更新块中
                  {
                        addr = MEMERY_ADDR + memvss*SAT_SIZE;   // 对应VSS ID的物理地址   
                        Flash_Read(addr, buf, SECTOR_SIZE);                                                   
                        addr = VSS_START_ADDR + (renew << 14) + memvss*SECTOR_SIZE;
                        Flash_Enable(addr);                     // 数据写入更新块中
                        Flash_Write(addr, buf, SAT_SIZE);
                        Flash_Disable(addr);         
                        Cpu_Delay100US(100);
                        
                        BlockID = renew;
                        SatID   = memvss;
                        SAT_SetState(vss, VSS_Valid);         // 修改SAT属性为有效                     
                  }
                  renew++;      // 更新块数+1
                }
            }            
      }
    }
    if(cnt != 0)   // 不满一整块的VSS整理后写入
    {
      addr = VSS_START_ADDR + (renew << 14);      // 格式化已检测完的块并重新写入整理后VSS数据
      Flash_Enable(addr);
      Flash_ErasePage(addr);   
      Flash_Disable(addr);                           
      for(memvss = 0; memvss < cnt; memvss++)// 将缓冲区中VSS更新至更新块中
      {
            addr = MEMERY_ADDR + memvss*SAT_SIZE;   // 对应VSS ID的物理地址   
            Flash_Read(addr, buf, SECTOR_SIZE);                                                   
            addr = VSS_START_ADDR + (renew << 14) + memvss*SECTOR_SIZE;
            Flash_Enable(addr);                     // 数据写入更新块中
            Flash_Write(addr, buf, SAT_SIZE);
            Flash_Disable(addr);         
            Cpu_Delay100US(100);   
            
            BlockID = renew;
            SatID   = memvss;            
            SAT_SetState(vss, VSS_Valid);         // 修改SAT属性为有效                        
      }      
    }
    for(i = 0; i < MAX_BLOCK; i++)
    {
      cnt = 0;                  // 作为无效VSS计数
      Cpu_Delay100US(800);
      EnterCritical();
      psat = ReadSat(i);
      ExitCritical();
      for(j = 0; j < MAX_SI; j++)
      {
            sat = *psat++;
            if(IsInvalid(sat))      // 是否无效
                cnt++;         
      }
      if(cnt == MAX_SI)         // 如果该块全为无效则格式化该扇区
      {                           // 也可以设定成本块内无效数大于某一值就格式化 此时有效VSS都已压入缓冲区
                                    // 而且reney始终小于等于当前检测块号 不担心误删有效数据         
            addr = VSS_START_ADDR + (i << 14);
            Flash_Enable(addr);
            Flash_ErasePage(addr);   
            Flash_Disable(addr);            
      }
    }      
    memset(VSS_Table, 0x00, sizeof(VSS_Table));   // 更新VSS_Table表      
    for(i = 0; i < MAX_BLOCK; i++)
      Scan_SAT(i);   
    //VSS_Table &= ~(1 << SatID);          // 更新VSS_Table表
}

ufbycd 发表于 2011-2-14 12:38:55

mark 正想用

wenxusun 发表于 2011-2-14 12:51:05

mark

yufan 发表于 2011-2-16 20:41:00

mark

MYMCU 发表于 2011-2-16 21:37:39

mark

380121850 发表于 2011-2-16 22:59:13

最近也正在想写一份FLASH管理,也就是把FLASH的“不平坦”转换成“平坦”模式,思想跟你这有点像,呵呵

ERDTxiduoduo 发表于 2011-2-17 01:22:09

mark

wuyiduan 发表于 2011-2-26 15:16:37

mark

hdd961140543 发表于 2011-6-24 18:57:40

mark

lixupeng 发表于 2011-6-24 22:47:31

mark!

机器人天空 发表于 2014-10-16 19:20:33

很不错,谢谢楼主
页: [1]
查看完整版本: 基于虚拟扇区的Flash存储管理技术