关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。

因为项目需要使用IAP进行固件更新,于是我的芯片FLASH需要划分为四段:

  1. 一段Boot loader 用以判断上电后进入哪一段APP
  2. APP1程序
  3. APP2程序
  4. 配置文件ID_card

因为程序更新采用的是末尾淘汰,即进入程序后,判断APP1.2哪一个是完整的,在完整的同时判断那一段APP版本更加高就运行哪一段APP。所以我需要一段保存配置,用来记录APP1和APP2的数据信息,即程序APP的ID_card。那么除去APP的擦写不说,对于配置文件,也需要进行频繁的FLASH擦写操作

我使用的单片机是STM32F429ZIT6,于是我去查看了429的FLASH区域介绍。
关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第1张图片
大家可以看到, STM32F4系列和F1,F3的FLASH划分不同。我们都知道F1系列的FLASH划分是从0x0800 0000地址开始,对于小容量的进行1kb一页,对于大容量的2kb一页进行划分的。而在F4中进行了升级,以我使用的STM32F429ZIT6为例,2M大小按照容量先进行了块1和块2划分,即程序中经常看到的关键字Bank1和Bank2。在每个Bank中,又以扇区划分为16kb的扇区4个,64kb扇区1个,128kb扇区7个,共计大小1M。

理论如上,说干就干。于是用STM32CubeMX生成驱动,配置时间树,调试串口打印……
一系列猛如虎的操作过后。程序写好了如下图所示。

关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第2张图片
以上是main.h中的声明,以及我对程序位置的一些把控。可以看到我把ID_card的位置放在了块1的扇区2位置。因为单片机运行以后都是从0x0800 0000位置开始,所以前两页32kb大小用来存放Bootloader。紧跟着扇区2位置即0x0800 8000位置的一个扇区用来存放ID_card配置信息。
关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第3张图片
这是程序初始化后进入的demo循环读写测试程序。流程如下:

  1. 解锁FLASH
  2. 清除一系列标志位,这一条可以不要,但是为了确保成功以及排除一切障碍,手动将他全部清除,加大代码的安全性。
  3. 配置擦除FLASH的结构体包括擦除的方式,擦除的地址,擦除的大小,擦除的电压。
  4. 执行HAL_FLASHx_Erase函数进行擦除。
  5. 执行HAL_FLASH_Program对擦除区域写入新的数值。
  6. 打印判断是否成功。
  7. FLASH上锁。

这里说一下,我在之前有对指针变量赋值地址,如图:
在这里插入图片描述
即将ID_card的指针地址指向0x0800 8000的位置。

另外说一下,不建议大家循环擦写FLASH,因为FLASH是有擦除次数限制的,在循环中擦FLASH会导致FLASH到达上限后无法擦除,读没有限制。擦除结构体中VoltageRange参数是可以配置擦除电压,从而影响擦除的大小是多少,这里采用32bit一擦。不建议采用64bit擦除,虽然速度会快但是影响芯片FLASH寿命。
当然以上都是我的逼逼赖赖,我自己也循环擦了,当然这只是demo测试用法。

好,一切准备就绪,开始大刀阔斧的跑程序了,看了黄历今天适合代码测试,如果我运气足够好,我马上就可以下班了。然而,,,我裂开了。
关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第4张图片
什么情况???除了上电第一次按照我的逻辑正常写入以外,第二次就跑飞了。尼玛!!!STM32你已经是一个成熟的芯片了,要学会看懂开发者的意思,自己跑起来。不要让我加班和掉头发。

好,口嗨过后,冷静思考。
我们都知道STM32的FLASH数据分布是没有数据默认全都是1。即擦除以后,应该所有位都是1。我按照4字节一个数据,那么擦除后应该是32位1即0xFFFFFFFF才对。写入数据说明有的位置变为0了。而从第一次擦除我们可以看的到全都是1,写入数据之前是没有问题。这说明我们的写入数据的函数是没有问题的,而且第一次写入之前也进行了擦除,而数据没有改变。是在我们写入了数据或者是现有数据的情况下擦除出现了错误,之后的打印数据全部改为了0,正常的擦除应该是1所以可以断定是擦除函数有问题。

于是开始大量掉发,疯狂找资料。参考了野火和原子的标准库例程,发现读写没有问题。参考了ST的HAL库例程也没有问题。发现他们都用了一个GetSector获取地址所在扇区的函数。而这个函数的调用是直接获取函数擦除的位置。根据一步步goto,goto,goto后。我终于找到了问题所在。

F4的擦除函数不同于F1和F3的擦除函数,虽然名字和结构体在hal库中仍然一样。但是F4中的起始地址,已经改为扇区的宏定义了。而我的这些代码,均是从我之前项目F373的程序中移植过来的。在373和103中,他的擦除页面起始位置均是地址。而我直接移植后没有因为名字没有变化所以我仍然以为是地址。而实际上应该是扇区的扇区码,在相关宏定义中可以找到。

关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第5张图片
修改过后的程序如下所示:
关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第6张图片

调用以后发现:

关于STM32F429ZIT6的IAP固件升级,基于HAL库FLASH擦除读写失败的问题以及解决方案。_第7张图片

AMAZING!!!成功完成擦读写的操作后。我感觉自己已经站在人生巅峰并且那是一个举止从容,行云流水,一顿操作猛如虎,一路火花带闪电,狂拽酷炫吊炸天,我就是那个站在宇宙巅峰的男人啊。

又是一段口嗨以后插一段简短的总结:

  • 在STM32中对FLASH的操作可以分为三种:擦,读,写。其中擦和写需要解锁FLASH,同时用完后需要上锁FLASH。
  • 每当要写数据之前,一定要执行擦除这个页面。同时,F4中擦除页面最小也是16kb一擦,所以注意一定要确保扇区没有任何程序以及数据,实在不行,先全部读取出来,擦除后再整体写入。当然这有一定的代码风险(读取后,在擦除时断电,会导致在读取在ROM里的数据全部丢失)
  • 这里以429ZI为例,在读写其他芯片FLASH的时候,也需要对FLASH的存储空间分布进行了解,最起码F1和F4的就有很大不同。

最后特别感谢@ tyustli 朋友给我的一些提示和帮助。

然后看了一下表,卧槽25点01分了,赶紧下班。回头瞟了一眼我桌子上那些黑色的头发,我知道那是我逝去的青春。

你可能感兴趣的:(单片机,嵌入式Linux)