基于虚拟扇区的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) 差点忘了头文件的几个宏定义
#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 好东西,顶一下 几个基本函数我写成和文章中描述的一样,方便理解,另外如加入了查找空闲扇区的函数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的驱动也....上面才是关键代码 楼主, 你那个仿nano转盘按键的MP3做了没有 ? ./emotion/em010.gif没啊,那些座子别人卖我10块钱一个,屏也要一次买几百个才行....
那转盘所用芯片是SPI接口的,用分析仪抓下来应该能把各动作指令解析出来的,只是那种操作手感就... 你做MP3就用五维的开关不错 内存整理程序也写好了,调试时检测无误。我的方法是划一块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表
} mark 正想用 mark mark mark 最近也正在想写一份FLASH管理,也就是把FLASH的“不平坦”转换成“平坦”模式,思想跟你这有点像,呵呵 mark mark mark mark! 很不错,谢谢楼主
页:
[1]