硬汉嵌入式论坛

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

SPI接口串行FLASH BUG问题

  [复制链接]

5

主题

85

回帖

100

积分

初级会员

积分
100
发表于 2018-9-14 09:42:05 | 显示全部楼层 |阅读模式
最近在做项目时用到硬汉的bsp_spi_flash.c的代码,但是发现了一个bug,主要在
static uint8_t sf_NeedErase(uint8_t * _ucpOldBuf, uint8_t *_ucpNewBuf, uint16_t _usLen)函数中,该算法如下:
/*
    算法第1步:old 求反, new 不变
          old    new
          1101   0101
    ~     1111
        = 0010   0101

    算法第2步: old 求反的结果与 new 位与
          0010   old
    &      0101   new
         =0000

    算法第3步: 结果为0,则表示无需擦除. 否则表示需要擦除
*/
算法是基于在一没有写的区域中(全是0xFF),太过理想化了。
假如有一数组a[0x77,0x88,0x99,0xaa,0x00,0x00,0xbb,0xcc]写到flash中的4096地址,
而flash的4096地址读出相同长度的数据是b[0x77,0x88,0x99,0xaa,0x22,0x55,0xbb,0xcc],
执行sf_NeedErase(b,a,8)恒为0,即无需擦除,导致写不成功,原因是数组a中的a[4]和a[5]为0x00。
避免这个bug只能写之前就执行擦除。

回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115667
QQ
发表于 2018-9-14 10:22:17 | 显示全部楼层
0x00取反是0xFF,与0x22和0x55进行与操作怎么恒为0?
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 11:50:25 | 显示全部楼层
eric2013 发表于 2018-9-14 10:22
0x00取反是0xFF,与0x22和0x55进行与操作怎么恒为0?

数组b是从flash读出的数据,数组a是新写进的数据,数组b是_ucpOldBuf,数组a是_ucpNewBuf
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115667
QQ
发表于 2018-9-14 12:03:00 | 显示全部楼层
huohua1991 发表于 2018-9-14 11:50
数组b是从flash读出的数据,数组a是新写进的数据,数组b是_ucpOldBuf,数组a是_ucpNewBuf

0x00是特殊情况,这个不用管,因为之前位是1的时候,可以变成0,而是0的时候还是0。
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 12:52:36 | 显示全部楼层
eric2013 发表于 2018-9-14 12:03
0x00是特殊情况,这个不用管,因为之前位是1的时候,可以变成0,而是0的时候还是0。

