【軟件設計】通過軟件設計提高 Flash 的擦寫次數

目錄

  • 0. 個人簡介 && 授權須知
  • 1. Flash 和 EEROM 基本情況
  • 2. 場景要求
  • 3. 軟件設計思路
  • 4. 代碼展示
    • 4.1 flash.h
    • 4.2 flash.c

0. 個人簡介 && 授權須知

image-20230911133730620

📋 個人簡介

  • 💖 作者簡介:大家好,我是喜歡記錄零碎知識點的菜鳥打工人。😎
  • 📝 個人主頁:歡迎訪問我的博客主頁🔥…
    • https://blog.csdn.net/qq_39217004?spm=1010.2135.3001.5343
  • 🎉 支持我:點贊👍+收藏??+留言📝
  • 📣 系列專欄:嵌入式Linux開發 🍁 🍁
  • 💬格言:寫文檔啊不是寫文章,重要的還是直白!🔥

轉載文章,禁止聲明原創;不允許直接二次轉載,轉載請根據原文鏈接聯系作者

若無需改版,在文首清楚標注作者及來源/原文鏈接,并刪除【原創聲明】,即可直接轉載。
但對于未注明轉載來源/原文鏈接的文章,我將保留追述的權利。

https://blog.csdn.net/qq_39217004?spm=1010.2135.3001.5343

作者:積跬步、至千里

image-20230911133724204

場景分析:
在嵌入式開發中,EEPROM 因其非易失性存儲特性,常用于保存配置參數等數據。EEPROM 的擦寫次數一般在10萬次左右。

單片機一般通過 IIC 接口外加一個 EEROM 存儲芯片

但是,有些小項目為了節省成本或者以前維護的一些項目,只能把 flash 劃分出一段空間,當數據存儲空間來用。

這樣的缺點是

  1. 升級固件時如果固件變大了,一旦覆蓋了原來劃分的數據存儲區域,那數據就沒了。
  2. Flash 的擦寫次數一般都是在 1萬次 左右,這個次數可是遠小于 EEROM

1萬次,在很多場景下并不夠。
因此本文參考
老板說:單片機,Flash模擬EEPROM,16字節,算法輪詢存儲給我做到100萬次的存儲次數 ,
的思路,通過軟件設計,提高flash的擦寫次數。

1. Flash 和 EEROM 基本情況

  1. 獨立的EEPROM芯片是可以直接寫字節的,即使覆蓋寫也無須擦除,
  2. 單片機的FLASH 寫入數據之前,必須按頁來擦除,先擦除再寫入

2. 場景要求

程序一共有15個字節的內容需要斷電保存,每改變其中一個字節就需要保存一次,做到100萬次的一個存儲次數。

單片機的 Flash 一共是 128KB ,從0x08000000----0x0801FFFF,一共64頁,每頁2KB。

3. 軟件設計思路

以每個 page 2KB 的大小為一個節點,假設數據要存儲到【最后一個page】,設計思路如下:

  1. 從第一個 page 開始寫入,第一次寫前16字節,然后更新寫入地址索引到第16個字節
  2. 第二次從 17-31 字節寫入,然后更新寫入地址索引到第32個字節,依次循環

這樣來算,每個 page 可以存儲 2048/16 = 128 次,寫滿一頁擦除一次,理論上存儲次數能達到128 × 1萬次/頁 = 128萬次。

4. 代碼展示

4.1 flash.h

#ifndef FLASH__H
#define FLASH__H#include "stm32g0xx_hal.h"
#include <string.h>// FLASH配置
#define FLASH_BASE_ADDR 0x0801F800 // FLASH最后一頁起始地址 (128KB - 2KB)
#define PAGE_SIZE 2048             // STM32G071頁面大小為2KB
#define STATE_SIZE 16             // 結構體大小(填充到24字節)typedef struct {unsigned int  color;                  // 顏色unsigned int  seconds;                // 秒數unsigned char mode;                   // 模式unsigned char number;                 // 序號unsigned char padinng[5];             // 預留5unsigned char checksum;               // 1字節,校驗和
}dataState;extern dataState old_state;
extern dataState current_state;
void printState(dataState *state);
HAL_StatusTypeDef flash_program(unsigned int addr, unsigned char* data, unsigned int len);
void read_flash(unsigned int addr, unsigned char* data, unsigned int len);
void init_flash_addr(void);
void save_state(dataState* state);
void get_state(dataState* state);
void update_state(dataState* state);#endif

