时间:2022-06-02 10:48:21
【 摘 要 】 本文针对许多仪器需要预置数据于FLASH芯片的情况,提出了一种将FLASH操作从数据管理类中剥离,从而使FLASH MEMORY数据管理变得更加简便、灵活及易于使用的有效解决方案。
【 关键词 】 闪存;数据管理
An Design on an Flexible Flash Memory Data Management
Wang Qing-shan
( Hainan University HainanHaikou 570208)
【 Abstract 】 Nowadays, more and more equipments using Flash Memory to store pre-set data and parameters. This design separates Flash data operations from the Management class, making the flash memory data easier and more flexible to manage and use.
【 Keywords 】 flash memory;data management
1 问题的提出
许多重要的仪器,如医学仪器,全部预置数据均存储在一片Flash Memory的若干扇区上,根据用途这些预置数据分为通用参数、各检查模式参数、TGC曲线、后处理参数、注释用语库以及穿刺参数等,系统运行时,这些数据被分别载入系统内的对应的预置数据管理对象,供系统使用,同时这些数据也可以由其对应的预置数据管理对象写入Flash Memory。
提出一个解决方案并不困难,但我们希望能实现一个合理、灵活、易于使用的解决方案:
1)预置数据根据其对应的预置数据管理对象来组织,其读写由预置数据管理对象实现;
2)多个预置数据管理对象管理的数据可以存放在同一扇区,以提高Flash Memory的存储效率;
3)各预置数据管理对象可以单独写入数据,由于Flash Memory的读写特性,同一扇区的其它数据也要同时写入;
4)容易增加新的预置数据及其管理对象;
5)使用方法简易、清晰,最好能以流的方式读写数据;
6)容易支持多厂商的FLASH芯片。
2 实现方案
数据由预置数据管理对象管理,问题在于FLASH MEMORY的读写应该由谁实现。较早的方案是数据管理对象自行读写FLASH MEMORY,为此,所有的数据管理类都继承了一个具备读、写、擦除FLASH MEMORY能力的类FlashManager。鉴于FLASH MEMORY的读写特性,在将多个预置数据管理对象管理的数据存放于同一扇区时,采用这种方案会遇到困难,因为每个数据管理对象在将数据写入FLASH MEMORY之前,都要先擦除将要写入的扇区。
解决问题的方法是,将读写FLASH MEMORY的功能从数据管理类中剥离出来,由一个单独的对象完成。
类关系图如图1所示。
DataManagerBase是纯虚类,它定义了数据管理类的接口部分:
class DataManagerBase:public UObject
{
protected:
short mSector; //数据存储在FLASH MEMORY中的扇区号
short mOffset; //存储的起始位置相对于扇区首地址的偏移量
public:
DataManagerBase(…); //构造函数,略
virtual void ReadData()=0; //将数据从FLASH读入,将由派生类重载
virtual void WriteData();
virtual void DoWriteData(FlashManager* the_flash)=0;
//将数据写入FLASH,将由派生类重载
…
};
UObject是提供对象管理基本特性的根类,如对象分层管理、对象遍历、对象识别、收发消息等能力,在此不作讨论。
WriteData()是将数据写入FLASH MEMORY的接口函数,但它实际上并未执行将数据写入FLASH操作,它只是通知父对象刷新它所在的扇区,实际的数据写入工作是由DoWriteData(FlashManager* the_flash)函数完成的,该函数通过the_flash所指的FlashManager对象将自己管理的数据写入FLASH。
ReadData和DoWriteData负责完成读写FlashManager所管理数据的工作,它们的实现代码将在派生类中定义。
FlashManager类封装了对FLASH的操作,而PresetServer类是个控制类,它拥有所有的预置数据管理对象(图2示例中的d1和d2,实际上预置数据管理对象的个数没有限制)和一个由m_pTheFlash指向的FlashManager对象,该FlashManager对象负责完成所有对FLASH的实际操作。
单独地将一个数据管理对象(d1:Data1Manager)的数据写入FLASH的时序如图3所示。
1)d1:Data1Manager收到将数据写入FLASH的消息;
2)d1:Data1Manager向thePresetServer请求刷新本扇区(mSector);
3)thePresetServer将*m-pTheFlash的扇区设置为将要刷新的扇区;
4)调用*m_pTheFlash函数擦除该扇区;
5)遍历并将m_pTheFlash对象指针传给所有的DataManager对象(包括发出请求的d1:Data1Manager对象),使扇区号与指定扇区号相符的数据管理对象将自身数据写入FLASH。
代码如下:
//如果obj是DataManager对象,则obj通过p_flash所指的FlashManager对象
//将自己所管理的数据写入FLASH
//p_flash: FlashManager指针
static void DoWriteDataTo(UObject& obj, void* p_flash)
{
if (DataManagerBaseClass == obj.TypeOfClass()) //类的动态识别
( (DataManagerBase&)obj ).DoWriteData((FlashManager*)p_flash);
}
void DataManagerBase::WriteData()
{
//请求刷新mSector扇区
RequestToParent(Req_WriteDataInSector, 0, mSector);
}
//处理子对象发来的请求
void PresetServer::HandleChildRequest(UChildMessage* p_req)
{
if(p_req->NotifyCode == Req_WriteDataInSector) //如果是刷新扇区的请求
{
//将FlashManager的扇区设置为将要刷新的扇区
m_pTheFlash -> SetSectorNum( p_req->LParam );
//擦除该扇区
m_pTheFlash -> EraseSector();
//遍历所有的DataManager子对象,使扇区号与指定扇区号相符的数据管理对象
//用m_pTheFlash所指的FlashManager对象将自身数据写入FLASH
ForEach(DoWriteDataTo, (void *)m_pTheFlash);
}
… //处理其它请求
}
void Data1Manager::DoWriteData(UFlashManager* the_flash)
{
//如果不是操作自身数据所在扇区,返回
if (the_flash->SectorNum() != mSector)
return;
//将数据写入FLASH
the_flash->WriteData(地址偏移1,数据1);
the_flash->WriteData(地址偏移2,数据2);
…
}
相对于写数据,DataManager从FLASH中读出数据简单一些,DataManager只须从PresetServer处得到FlashManager对象指针,然后按地址偏移将数据读出即可。
3 讨论
以上方案实现了数据管理对象(DataManager)与数据操作对象(FlashManager)的动态绑定,经过进一步分析,发现这实际上是bridge设计模式的变型。这是说明组合关系比继承关系更为灵活的又一个例子。
我们的出发点是避免数据管理对象在写FLASH之前擦除目标扇区,为此将FLASH操作从数据管理类中剥离出来,形成独立的FlashManager类,现在这种灵活性带来了其它好处:
1)DataManager和FlashManager可以彼此独立地变化,我们可以独立地扩充DataManager和FlashManager的类层次结构。
2)DataManager对象和FlashManager对象可以动态组合,DataManager甚至可以在程序运行时选择其它的FlashManager(例如支持其它公司的FLASH芯片)。当然在线切换FLASH型号没什么必要,但在其它场合这种切换是有用的,如医学仪器的中英文版动态切换、检查模式参数动态切换就是用bridge模式实现的。
3)FlashManager有可能被设计为通用的FLASH操作类,支持多种FLASH,也可以用于凡是需要操作FLASH的场合。
4)FlashManager与DataManager的分离使得FlashManager采用流的方式读写数据成为可能,DataManager不必再理会每个数据在FLASH扇区中的偏移地址,它只需以固定的顺序将自己管理的数据流入数据管理大为简化,代码简洁清晰。
4 以流的方式工作
要使FlashManager能以流的方式工作,首先定义与流有关的函数,重载流运算符:
class FlashManager
{
short mSectorNum; //扇区号
short* ChipBaseAddr; //FLASH基地址
short* SectorBaseAddr; //扇区基地址
int mOffsetInWord; //偏移量
int mStreamPosi; //流位置
public:
//构造函数与析构函数,略
//流函数
void MoveTo(int stream_posi); //移到流的指定位置
FlashManager& operator
FlashManager& operator
FlashManager& operator
FlashManager& operator
//… 其它需要流入的数据类型
FlashManager& operator>>(char& d);
FlashManager& operator>>(short& d);
FlashManager& operator>>(int& d);
FlashManager& operator>>(float& d);
//… 其它需要流出的数据类型
private:
short ReadWord(long rAddr)
float ReadFloat(long rAddr);
bool WriteWord(long rAddr, short sData);
void WriteFloat(long rAddr,float fData);
};
重载什么数据类型的流运算符取决于DataManager管理的数据类型有多少。
//将一个char数据写入FLASH
FlashManager& FlashManager::operator
{
WriteWord(mStreamPosi, d); //将数据写入FLASH流指针指定位置
mStreamPosi ++; //流指针加1
return *this;
};
//从FLASH读出一个char数据
FlashManager& FlashManager::operator>>(char& d)
{
d = (char) ReadWord(mStreamPosi); //从FLASH流指针指定位置读出数据
mStreamPosi ++; //流指针加1
return *this;
};
流操作符返回FlashManager对象引用的目的在于能以连续调用的格式书写代码:
//以下为伪代码
//Data1Manager从FLASH读入数据
void Data1Manager::ReadData()
{
//获取FlashManager对象指针
FlashManager* p_flash = (FlashManager*)RequestToParent(Req_PresetFlashPtr, mSector,
mOffsetInWord);
//移到流的起始位置;
p_flash -> MoveTo( 0 );
//数据从FlashManager流出(从FLASH读),数据1~n为Data1Manager管理的数据
*p_flash >> 数据1
>> 数据2
…
>> 数据n;
}
//Data1Manager将数据写入FLASH
void Data1Manager::DoWriteData(FlashManager* p_flash)
{
//移到流的起始位置;
p_flash -> MoveTo( 0 );
//数据流入FlashManager(写入FLASH),数据1~n为Data1Manager管理的数据
*p_flash
…
}
5 结束语
本方案设计将FLASH操作从数据管理类中剥离出来,避免了数据管理对象在写FLASH之前擦除目标扇区,数据管理对象和数据操作对象可以相对独立地变化及按需进行动态组合,采用流的方式读写数据,进而使得FLASH MEMORY的数据管理变得更加简便、合理、灵活及易于使用。
基金项目:
本文受国家自然科学基金项目资助(项目批准号:70561001)
作者简介:
王青山,男,海南大学讲师,华侨大学博士研究生;研究方向:信息化管理及知识管理。