[STM32F1] 关于STM32对FLASH的设计思路

[复制链接]
2177|11
 楼主| lando 发表于 2020-3-19 12:15 | 显示全部楼层 |阅读模式
我这种存储方式不知道有没有改进的空间,请大家帮忙指正。

W25Q128将16M的容量分为256个块(Block),每个块大小为64K字节,每个块又分为
16个扇区(Sector),每个扇区4K个字节。W25Q128的最小擦除单位为一个扇区,也就是每次
必须擦除4K个字节。

所以我是按照扇区为单位存储数据的。

第一个扇区为配置信息数据,第二个扇区为升级程序(SPI远程升级用的)长度。第三、四个扇区为升级程序。第五个扇区存储离线数据占的扇区个数(后简称扇区标识位),第六个扇区以后存储离线数据。

其实前面4个扇区不用说了,关键是离线数据那块想了好久,才出了这个解决方案。

应用场景:
如果出现通信不畅的问题,需要将GPS信息缓存到flash里,等通信正常以后再传出去。

我想的是以扇区为单位进行存储,因为如果缓存数据量很庞大,我可以一个扇区一个扇区进行读,发送,擦除操作,避免通信通道被大量离线数据拥堵。

解决方法:
1. 第五个扇区存储离线数据占的扇区个数,也就是最后一个未满扇区的标志位。这样数据存进来直接根据这个扇区获取到可以存放的扇区基地址。

2. 找到这个扇区基地址以后,要先查前四位,这前四位为此扇区的数据长度。如果存入数据的长度加此扇区的数据长度大于扇区长度,那么就要新开辟一块扇区,并且扇区标识位要加1。如果没超过,将整个扇区取出,修改数据长度,末尾追加新数据。

3. 取数据的时候,也是先去查询扇区标识位然后得到最后数据的扇区地址,取出此扇区数据,格式化此扇区,修改扇区标识位。属于先进后出的方式(因为GPS信息有时间,所以不在乎顺序问题)

4. 数据存满了怎么办,当再次开辟新扇区时,发现扇区标识位+1 大于FLASH扇区个数或者设计离线数据占用扇区个数。那么开辟新扇区会直接返回数据的最后一块扇区,擦除以后再将新数据存入。相当于如果数据太大,就只会保存头数据和尾数据,而把中间的数据丢弃掉。

大家发现有设计不合理的,帮忙指正!谢谢!
21ic小喇叭 发表于 2020-3-20 09:48 | 显示全部楼层
楼主把自己的问题、思路、尝试过的办法,写的都比较详细,希望大家看到后能尽快帮楼主解决问题~~~
21ic小喇叭 发表于 2020-3-20 09:49 | 显示全部楼层
奖励您10家园币,家园币是我们新上线的产品,您可以兑换礼品~
 楼主| lando 发表于 2020-3-20 20:39 | 显示全部楼层