4.2 flash.c

// 初始化:查找最新有效數據
void init_flash_addr(void) {dataState temp_state;uint32_t addr = FLASH_BASE_ADDR;uint32_t last_valid_addr = FLASH_BASE_ADDR;int found_valid_data = 0;while (addr < FLASH_BASE_ADDR + PAGE_SIZE) {read_flash(addr, (uint8_t*)&temp_state, STATE_SIZE);// 檢查是否全0xFFuint8_t all_ff[STATE_SIZE];memset(all_ff, 0xFF, STATE_SIZE);int is_all_ff = (memcmp(&temp_state, all_ff, STATE_SIZE) == 0);if (!is_all_ff && temp_state.checksum == calculate_checksum(&temp_state)) {last_valid_addr = addr;found_valid_data = 1;memcpy(&current_state, &temp_state, STATE_SIZE);        } else {     break;}addr += STATE_SIZE;}flash_addr = last_valid_addr + (found_valid_data ? STATE_SIZE : 0);if(found_valid_data)printf("init first,found valid data,last_valid_addr:%X\r\n",last_valid_addr);elseprintf("init first,it is all ff,it is the first data\r\n");if (flash_addr > FLASH_BASE_ADDR + PAGE_SIZE)     {printf("init erase page 2KB\r\n");erase_page(FLASH_BASE_ADDR);flash_addr = FLASH_BASE_ADDR;}if (!found_valid_data) {printf("not found valid data\r\n");current_state.color = 100;current_state.seconds = 200;current_state.mode = 1;current_state.number = 1;current_state.checksum = calculate_checksum(&current_state);__disable_irq(); flash_program(FLASH_BASE_ADDR, (uint8_t*)&current_state, STATE_SIZE);__enable_irq(); flash_addr = FLASH_BASE_ADDR + STATE_SIZE;       }printState(&current_state);
}
  1. 第一步,先從第一個地址讀取第一個16字節,然后判斷是不是全部等于0xFF,如果第一次是就證明是第一次,下一步flash_addr就不需要增加STATE_SIZE,寫入地址索引就是FLASH_BASE_ADDR
  2. 第二步,如果不全是0xFF并且校驗字節通過,證明這是一組有效數據,我們先將此數據更新到current_state,但是這里還不能證明是最后一組有效數據,因為最后一組有效數據才是我們要找到的數據。
  3. 繼續檢查下一組數據,直到檢查到一組數據是全0xFF,證明上一組數據就是最后一組有效數據,就跳出,此時我們也就找到了最后一組有效數據的起始地址。
  4. 此地址增加STATE_SIZE就是最新可以存儲數據的地址索引了。
  5. 如果沒找到有效數據,證明是第一次,就寫入默認數值并保存,更新索引。

保存數據save_state函數:

oid save_state(dataState* state) {dataState last_state;if (flash_addr > FLASH_BASE_ADDR) {read_flash(flash_addr - STATE_SIZE, (uint8_t*)&last_state, STATE_SIZE);if (memcmp(&last_state, state, STATE_SIZE) == 0) {printf("數據沒有變化,直接返回");return; }}__disable_irq(); if (flash_addr + STATE_SIZE > FLASH_BASE_ADDR + PAGE_SIZE) {printf("erase page 2KB\r\n");erase_page(FLASH_BASE_ADDR);flash_addr = FLASH_BASE_ADDR;   }state->checksum = calculate_checksum(state);flash_program(flash_addr, (uint8_t*)state, STATE_SIZE);flash_addr += STATE_SIZE; __enable_irq(); 
}

函數就比較簡單了,首先將要保存的數據與最新存儲的數據做對比,如果沒變化,就不操作;如果地址超出范圍了,就先擦除整個頁,更新寫索引到FLASH_BASE_ADDR,接著保存數據到當下最新寫地址索引即可。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907564.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907564.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907564.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OpenCV CUDA模塊直方圖計算------在 GPU 上計算輸入圖像的直方圖(histogram)函數histEven()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該函數用于在 GPU 上計算輸入圖像的直方圖&#xff08;histogram&#xff09;。它將像素值區間均勻劃分為若干個 bin&#xff08;桶&#xff09;…

龍虎榜——20250530

上證指數陽包陰&#xff0c;量能較前期下跌有放大&#xff0c;但個股跌多漲少&#xff0c;下跌超過4000個。 深證指數和上漲總體相同。 2025年5月30日龍虎榜行業方向分析 1. 醫藥&#xff08;創新藥原料藥&#xff09; 代表標的&#xff1a;華納藥廠、舒泰神、睿智醫藥、華…

HarmonyNext使用request.agent.download實現斷點下載

filedownlaod(API12) &#x1f4da;簡介 filedownload 這是一款支持大文件斷點下載的開源插件&#xff0c;退出應用程序進程殺掉以后或無網絡情況下恢復網絡后&#xff0c;可以在上次位置繼續恢復下載等 版本更新—請查看更新日志!!! 修復已知bug,demo已經更新 &#x1f4d…

nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: 80端口被占用

Nginx啟動報錯&#xff1a;nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions) 這個報錯代表80端口被占用 先查看占用80的端口 netstat -aon | findstr :80 把它殺掉&#xff0c;強…

embbeding 視頻截圖

Embedding是什么&#xff1f;有什么作用&#xff1f;是怎么得到的&#xff1f;_嗶哩嗶哩_bilibili

服務器tty2終端如何關機

在服務器的 tty2 或其他虛擬終端上&#xff0c;要安全地進行關機操作&#xff0c;可以使用以下命令之一&#xff1a; 1.1 使用 shutdown 命令&#xff1a; shutdown 命令可以計劃系統關機。默認需要超級用戶權限。 sudo shutdown -h now-h 選項表示關機&#xff08;halt&…

時序數據庫IoTDB啟動方式及集群遷移指南

IoTDB啟動方式 IoTDB在配置啟動時有兩種推薦方式&#xff1a; ?主機名啟動?&#xff1a; ?推薦理由?&#xff1a;主機名啟動方式更為靈活&#xff0c;便于在不同網絡環境中部署相同的IoTDB實例。?工作原理?&#xff1a;IoTDB啟動后會維護一張節點編號與網絡地址的映射表…

如何在Qt中繪制一個帶有動畫的弧形進度條?

如何在Qt中繪制一個弧形的進度條 在圖形用戶界面開發中&#xff0c;進度指示控件&#xff08;Progress Widget&#xff09;是非常常見且實用的組件。CCArcProgressWidget 是一個繼承自 QWidget 的自定義控件&#xff0c;用于繪制圓弧形進度條。當然&#xff0c;筆者看了眼公開…

在 Mac 下 VSCode 中的終端使用 option + b 或 f 的快捷鍵變成輸入特殊字符的解決方案

前言 在終端里&#xff0c;我們可以使用 option b 和 option f 來在我們輸入的命令中進行快速的前后調整光標&#xff0c;但是&#xff0c;在未設置的情況下&#xff0c;在 MacOS 中&#xff0c;會變成輸入特殊字符。 普通鍵盤上是 alt b 和 alt f &#xff0c;只是叫法不…

Android bindservice綁定服務,并同步返回service對象的兩個方法

先上一段代碼&#xff1a; private IDeviceService deviceService null; private ServiceConnection connnull; private synchronized void bindyourservice() { Intent intent new Intent();intent.setPackage("servicepackagename");intent.setAction("…

Go語言之空接口與類型斷言

Go 語言中&#xff0c;接口是一種強大的抽象機制。其中&#xff0c;空接口&#xff08;interface{}&#xff09;和類型斷言為我們提供了處理任意類型與類型檢查的能力。 一、空接口&#xff08;interface{}&#xff09; 空接口是 Go 中最特殊的接口&#xff1a;不包含任何方法…

三、OrcaSlicer預設顯示

一、界面類 主框架使用的是wxWidgets庫&#xff1b;3D模型的渲染區的控件&#xff0c;使用的是imgui庫。 1、Plater 此類在OrcaSlicer\src\slic3r\GUI\Plater.hpp文件中定義 1.1 Plater::priv 此結構體是Plater的數據類&#xff0c;各種數據的對象和指針保存在此結構體中。如…

00 QEMU源碼中文注釋與架構講解

QEMU源碼中文注釋與架構講解 先占坑&#xff1a;等后續完善后再更新此文章 注釋作者將狼才鯨創建日期2025-05-30更新日期NULL CSDN閱讀地址&#xff1a;00 QEMU源碼中文注釋與架構講解Gitee源碼倉庫地址&#xff1a;才鯨嵌入式/qemu 一、前言 參考網址 QEMU 源碼目錄簡介qe…

一、Sqoop歷史發展及原理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月30日 專欄&#xff1a;Sqoop教程 在大數據時代&#xff0c;數據往往分散存儲在各種不同類型的系統中。其中&#xff0c;傳統的關系型數據庫 (RDBMS) 如 MySQL, Oracle, PostgreSQL 等&#xff0c;仍然承載著大量的關鍵業務…

【Halcon】圖像分割中的 regiongrowing 與dyn_threshold 動態閾值 算法詳解對比

圖像分割中的 regiongrowing 與動態閾值算法詳解對比 在使用 HALCON 進行圖像處理時&#xff0c;圖像分割是最常見也最關鍵的操作之一。本文將深入講解 regiongrowing 算子的原理與使用方法&#xff0c;并與另一常見方法——動態閾值 (dyn_threshold) 進行詳細對比&#xff0c…

Docker部署項目無法訪問,登錄超時完整排查攻略

項目背景&#xff1a;遷移前后端應用&#xff0c;prod環境要求保留443端口&#xff0c;開發環境37800端口&#xff0c;后端容器端口為8000&#xff0c;前端為80&#xff0c;fastAPI對外端口為41000 生產環境部署在VM01,開發環境部署在VM03&#xff0c;在VM01配置nginx轉發 [r…

充電便捷,新能源汽車移動充電服務如何預約充電

隨著新能源汽車的普及&#xff0c;充電便捷性成為影響用戶體驗的關鍵因素之一。傳統的固定充電樁受限于地理位置和數量&#xff0c;難以完全滿足用戶需求&#xff0c;而移動充電服務的出現&#xff0c;為車主提供了更加靈活的補能方式。通過手機APP、小程序或在線平臺&#xff…

探索C++標準模板庫(STL):從容器到底層奧秘-全面解析String類高效技巧(上篇)

前引&#xff1a;在現代軟件開發中&#xff0c;字符串處理是幾乎所有程序的核心需求之一。無論是文本解析、網絡通信&#xff0c;還是用戶交互&#xff0c;高效且安全的字符串操作能力直接決定了代碼的質量與可維護性。而C標準模板庫&#xff08;Standard Template Library, ST…

Python爬蟲實戰:抓取百度15天天氣預報數據

&#x1f310; 編程基礎第一期《9-30》–使用python中的第三方模塊requests&#xff0c;和三個內置模塊(re、json、pprint)&#xff0c;實現百度地圖的近15天天氣信息抓取 記得安裝 pip install requests&#x1f4d1; 項目介紹 網絡爬蟲是Python最受歡迎的應用場景之一&…

HTML常見事件詳解:從入門到實戰應用

前言 在Web開發中&#xff0c;事件是用戶與網頁交互的核心機制。HTML事件讓我們能夠響應用戶的各種操作&#xff0c;如點擊、鼠標移動、鍵盤輸入等。掌握HTML事件是前端開發的基礎技能之一&#xff0c;本文將深入探討HTML中的常見事件類型及其實際應用。 HTML事件概覽總結 H…