硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4558|回复: 11
收起左侧

[有问必答] SpiFlash操作函数bug+FatFs操作SpiFlash的bug讨论,再问硬汉一个问题

[复制链接]

20

主题

249

回帖

309

积分

高级会员

积分
309
发表于 2016-4-12 22:36:30 | 显示全部楼层 |阅读模式
如题,我移植F407开发板例程中【V5-102_串行Flash读写例程(V1.2).rar】的SpiFlash操作函数时发现sf_AutoWritePage函数的一个bug,请硬汉哥确认下。

bug:第87行应该加一句:ucNeedErase = 1;原因见图片中的解释。
问题1:写完数据后为什么要比较两次呢?sf_CmpData条用了2次,谢谢

问题2:f_mkfs("0:",0,0);第三个参数为什么写0,这个值到底写多少合适?

bug.png

  1. static uint8_t sf_AutoWritePage(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16_t _usWrLen)
  2. {
  3.   uint16_t i;
  4.   uint16_t j;         /* 用于延时 */
  5.   uint32_t uiFirstAddr;   /* 扇区首址 */
  6.   uint8_t ucNeedErase;    /* 1表示需要擦除 */
  7.   uint8_t cRet;
  8.   /* 长度为0时不继续操作,直接认为成功 */
  9.   if (_usWrLen == 0)
  10.   {
  11.     return 1;
  12.   }
  13.   /* 如果偏移地址超过芯片容量则退出 */
  14.   if (_uiWrAddr >= g_tSF.TotalSize)
  15.   {
  16.     return 0;
  17.   }
  18.   /* 如果数据长度大于扇区容量,则退出 */
  19.   if (_usWrLen > g_tSF.PageSize)
  20.   {
  21.     return 0;
  22.   }
  23.   /* 如果FLASH中的数据没有变化,则不写FLASH */
  24.   sf_ReadBuffer(s_spiBuf, _uiWrAddr, _usWrLen);
  25.   if (memcmp(s_spiBuf, _ucpSrc, _usWrLen) == 0)
  26.   {
  27.     return 1;
  28.   }
  29.   /* 判断是否需要先擦除扇区 */
  30.   /* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
  31.   ucNeedErase = 0;
  32.   if (sf_NeedErase(s_spiBuf, _ucpSrc, _usWrLen))
  33.   {
  34.     ucNeedErase = 1;
  35.   }
  36.   uiFirstAddr = _uiWrAddr & (~(g_tSF.PageSize - 1));
  37.   if (_usWrLen == g_tSF.PageSize)   /* 整个扇区都改写 */
  38.   {
  39.     for (i = 0; i < g_tSF.PageSize; i++)
  40.     {
  41.       s_spiBuf[i] = _ucpSrc[i];
  42.     }
  43.   }
  44.   else            /* 改写部分数据 */
  45.   {
  46.     /* 先将整个扇区的数据读出 */
  47.     sf_ReadBuffer(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
  48.     /* 再用新数据覆盖 */
  49.     i = _uiWrAddr & (g_tSF.PageSize - 1);
  50.     memcpy(&s_spiBuf[i], _ucpSrc, _usWrLen);
  51.   }
  52.   /* 写完之后进行校验,如果不正确则重写,最多3次 */
  53.   cRet = 0;
  54.   for (i = 0; i < 3; i++)
  55.   {
  56.     /* 如果旧数据修改为新数据,所有位均是 1->0 或者 0->0, 则无需擦除,提高Flash寿命 */
  57.     if (ucNeedErase == 1)
  58.     {
  59.       sf_EraseSector(uiFirstAddr);    /* 擦除1个扇区 */
  60.     }
  61.     /* 编程一个PAGE */
  62.     sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.PageSize);
  63.     if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
  64.     {
  65.       cRet = 1;
  66.       break;
  67.     }
  68.     else
  69.     {
  70.       if (sf_CmpData(_uiWrAddr, _ucpSrc, _usWrLen) == 0)
  71.       {
  72.         cRet = 1;
  73.         break;
  74.       }
  75.       /* 失败后延迟一段时间再重试 */
  76.       for (j = 0; j < 10000; j++);
  77.     }
  78.   }
  79.   return cRet;
  80. }
复制代码



编辑原因:又发现一个FatFs操作SpiFlash的bug。例程为:【V5-107e_FatFS文件系统例程(SPI串行Flash)(V1.2).rar】

dickio.c文件中写flash有问题:count次调用了sf_WriteBuffer,但是参数却没有变化,这里有问题哦。
  1. #if _USE_WRITE
  2. DRESULT disk_write (
  3.     BYTE pdrv,            /* Physical drive nmuber (0..) */
  4.     const BYTE *buff,    /* Data to be written */
  5.     DWORD sector,        /* Sector address (LBA) */
  6.     BYTE count            /* Number of sectors to write (1..128) */
  7. )
  8. {
  9.     switch (pdrv) {
  10.         case FS_SPI_FLASH :
  11.             {
  12.                 uint8_t i;
  13.                 for(i = 0; i < count; i++)
  14.                 {
  15.                     sf_WriteBuffer((uint8_t *)buff, sector << 12, 4096);
  16.                 }
  17.                 return RES_OK;
  18.             }
  19.     }
  20.     return RES_PARERR;
  21. }
  22. #endif