既然被推荐了,那我就把昨天调了一晚上的核心代码部分传上来。已经测试通过了,非常好用。
  1.                 SPI_FLASH_SectorErase(FLASH_OFFLINE_LENGTH);
  2.                 SPI_FLASH_SectorErase(FLASH_OFFLINE_DATA);
  3.                 offlineLengthInit();
  4.                 char data[] = "0102030405040304938473625364738473645263748594837463547182736475847364";
  5.                 int index = 60;
  6.                 while(index > 0 )
  7.                 {       
  8.                         writeOfflineData(data, strlen(data));
  9.                         index--;
  10.                 }
  11.                
  12.                 readOfflineData();
  13.                 readOfflineData();
  14.                 readOfflineData();

  1.         //初始化离线数据扇区
  2. void offlineDataInit(int data_index)
  3. {       
  4.         SPI_FLASH_SectorErase(W25Q128FV_SUBSECTOR_SIZE * data_index);
  5.         uint8_t data_length[5] = {0};
  6.         sprintf((char*)data_length, "%04x", 0);
  7.         SPI_FLASH_BufferWrite(data_length, W25Q128FV_SUBSECTOR_SIZE * data_index, 4);
  8. }

  9. // 初始化离线数据标识扇区
  10. void offlineLengthInit(void)
  11. {
  12.         uint8_t data_index_length[5] = {0};
  13.         SPI_FLASH_SectorErase(FLASH_OFFLINE_LENGTH);
  14.         sprintf((char*)data_index_length, "%04x", 6);
  15.         SPI_FLASH_BufferWrite(data_index_length, FLASH_OFFLINE_LENGTH, 4);
  16.        
  17.         offlineDataInit(6);
  18. }

  19. // 修改离线数据标识
  20. u32 newOfflineLength(int d_index)
  21. {       
  22.         if(d_index < 6 ){
  23.                 d_index = 6;
  24.         }
  25.         if(d_index > 256 ){
  26.                 d_index = 256;
  27.         }
  28.        
  29.         printf("\r\n newOfflineLength: %d \r\n", d_index);
  30.         uint8_t data_index[5] = {0};
  31.         SPI_FLASH_SectorErase(FLASH_OFFLINE_LENGTH);
  32.         sprintf((char*)data_index, "%04x", d_index);
  33.         SPI_FLASH_BufferWrite(data_index, FLASH_OFFLINE_LENGTH, 4);
  34.        
  35.         return W25Q128FV_SUBSECTOR_SIZE * d_index;
  36. }

  1. void readOfflineData(void)
  2.         {
  3.         // 查询离线数据标识确定最后数据地址
  4.         uint8_t data_index[5] = {0};
  5.         SPI_FLASH_BufferRead(data_index, FLASH_OFFLINE_LENGTH, 4);
  6.         int d_index = charhex_to_dec((char*)data_index);
  7.         printf("\r\n readOfflineData: %d \r\n", d_index);
  8.         u32 data_address = W25Q128FV_SUBSECTOR_SIZE * d_index;
  9.        
  10.         // 查询离线数据标识扇区内数据长度标识
  11.         uint8_t data_length[5] = {0};
  12.         SPI_FLASH_BufferRead(data_length, data_address, 4);
  13.         int d_length = charhex_to_dec((char*)data_length);
  14.        
  15.         SPI_FLASH_BufferRead(read_data, data_address + 4, d_length);
  16.         SPI_FLASH_SectorErase(data_address);
  17.         if(d_index > 6){
  18.                 newOfflineLength(d_index - 1);
  19.         }

  20.         printf("\r\n 读出长度为:%d \r\n", d_length);
  21.         printf("\r\n 读出的数据为:%s \r\n", read_data);
  22.         printf("\r\n 读出长度为:%d \r\n", strlen((char*)read_data));
  23.         memset(read_data, 0, 4069);
  24. }

  1. void writeOfflineData(char* data, int dataLength)
  2.         {

  3.         // 查询离线数据标识确定最后数据地址
  4.         uint8_t data_index[5] = {0};
  5.         SPI_FLASH_BufferRead(data_index, FLASH_OFFLINE_LENGTH, 4);
  6.         int d_index = charhex_to_dec((char*)data_index);
  7.         u32 data_address = W25Q128FV_SUBSECTOR_SIZE * d_index;
  8.        
  9.         // 查询离线数据标识扇区内数据长度标识
  10.         uint8_t data_length[5] = {0};
  11.         SPI_FLASH_BufferRead(data_length, data_address, 4);
  12.         int d_length = charhex_to_dec((char*)data_length);
  13.        
  14.        
  15.         // 如果新数据+扇区数据长度>扇区长度
  16.         if((dataLength + d_length) > W25Q128FV_SUBSECTOR_SIZE){
  17.                 // 开辟新扇区
  18.                 data_address = newOfflineLength(d_index + 1);
  19.                 SPI_FLASH_SectorErase(data_address);
  20.                
  21.                
  22.                 sprintf((char*)write_data, "%04x%s", dataLength, data);
  23.                 SPI_FLASH_BufferWrite(write_data, data_address,  strlen((char*)write_data));
  24.                 printf("\r\n 写入的数据为:%s \r\t", write_data);
  25.                 printf("\r\n 长度:%d \r\t", strlen((char*)write_data));
  26.                
  27.         } else if(d_length == 0)
  28.         {
  29.                 SPI_FLASH_SectorErase(data_address);
  30.                        
  31.                 sprintf((char*)write_data, "%04x%s", dataLength, data);
  32.                 SPI_FLASH_BufferWrite(write_data, data_address, strlen((char*)write_data));
  33.         } else
  34.         {
  35.                 // 将旧数据读出,修改扇区数据长度,末尾追加新数据
  36.                 SPI_FLASH_BufferRead(read_data, data_address + 4, d_length);
  37.                 SPI_FLASH_SectorErase(data_address);
  38.                
  39.                 sprintf((char*)write_data, "%04x%s%s%s", dataLength + d_length + 1, read_data, ",", data);

  40.                 SPI_FLASH_BufferWrite(write_data, data_address, strlen((char*)write_data));
  41.         }
  42.         memset(write_data, 0, 4069);
  43. }


只只是个DEMO,还没往产品里移植呢,如果需要完整代码的,可以找我要。
花自向阳开 发表于 2020-3-21 08:42 | 显示全部楼层
你是BIOS RD嗎
花自向阳开 发表于 2020-3-21 08:52 | 显示全部楼层
STM32L452CCU6-C01 ST UFQFPN48这款呼吸灯控制芯片的FW有写过吗?
 楼主| lando 发表于 2020-3-23 11:28 | 显示全部楼层
花自向阳开 发表于 2020-3-21 08:52
STM32L452CCU6-C01 ST UFQFPN48这款呼吸灯控制芯片的FW有写过吗?

现在在研究L系列
晓伍 发表于 2020-4-6 18:12 | 显示全部楼层
非常感谢楼主分享
八层楼 发表于 2020-4-6 18:23 | 显示全部楼层

非常感谢楼主分享
观海 发表于 2020-4-6 18:23 | 显示全部楼层
非常感谢楼主分享
guanjiaer 发表于 2020-4-6 18:23 | 显示全部楼层
非常感谢楼主分享
heimaojingzhang 发表于 2020-4-6 18:24 | 显示全部楼层
非常感谢楼主分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

15

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部