提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
在单片机项目的编程中,存储模块是很常见的,所以有必要对存储模块进行架构分析并完成代码封装
此次文章以flash存储模块举例,主要讨论存储地址的划分、存储空间的划分、存储内容的格式、以及
代码如何分层的问题。
在存储模块中,我们可以将内存分为很多个块、每一个块存储不同的数据、而且存储的数据可能不止一个。这个时候应用层可以将内存抽象化为块设备,属性有如下:数据的长度、实际存储数据的长度、接受写入数据的缓存区、接受读出数据的缓存区、写入数据的条数(将来映射为地址)、读出数据的起始条数、读出数据的结束条数。块设置的状态可分为:空闲状态、du写请求、写挂起、写成功、写失败,读状态和写状态一样。当用户需要写入数据的时候我们,用户需要将数据的内容、哪个块、地址(条数)传入。有如下操作将写入的数据拷贝到定义好的缓存中、地址复制靠缓存中。将快设备的状态置为写请求。将块设置的状态置为写状态。当应用层的数据给传输层的时候,我们需要知道传出层的状态如下:
typedef void (*pDflashIf_InitType)(void);
typedef void (*pDflashIf_MainType)(void);
typedef void (*pDflashIf_ReadAllType)(void);
typedef uint8_t (*pDflashIf_WriteBankType)(uint16_t BankID, const uint8_t *BankData);
typedef uint8_t (*pDflashIf_GetIdleStatusType)(void);
typedef struct
{
pDflashIf_InitType MidInitMemory;
pDflashIf_MainType MidMainFunction;
pDflashIf_ReadAllType MidReadAll;
pDflashIf_WriteBankType MidWriteBank;
pDflashIf_GetIdleStatusType MidGetIdleStatus;
pDflashIf_InitType DrvInit;
pDflashIf_InitType DrvInitMemory;
pDflashIf_MainType DrvMainFunction;
}DflashIf_ApiType_Struct;
然后在主任务里面查询设备的状态来去给传输层传递数据。当设备是写状态的时候,我们将设备开始准备写。在准备写的操作中,需要知道传输层的状态是空闲,然后去查询设备有没有请求写,如果有就将数据和状态给传输层。
static void Dflash_PrepareWrite( void )
{
uint16 TempBankId;
if((uint8)TRUE == gv_stDflashIf_ApiFun[0u].MidGetIdleStatus())
{
if(DFLASH_WRITE_PENDING == lv_stDflashM.lv_arrDflashWriteBankFlag[lv_stDflashM.usCurrWriteBankId])
{
DflashM_SetBankWriteFlag(lv_stDflashM.usCurrWriteBankId, DFLASH_WRITE_OK);
}
else
{}
for(TempBankId = 0u; TempBankId < (uint16)DFLASHM_BANK_MAX_NUM; TempBankId++ )
{
if(DFLASH_WRITE_REQ == lv_stDflashM.lv_arrDflashWriteBankFlag[TempBankId])
{
//д״ֵ̬Ϊ��
DflashM_SetWriteBankStatus(TempBankId,FALSE);
LIB_Copy(gv_stDflashBankCfg[TempBankId].ucRamAdd,gv_stDflashBankCfg[TempBankId].ucKam1Add, gv_stDflashBankCfg[TempBankId].usLength);
if((uint8)E_OK == gv_stDflashIf_ApiFun[0u].MidWriteBank(TempBankId,gv_stDflashBankCfg[TempBankId].ucRamAdd)) //DVM_WriteBank
{
DflashM_SetBankWriteFlag(TempBankId, DFLASH_WRITE_PENDING);
lv_stDflashM.usWriteTimeOutCnt = DFLASH_WRITE_TIMEOUT_MAX_TIMER;
lv_stDflashM.bSlDflashReqStatus = FALSE;
}
else
{
}
lv_stDflashM.usCurrWriteBankId = TempBankId;
break;
}
else
{}
}
}
}
在传输层我们要定义好块的起始地址和结束地址、块的数据长度、读写的起始地址和长度。
typedef struct
{
uint8 ucMode;
uint8 ucCtrStatus;
uint8 ucApiFlag;
uint8 ucBuff[1024];
uint8 ucReReadCnt;
uint8 ucReadBankStep;
uint8 ucReWriteCnt;
uint8 ucWriteBankStep;
uint16 usWriteBankId;
uint16 usReadBankId;
uint16 usCurWriteAmount;
uint16 usStartReadAmount;
uint16 usStopReadAmount;
} Dvm_Struct;
void DVM_MainFunction( void )
{
switch(gv_stDvM.ucMode)
{
case DVM_MODE_IDLE:
DVM_ModeIdleHandle();
break;
case DVM_MODE_READALL:
DVM_ModeReadAllHandle();
break;
case DVM_MODE_WRITE_BANK:
DVM_ModeWriteBankHandle();
break;
default:
gv_stDvM.ucMode = DVM_MODE_IDLE;
break;
}
switch(gv_stDvM.ucCtrStatus)
{
case DVM_CTR_READ_BANK:
DVM_InternalReadBank();
break;
case DVM_CTR_WRITE_BANK:
DVM_InternalWriteBank();
break;
default:
gv_stDvM.ucCtrStatus = DVM_CTR_IDLE;
break;
}
}
typedef struct
{
DFJobResult_Enum ucJobResult;
DFJobType_Enum ucJob;
uint8 *ucDataPtr;
uint32 ulStartAddress;
uint32 ulLenIndex;
uint32 ulLength;
}DF_Struct;
void DF_MainFunction( void )
{
DFJobResult_Enum workResult = DF_JOB_OK;
if( DF_JOB_PENDING == lv_stDF.ucJobResult )
{
switch( lv_stDF.ucJob )
{
case DF_JOB_READ:
workResult = DF_DoJobRead();
break;
case DF_JOB_WRITE:
workResult = DF_DoJobWrite();
break;
default:lv_stDF.ucJob = DF_JOB_IDLE;
break;
}
lv_stDF.ucJobResult = workResult;
if( DF_JOB_OK == lv_stDF.ucJobResult )
{
//DF_WriteDisable();
}
else if( DF_JOB_FAILED == lv_stDF.ucJobResult )
{
//DF_WriteDisable();
LOG_DEBUG(LOG_MODULE_FLH,"SPI����\r\n");
}
else
{
;
}
}
}
对于存储设备完全可以按照上面的思路来编写驱动模型,具体代码实现,可以私信我。我