搜索
bottom↓
回复: 17

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

[复制链接]

出0入0汤圆

发表于 2011-2-11 16:21:03 | 显示全部楼层 |阅读模式
早上在公司整理办公室时翻出几本老杂志,看到一翻《基于虚拟扇区的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[MAX_BLOCK];          // 用于记录Flash中各个block的使用情况 某位为1表示相应sector未使用
byte BlockID, SatID;                //

// 擦除所占用Flash区域
void VSS_Format(void)
{
    byte  block;
   
    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[MAX_SI];
   
    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;
    word  satval;
   
    //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[blockID] |= 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)

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

 楼主| 发表于 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_ADDR  0x0000      // 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[MAX_BLOCK];          // 用于记录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

出0入0汤圆

发表于 2011-2-11 16:29:27 | 显示全部楼层
好东西,顶一下

出0入0汤圆

 楼主| 发表于 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[3] - 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[256];

    if(Find_VSS(vss))       // 如果VSS记录号已使用则禁用之前的SAT并重新生成VSS
    {                       // 同时获得块号BlockID和分割号SatID
        SAT_SetState(vss, VSS_Invalid);     // 禁用之前SAT
        VSS_Table[BlockID] &= ~(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[BlockID] &= ~(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[BlockID] &= ~(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的驱动也....上面才是关键代码

出0入0汤圆

发表于 2011-2-11 16:50:57 | 显示全部楼层
楼主, 你那个仿nano转盘按键的MP3做了没有 ?

出0入0汤圆

 楼主| 发表于 2011-2-11 16:56:06 | 显示全部楼层
没啊,那些座子别人卖我10块钱一个,屏也要一次买几百个才行....
那转盘所用芯片是SPI接口的,用分析仪抓下来应该能把各动作指令解析出来的,只是那种操作手感就...

出0入0汤圆

 楼主| 发表于 2011-2-11 16:58:35 | 显示全部楼层
你做MP3就用五维的开关不错

出0入0汤圆

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

// 读取块号为Mem缓冲区的SAT
word *Read_MemSAT(void)
{
    dword addr;
    word psat[MAX_SI];
   
    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;
    word  satval;
   
    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)
{
    byte  buf[SECTOR_SIZE];  //
    dword addr;
    word  *psat;
    word  sat;
    byte  i, j;
    byte  vss;
    byte  memsat;
    byte  memvss;
    byte  cnt   = 0;    // 转入缓冲区的VSS数
    byte  renew = 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[BlockID] &= ~(1 << SatID);          // 更新VSS_Table表  
}

出0入0汤圆

发表于 2011-2-14 12:38:55 | 显示全部楼层
mark 正想用

出0入0汤圆

发表于 2011-2-14 12:51:05 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-16 20:41:00 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-16 21:37:39 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-16 22:59:13 | 显示全部楼层
最近也正在想写一份FLASH管理,也就是把FLASH的“不平坦”转换成“平坦”模式,思想跟你这有点像,呵呵

出0入0汤圆

发表于 2011-2-17 01:22:09 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-2-26 15:16:37 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-6-24 18:57:40 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-6-24 22:47:31 | 显示全部楼层
mark!

出0入0汤圆

发表于 2014-10-16 19:20:33 | 显示全部楼层
很不错,谢谢楼主
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 00:51

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表