|
本帖最后由 sanit 于 2019-4-12 18:53 编辑
1.好久之前在论坛好像看到有坛友指出sf_AutoWritePage函数中重复读取flash,导致效率不高的问题。暂时没有在论坛中搜索出来帖子了。记不太清原帖子的具体内容了,谁帮我找出来原帖子,多谢!已经找到:http://www.armbbs.cn/forum.php?m ... 1095&fromuid=58
2.关于这个问题,sf_AutoWritePage函数本身是没有多余读取的,不过还有可以优化的地方。下面分别给出原版以及优化后的代码,如有不对,欢迎大家指出。
3.原版:
/*
*********************************************************************************************************
* 函 数 名: sf_AutoWritePage
* 功能说明: 写1个PAGE并校验,如果不正确则再重写三次。本函数自动完成擦除操作。
* 形 参: _pBuf :数据源缓冲区
* _uiWriteAddr :目标区域首地址
* _usSize :数据个数,不能超过页面大小(4KB)
* 返 回 值: 0 : 错误, 1 : 成功 测试通过
*********************************************************************************************************
*/
static uint8_t sf_AutoWritePage1(const uint8_t *_ucpSrc, const uint32_t _uiWrAddr, const uint16_t _usWrLen)
{
uint16_t i; /* 计算数据偏移以及for循环使用 */
uint16_t j; /* 用于延时 */
uint32_t uiFirstAddr;/* 扇区首址 */
uint8_t ucNeedErase;/* 1表示需要擦除 */
uint8_t cRet; /* 函数执行成功标志 */
/* 长度为0时不继续操作,直接认为成功 */
if (_usWrLen == 0)
{
return 1;
}
/* 如果偏移地址超过芯片容量则认为失败退出 */
if (_uiWrAddr >= g_tSF.TotalSize)
{
return 0;
}
/* 如果数据长度大于扇区容量,则认为失败退出 */
if (_usWrLen > g_tSF.PageSize)
{
return 0;
}
/* 如果FLASH中的数据没有变化,则不写FLASH */
sf_ReadBuffer(s_spiBuf, _uiWrAddr, _usWrLen);
if (memcmp(s_spiBuf, _ucpSrc, _usWrLen) == 0)
{
return 1;
}
/* 判断是否需要先擦除扇区 */
/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
ucNeedErase = 0;/* 先清除擦除标志 */
if (sf_NeedErase(s_spiBuf, _ucpSrc, _usWrLen))
{
ucNeedErase = 1;
}
/* 找到当前地址所在的页首地址,比如0x1010,属于第二页,第二页首地址是0x1000 */
uiFirstAddr = _uiWrAddr & (~(g_tSF.PageSize - 1));
if (_usWrLen == g_tSF.PageSize)/* 整个扇区都改写 */
{
for (i = 0; i < g_tSF.PageSize; i++)
{
s_spiBuf = _ucpSrc;
}
}
else/* 改写部分数据 */
{
/* 先将整个扇区的数据读出 */
sf_ReadBuffer(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
/* 再用新数据覆盖 */
i = _uiWrAddr & (g_tSF.PageSize - 1);
memcpy(&s_spiBuf, _ucpSrc, _usWrLen);
}
/* 写完之后进行校验,如果不正确则重写,最多3次 */
cRet = 0;/* 清除写入成功标志 */
for (i = 0; i < 3; i++)
{
/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
/* 读者标注:但是失败一次后,下次重新写必须执行擦除操作 */
if (ucNeedErase == 1)
{
sf_EraseSector_4K(uiFirstAddr);/* 擦除1个扇区 */
}
/* 编程一个PAGE */
sf_Page_Program(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
/* 校验数据,判断写入是否成功 */
if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
{
cRet = 1;
break;
}
else /* 连续校验两次,判断写入是否成功 */
{
if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
{
cRet = 1;
break;
}
ucNeedErase = 1;/* 写入失败的话,必须执行擦除操作 */ //读者增加,这是一个BUG
/* 失败后延迟一段时间再重试 */
for (j = 0; j < 65500; j++);
}
}
return cRet;
}
优化后:
/*
*********************************************************************************************************
* 函 数 名: sf_AutoWritePage
* 功能说明: 写1个PAGE并校验,如果不正确则再重写三次。本函数自动完成擦除操作。
* 形 参: _pBuf :数据源缓冲区
* _uiWriteAddr :目标区域首地址
* _usSize :数据个数,不能超过页面大小(4KB)
* 返 回 值: 0 : 错误, 1 : 成功 测试通过
*********************************************************************************************************
*/
static uint8_t sf_AutoWritePage(const uint8_t *_ucpSrc, const uint32_t _uiWrAddr, const uint16_t _usWrLen)
{
uint16_t i; /* 计算数据偏移以及for循环使用 */
uint16_t j; /* 用于延时 */
uint32_t uiFirstAddr;/* 扇区首址 */
uint8_t ucNeedErase;/* 1表示需要擦除 */
uint8_t cRet; /* 函数执行成功标志 */
uint16_t offset; /* 偏移量 */
/* 长度为0时不继续操作,直接认为成功 */
if (_usWrLen == 0)
{
return 1;
}
/* 如果偏移地址超过芯片容量则认为失败退出 */
if (_uiWrAddr >= g_tSF.TotalSize)
{
return 0;
}
/* 如果数据长度大于扇区容量,则认为失败退出 */
if (_usWrLen > g_tSF.PageSize)
{
return 0;
}
/* 找到当前地址所在的页首地址,比如0x1010,属于第二页,第二页首地址是0x1000 */
uiFirstAddr = _uiWrAddr & (~(g_tSF.PageSize - 1));
/* 先将整个扇区的数据读出 */
sf_ReadBuffer(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
offset = _uiWrAddr-uiFirstAddr;
/* 如果FLASH中的数据没有变化,则不写FLASH */
if (memcmp(&s_spiBuf[offset], _ucpSrc, _usWrLen) == 0)
{
return 1;
}
/* 判断是否需要先擦除扇区 */
/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
ucNeedErase = 0;/* 先清除擦除标志 */
if (sf_NeedErase(&s_spiBuf[offset], _ucpSrc, _usWrLen))
{
ucNeedErase = 1;
}
if (_usWrLen == g_tSF.PageSize)/* 整个扇区都改写 */
{
for (i = 0; i < g_tSF.PageSize; i++)
{
s_spiBuf = _ucpSrc;
}
}
else/* 改写部分数据 */
{
/* 再用新数据覆盖 */
memcpy(&s_spiBuf[offset], _ucpSrc, _usWrLen);
}
/* 写完之后进行校验,如果不正确则重写,最多3次 */
cRet = 0;/* 清除写入成功标志 */
for (i = 0; i < 3; i++)
{
/* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
/* 读者标注:但是失败一次后,下次重新写必须执行擦除操作 */
if (ucNeedErase == 1)
{
sf_EraseSector_4K(uiFirstAddr);/* 擦除1个扇区 */
}
/* 编程一个PAGE */
sf_Page_Program(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
/* 校验数据,判断写入是否成功 */
if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
{
cRet = 1;
break;
}
else /* 连续校验两次,判断写入是否成功 */
{
if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
{
cRet = 1;
break;
}
ucNeedErase = 1;/* 写入失败的话,必须执行擦除操作 */ //读者增加,这是一个BUG
/* 失败后延迟一段时间再重试 */
for (j = 0; j < 65500; j++);
}
}
return cRet;
}
这个函数优化是针对每次写数据不满一页,否则不能提高写入效率。
|
评分
-
查看全部评分
|