复制代码
改法一:
  1. for(i = 0; i < count; i++)
  2. {
  3.       sf_WriteBuffer((uint8_t *)buff, sector << 12, 4096);   
  4. }
  5. 上面的for直接改为:
  6. sf_WriteBuffer((uint8_t *)buff, sector << 12, count<<12);
  7. 因为sf_WriteBuffer函数支持写多个扇区的功能,所以可以这样写
复制代码

改法二:
  1. for循环改为如下的方式:
  2. for(i = 0; i < count; i++)
  3. {
  4.       sf_WriteBuffer((uint8_t *)buff, sector << 12, 4096);   
  5.       sector++;
  6.       buff +=4096;
  7. }
复制代码
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107128
QQ
发表于 2016-4-13 00:52:21 | 显示全部楼层
你的这种做法可以的。感觉是个bug,这个驱动是站长做的,我跟站长说一下。
回复

使用道具 举报

747

主题

1049

回帖

3295

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3295
发表于 2016-4-13 01:09:58 | 显示全部楼层
如果比较OK 就返回, 如果出错,就再比较一次确认有错再重试。大多数情况,写一次就会OK,如果由于干扰导致比较出错,那么就再比较一次,如果还有错,才能认为确实错了,此时需要重试。
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-13 08:40:18 | 显示全部楼层

回 armfly 的帖子

armfly:如果比较OK 就返回, 如果出错,就再比较一次确认有错再重试。大多数情况,写一次就会OK,如果由于干扰导致比较出错,那么就再比较一次,如果还有错,才能认为确实错了,此时需要重试。 (2016-04-13 01:09) 
哦,明白了,为了保险起见比较两次
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-13 08:41:07 | 显示全部楼层

回 eric2013 的帖子

eric2013:你的这种做法可以的。感觉是个bug,这个驱动是站长做的,我跟站长说一下。
 (2016-04-13 00:52) 
好的,谢谢了啦
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-15 10:24:57 | 显示全部楼层

回 eric2013 的帖子

eric2013:你的这种做法可以的。感觉是个bug,这个驱动是站长做的,我跟站长说一下。
 (2016-04-13 00:52) 
呼叫硬汉哥 ,,@eric2013
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107128
QQ
发表于 2016-4-15 10:29:41 | 显示全部楼层

回 小狐狸 的帖子

小狐狸:呼叫硬汉哥 ,,@eric2013    

 (2016-04-15 10:24) 
不要用这个例子,这个有bug,我已经修正了,参考这个帖子我更新,:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=14894
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-15 23:22:29 | 显示全部楼层

回 eric2013 的帖子

eric2013:不要用这个例子,这个有bug,我已经修正了,参考这个帖子我更新,:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=14894 (2016-04-15 10:29)
谢谢回复 @eric2013  

后面的问题确实在新的例程中更新了,,但是我说的第一个bug还没改,是bug吗?

即在函数:
sf_AutoWritePage中第一次写没写正确,第二次写前需要把:ucNeedErase = 1;如果前面ucNeedErase = 0;时第二次就无法写了。。。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107128
QQ
发表于 2016-4-16 00:28:19 | 显示全部楼层

回 小狐狸 的帖子

小狐狸:谢谢回复 @eric2013   

后面的问题确实在新的例程中更新了,,但是我说的第一个bug还没改,是bug吗?

....... (2016-04-15 23:22)
for循环里面ucNeedErase 是一直等于1 的。
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-16 10:27:35 | 显示全部楼层

回 eric2013 的帖子

eric2013:

for循环里面ucNeedErase 是一直等于1 的。
@eric2013   

嗯,只要ucNeedErase=1后,for里面是没人清它的。。。关键是ucNeedErase进入for前不一定是1哦,所以for里面应该把ucNeedErase置位一次。。

请看截图: ucNeedErase置位是有条件的。
bug.png
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107128
QQ
发表于 2016-4-16 10:46:01 | 显示全部楼层

回 小狐狸 的帖子

小狐狸:@eric2013      

嗯,只要ucNeedErase=1后,for里面是没人清它的。。。关键是ucNeedErase进入for前不一定是1哦,所以for里面应该把ucNeedErase置位一次。。

....... (2016-04-16 10:27) 
对的,这样更加保险。[s:142]
回复

使用道具 举报

20

主题

249

回帖

309

积分

高级会员

积分
309
 楼主| 发表于 2016-4-16 21:28:44 | 显示全部楼层

回 eric2013 的帖子

eric2013:对的,这样更加保险。[s:142] (2016-04-16 10:46) 
嗯,,,好的

再次感谢,spiflash的驱动写的很完善。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-19 22:44 , Processed in 0.232838 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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