我觉得这个还是要关注一下,因为这会导致在某种情况下向flash写0x00不成功(我不知道这种情况发生的概率有多大,但还是会存在),而且这也不可估计。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115667
QQ
发表于 2018-9-14 13:15:22 | 显示全部楼层
huohua1991 发表于 2018-9-14 12:52
我觉得这个还是要关注一下,因为这会导致在某种情况下向flash写0x00不成功(我不知道这种情况发生的概率 ...

没问题,除非flash出bug了。

你不放心可以不做此判断,强制擦除即可。
回复

使用道具 举报

354

主题

2164

回帖

3231

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3231
发表于 2018-9-14 15:28:47 | 显示全部楼层
本帖最后由 caicaptain2 于 2018-9-14 15:32 编辑
huohua1991 发表于 2018-9-14 12:52
我觉得这个还是要关注一下,因为这会导致在某种情况下向flash写0x00不成功(我不知道这种情况发生的概率 ...

flash是按位bit存储信息的。 如果原来是1,可以直接写成0;如果原来是0,就必须先擦除,才能写成1. 所以,flash擦除的时候都是写成FF,而不是其他的数据。

bsp中函数的意思是,一个字节中,如果某个bit已经是1,可以直接写成0,无需擦除步骤。如果某个bit已经是0,那么可以不写,继续保持0.

按照你的例子,老数据是 1010 1010,写成0的时候,只要把为1的bit写成0即可,所以不用擦除。拓展来说,如果写字节“0”,不管原来位置是个啥数据,都不用擦除。
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 17:29:17 | 显示全部楼层
caicaptain2 发表于 2018-9-14 15:28
flash是按位bit存储信息的。 如果原来是1,可以直接写成0;如果原来是0,就必须先擦除,才能写成1. 所以 ...

我觉得你曲解我了,要往flash写新数据先擦除flash再写这是无可厚非的,我的意思是
有一数组a[8] = {0x77,0x88,0x99,0xaa,0x00,0x00,0xbb,0xcc}要写到flash中的4096地址,
而原本flash的4096地址原来的同是8字节长度数据b[8] = {0x77,0x88,0x99,0xaa,0x22,0x55,0xbb,0xcc},
数组a是新写的数据,数组b是早已存到flash中以4096地址为首地址的数据,通过执行函数sf_NeedErase(b,a,8)得出结果为0,无需擦除,但是数组a和数组b的数据不一样的,是需要擦除的。这显然不是你所解释的,这是判断需不需要擦除的算法的缺陷,算法忽略往flash写0x00的情况。可能我说的这种情况比较特殊,但并不代表不发生。假如一个int类型数据已进flash中(其中数据非零),之后我把这数据清零再写进flash,函数sf_NeedErase就执行有误导致无法更新相应flash的数据。
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-14 19:09:19 | 显示全部楼层
huohua1991 发表于 2018-9-14 17:29
我觉得你曲解我了,要往flash写新数据先擦除flash再写这是无可厚非的,我的意思是
有一数组a[8] = {0x77 ...

你理解错误,这个算法存在的原因就是1变0或者0变0不擦除,提高寿命。你实验写入不对,是你的原因。实测这套代码用了很久,没出现过任何写入和读出异常的现象。不过这套代码其他地方存在小细节上的问题,不是你指出的这个地方。
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 20:53:25 | 显示全部楼层
sanit 发表于 2018-9-14 19:09
你理解错误,这个算法存在的原因就是1变0或者0变0不擦除,提高寿命。你实验写入不对,是你的原因。实测这 ...

这一点我知道,但请你认真探究我的实验,并不是我写入不对。我在这提出这个bug只是在提醒大家检测需不需要擦除的算法在某些情况下会出错,而不至于出现问题后过分相信代码而忽视它从而无从下手,我在这里只是提供一个纠错的途径
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-14 21:55:31 | 显示全部楼层
huohua1991 发表于 2018-9-14 20:53
这一点我知道,但请你认真探究我的实验,并不是我写入不对。我在这提出这个bug只是在提醒大家检测需不需 ...

我想说的是,起码代码在你提出的这种情况下,是可以正常写入和读出的,不会出错。你反驳之前,你得找到一种情况,让它写入异常
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 22:17:33 | 显示全部楼层
sanit 发表于 2018-9-14 21:55
我想说的是,起码代码在你提出的这种情况下,是可以正常写入和读出的,不会出错。你反驳之前,你得找到一 ...

这种情况我已经提出,假如一个int类型数据已进flash中(其中数据非零),之后我把这数据清零再写进flash,函数sf_NeedErase就执行有误导致无法更新相应flash的数据。好好想一想
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-14 22:42:52 | 显示全部楼层
看来很多人并不懂我提出这个bug的想法,我在这里说一下:
假如我有一个int类型的数据 a 要写进flash的4096地址,而我之前已经写过这数据到flash的4096地址,假如flash中4096地址的数据值为12345678(十六进制:0xbc614e),
在flash中存的形式是:
        4096---------0x4e,
        4097---------0x61,
        4098---------0xbc,
        4099---------0x00
而我现在把数据a清零后在更新到flash的4096地址,调用函数sf_WriteBuffer((unsigned char*)&a, 4096,sizeof(int)),主要这里的传入的a值等于0,
之后会调用到uint8_t sf_NeedErase(uint8_t * _ucpOldBuf, uint8_t *_ucpNewBuf, uint16_t _usLen)函数,执行的过程是:
   (~0x4e) & 0x00 = 0x00, (~0x61) & 0x00 = 0x00, (~0xbc) & 0x00 = 0x00, (~0x00) & 0x00 = 0x00
返回的结果是0,即无需擦除;
在uint8_t sf_AutoWritePage(uint8_t *_ucpSrc, uint32_t _uiWrAddr, uint16_t _usWrLen)函数中:
                 if (ucNeedErase == 1)
                {
                        sf_EraseSector(uiFirstAddr);                /* 擦除1个扇区 */  这一步无法执行
                }

                /* 编程一个PAGE */
                sf_PageWrite(s_spiBuf, uiFirstAddr, g_tSF.PageSize);   直接执行这一步就错误了,因为flash更新数据前一定要擦除
综上所述,函数sf_NeedErase的算法有少许缺陷,这是我在项目中遇到的状况。
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-14 23:01:19 | 显示全部楼层
huohua1991 发表于 2018-9-14 22:42
看来很多人并不懂我提出这个bug的想法,我在这里说一下:
假如我有一个int类型的数据 a 要写进flash的4096 ...

前面好多网友都回复你了啊,由1变0不需要擦除,不需要擦除,不需要擦除,重要的事情说三遍。自己再实验一下吧。
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-15 11:35:59 | 显示全部楼层
sanit 发表于 2018-9-14 23:01
前面好多网友都回复你了啊,由1变0不需要擦除,不需要擦除,不需要擦除,重要的事情说三遍。自己再实验一 ...

你别当我是一个喷子,这我当然实验了,而且是反复试验,就是我说的情况不行,如果可以我也不会闲着在这瞎扯,那你也有没有实验我提出的情况,不要总是说由1变0不需要擦除,flash中的数据中的数据只能是0xFF的时候才可以写,这是flash的特性,并不是像你所说的flash中的数据只要是由1变0不需要擦除直接写,那这已经不是flash的特性了好吗,flash写之前就要擦除将所写块全置为0xff才可以写,这是flash的读写标准。由1变0不需要擦除的想法是你对flash的特性了解不足,这是一个误解。
回复

使用道具 举报

5

主题

85

回帖

100

积分

初级会员

积分
100
 楼主| 发表于 2018-9-15 11:48:47 | 显示全部楼层
sanit 发表于 2018-9-14 23:01
前面好多网友都回复你了啊,由1变0不需要擦除,不需要擦除,不需要擦除,重要的事情说三遍。自己再实验一 ...

另外,flash的操作没有按位操作,如果有,你说的由1变0不需要擦除很正确,但是实际flash是按字节操作,一个字节就是规定一个特定地址,而不是一个bit就规定一个特定地址
回复

使用道具 举报

3

主题

105

回帖

114

积分

初级会员

积分
114
发表于 2018-9-15 17:58:08 | 显示全部楼层
huohua1991 发表于 2018-9-15 11:35
你别当我是一个喷子,这我当然实验了,而且是反复试验,就是我说的情况不行,如果可以我也不会闲着在这瞎 ...

说实话,我没有实验做过。
楼主你的意思是,必须以字节为单位:可以把flash中的0xff改成任何值;
而不能把诸如0x55(非0xff)改成0x00对吗?
回复

使用道具 举报

3

主题

105

回帖

114

积分

初级会员

积分
114
发表于 2018-9-15 17:58:47 | 显示全部楼层
楼上各位不如按我的说法实验一下看看是不是这样
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-15 21:08:35 | 显示全部楼层
各位,等我明天给实验一下,等着我的结果。不见不散。
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-17 09:28:56 | 显示全部楼层
本帖最后由 sanit 于 2018-9-18 09:16 编辑

昨天台风太大,今天上班做个试验。大家看图片即可。代码我使用的是安富莱的原版代码,并没有在写之前强制擦除。
第一步,整体流程.jpg
擦除之后都是0xFF.jpg
写入第一组数据.jpg
结论.jpg

Flash_Test.rar

9.16 MB, 下载次数: 20

上传代码

回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-17 09:30:24 | 显示全部楼层
楼主快来看看试验结果啊
回复

使用道具 举报

680

主题

3479

回帖

5544

积分

论坛元老

积分
5544
发表于 2018-9-17 09:48:49 | 显示全部楼层
sanit 发表于 2018-9-17 09:28
昨天台风太大,今天上班做个试验。大家看图片即可。代码我使用的是安富莱的原版代码,并没有在写之前强制擦 ...

学习了啊,
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2018-9-17 10:28:45 | 显示全部楼层

必须怼回去,楼主还嘴硬
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115667
QQ
发表于 2018-9-17 11:57:45 | 显示全部楼层
sanit 发表于 2018-9-17 09:28
昨天台风太大,今天上班做个试验。大家看图片即可。代码我使用的是安富莱的原版代码,并没有在写之前强制擦 ...

非常感谢做的测试
回复

使用道具 举报

3

主题

105

回帖

114

积分

初级会员

积分
114
发表于 2018-9-18 08:02:11 | 显示全部楼层
sanit 发表于 2018-9-17 09:28
昨天台风太大,今天上班做个试验。大家看图片即可。代码我使用的是安富莱的原版代码,并没有在写之前强制擦 ...

终于有结论了,干得不错!
回复

使用道具 举报

354

主题

2164

回帖

3231

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3231
发表于 2018-9-18 09:28:59 | 显示全部楼层
sanit 发表于 2018-9-17 09:28
昨天台风太大,今天上班做个试验。大家看图片即可。代码我使用的是安富莱的原版代码,并没有在写之前强制擦 ...

非常认真负责! 赞一个!

妹子头像好靓!

楼主的观点是看datasheet得到的,可是datasheet经常不会深入,也属情有可原。
回复

使用道具 举报

5

主题

582

回帖

597

积分

版主

Rank: 7Rank: 7Rank: 7

积分
597
发表于 2019-2-23 10:34:02 | 显示全部楼层
huohua1991 发表于 2018-9-15 11:35
你别当我是一个喷子,这我当然实验了,而且是反复试验,就是我说的情况不行,如果可以我也不会闲着在这瞎 ...

基本就是你说的这个事实,不过stm32片内flash,如果要写入的值是0,那么即使原来flash值不是0xff,也是不会报错的,stm32只校验了这种情况,虽然完全可以是楼上某些同学说的还可以精细化一些

至于spi flash是不是也是这种做法,这个必须看手册
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2019-2-23 20:42:28 | 显示全部楼层
myxiaonia 发表于 2019-2-23 10:34
基本就是你说的这个事实,不过stm32片内flash,如果要写入的值是0,那么即使原来flash值不是0xff,也是不 ...

STM32片内flash写之前应该必须是0XFF才行,GD32有设置位去设置写之前是不是必须要是0XFF。W25Q这类的写之前不要求是0XFF。
回复

使用道具 举报

5

主题

582

回帖

597

积分

版主

Rank: 7Rank: 7Rank: 7

积分
597
发表于 2019-2-24 17:13:00 | 显示全部楼层
sanit 发表于 2019-2-23 20:42
STM32片内flash写之前应该必须是0XFF才行,GD32有设置位去设置写之前是不是必须要是0XFF。W25Q这类的写之 ...

原来如此,不过stm32如果写入0是可以不擦除,只有这种情况才可以,其他都会报错,这个是手册上的内容
回复

使用道具 举报

39

主题

1504

回帖

1626

积分

至尊会员

积分
1626
发表于 2019-2-24 18:09:37 | 显示全部楼层
myxiaonia 发表于 2019-2-24 17:13
原来如此,不过stm32如果写入0是可以不擦除,只有这种情况才可以,其他都会报错,这个是手册上的内容

是的,都是1变0不需要擦除。0变1需要擦除。
回复

使用道具 举报

29

主题

514

回帖

606

积分

金牌会员

积分
606
QQ
发表于 2019-4-12 09:04:56 | 显示全部楼层
sanit 发表于 2019-2-23 20:42
STM32片内flash写之前应该必须是0XFF才行,GD32有设置位去设置写之前是不是必须要是0XFF。W25Q这类的写之 ...

我觉得这句总结是重点。 楼主使用的flash 品牌没有说明,或许是flash 的使用差异。

我之前用STM32 内部的flash模拟 EEPROM,写数据的时候也有发现 在某些前提条件不需要擦除就可以继续写,另外一些条件,不擦除写就会报错。

flash发展到今天,确实不能把几年前的一些经验理论使用到最近的flash的新技术上。

简单举个例子。原来的数据是 0x1F,往这个地址直接写0x1F, 写成功;同样 往这个地址直接写0x13,也会写成功。但是如果直接写0x55,就会失败,需要先擦除再写。
Releasing your creativity
回复

使用道具 举报

29

主题

514

回帖

606

积分

金牌会员

积分
606
QQ
发表于 2019-4-12 09:09:35 | 显示全部楼层
前端时间我总结上一个emWin的项目我就深有体会,公司给的资源就那么一点。产品的配置是“穷”配置,很多画图的方法,优化的方法都不适用,折腾来折腾去,就得找回七八年前的一些技术、例子。

当下如果是“穷”配置,使用七八年前的技术,个别技术实测结果验证 当下的新技术新水平,确实容易比对出一些不一样的 bug,不一样的理论说法。
Releasing your creativity
回复

使用道具 举报

354

主题

2164

回帖

3231

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3231
发表于 2019-9-12 15:24:52 | 显示全部楼层
翻出这个老帖子,我碰到一个偶发现象。使用的armfly的spi flash的bsp来操作。 就是常用的25Q64系列,研发和测试的时候,都没有问题。
但是,产品在客户那边上电开机的时候,spi flash中存储的参数读出来全是ff。重启一下就好了。这种情况大概一两个月发生一次,很是诡异。
然后,生产部门说,产品在存储参数的时候,如果快速的按存储键三五次,有可能数据存储不成功。。。。
还没有找到合适的方法去定位这种bug。。。难。。。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
115667
QQ
发表于 2019-9-12 15:53:45 | 显示全部楼层
caicaptain2 发表于 2019-9-12 15:24
翻出这个老帖子,我碰到一个偶发现象。使用的armfly的spi flash的bsp来操作。 就是常用的25Q64系列,研发和 ...

找个好排查,可以使用我RL-FlashFS的SPI Flash底层驱动,比较简单易用。

V4网盘里面的FlashFS文件夹里面有。底层API一目了解,直接调用测试即可。
回复

使用道具 举报

354

主题

2164

回帖

3231

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3231
发表于 2019-9-17 10:56:38 | 显示全部楼层
本帖最后由 caicaptain2 于 2019-9-17 11:03 编辑
caicaptain2 发表于 2019-9-12 15:24
翻出这个老帖子,我碰到一个偶发现象。使用的armfly的spi flash的bsp来操作。 就是常用的25Q64系列,研发和 ...

根据经验,可能是硬件的原因。 又读了一下spi-flash的datasheet参数,发现操作flash的指令最好是上电10ms以后。datasheet给出的最小是1ms,最大是10ms。
一般读取flash里面的参数都在程序的开始阶段,我的代码中,示波器观察到是arm启动后6.5ms,所以这里的时间偶尔不充分?
先搞个普通延时函数,再读取flash看看反馈吧。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-10 19:14 , Processed in 0.446637 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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