<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > STM32 使用 Flash 存儲數(shù)據(jù)時的一種管理辦法

          STM32 使用 Flash 存儲數(shù)據(jù)時的一種管理辦法

          作者: 時間:2016-11-13 來源:網(wǎng)絡(luò) 收藏
          使用 stm32f3xx,需要存儲一些掉電不丟失的校準信息,查閱手冊得知:1、stm32 寫 flash 的長度是固定的 16bit;2、擦除時必須整塊(2Kbytes)擦除,給出某 flash 塊內(nèi)的地址,執(zhí)行擦除命令就可以了;3、參考手冊給出了最小擦寫次數(shù)為 10K。
          以上三點對于實際使用時的影響,首先,寫數(shù)據(jù)必須以 16bit 為單位,很多 32bit 長度的值就不能直接使用類似 A = B 的賦值語句的方法去操作了,可以統(tǒng)一轉(zhuǎn)化為指向 16bit 無符號整形值的指針來處理。舉例,有一個 32bit 長度的 float 變量 v_float,要存入地址為 (FLASH_ADDRESS)的 flash中:
          #define FLASH_ADDRESS (0x0803F800)
          if(FLASH->CR &= FLASH_CR_LOCK) //解鎖flash
          {
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
          while(FLASH->CR & FLASH_CR_LOCK);
          }
          while(FLASH->SR &= FLASH_SR_BSY);
          FLASH->CR |= FLASH_CR_PG; //功能選擇,寫flash
          {
          *((unsigned short *)(FLASH_ADDRESS) + 0 ) = *((unsigned short *)(&v_float) + 0 );
          *((unsigned short *)(FLASH_ADDRESS) + 1 ) = *((unsigned short *)(&v_float) + 1 );
          }
          FLASH->CR &= ~FLASH_CR_PG;
          FLASH->CR |= FLASH_CR_LOCK; //鎖flash
          如果需要讀取寫入到 flash 中的浮點數(shù)到 ram 中的變量 a_float,使用如下語句:
          a_float = *((float *) FLASH_ADDRESS);
          這樣,寫入和讀取就完成了,下面是擦除,按照參考手冊的流程來就可以了:
          if(FLASH->CR &= FLASH_CR_LOCK) //解鎖flash
          {
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
          while(FLASH->CR & FLASH_CR_LOCK);
          }
          while(FLASH->SR &= FLASH_SR_BSY); //Wait until flash not busy
          FLASH->CR |= FLASH_CR_PER; //功能選擇,擦除頁
          FLASH->AR = FLASH_ADDRESS; //寫入flash地址
          FLASH->CR |= FLASH_CR_STRT; //開始擦除
          while(FLASH->SR &= FLASH_SR_BSY); //等待
          if(FLASH->SR &= FLASH_SR_EOP)
          {
          FLASH->SR |= FLASH_SR_EOP; //重置EOP
          }
          FLASH->CR &= ~FLASH_CR_PER;
          FLASH->CR |= FLASH_CR_LOCK; //鎖flash
          以上代碼會擦除 FLASH_ADDRESS 所在的整個 flash 頁。
          實現(xiàn)少量數(shù)據(jù)的讀寫和擦除操作以后,下一步要開始組織 flash 中存儲的數(shù)據(jù),這樣以后閱讀和修改都更為方便,我使用類似 stm32 官方寄存器配置文件的方式,用結(jié)構(gòu)體來組織。假設(shè)我要將某個人的個人信息儲存在 flash 中地址為(FLASH_ADDRESS)的位置,包括:姓名、性別、身高、體重,代碼如下:
          typedef struct //構(gòu)造結(jié)構(gòu)體
          {
          unsigned char name[16];
          unsigned char male;
          float height;
          float weight;
          }Personal_Information_TypeDef;
          #define FLASH_ADDRESS (0x0803F800) //flash地址
          #define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
          以上代碼在起始地址為(FLASH_ADDRESS)的 flash 中,定義了用于儲存?zhèn)€人信息的結(jié)構(gòu)體指針 Personal_Data,也就是說 Personal_Data 這個指針的值就是 FLASH_ADDRESS,只不過除了這個指針的值以外,我們還定義了這個指針所指向的數(shù)據(jù)的結(jié)構(gòu)。要讀出這些儲存于 flash 中的值,使用讀取結(jié)構(gòu)體指針的方式:
          Temp_variable = Personal_Data -> male;
          寫 flash 的時候,因為每次只能寫 16bit,所以除了 short 類型以外,類似 int 和 float 這種 32bit 的數(shù)據(jù),都要取地址強制轉(zhuǎn)化為 16bit 類型后再取值最后寫入,方法類似一開始的 float 類型數(shù)據(jù)寫入 flash 的操作。我為了操作方便,在 ram 中建立了一個和 flash 內(nèi)結(jié)構(gòu)相同的結(jié)構(gòu)體,每次需要寫入 flash 的時候,就將 ram 中結(jié)構(gòu)體的所有值全部寫入 flash:
          typedef struct //構(gòu)造結(jié)構(gòu)體
          {
          unsigned char name[16];
          unsigned char male;
          float height;
          float weight;
          }Personal_Information_TypeDef;
          #define Length (5) //結(jié)構(gòu)體總長(16bit單位)
          #define FLASH_ADDRESS (0x0803F800) //flash地址
          #define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
          Personal_Information_TypeDef Personal_Data_Mirror; //ram中的結(jié)構(gòu)體
          /* 寫入過程 */
          (unsigned short *)WriteAddress = (unsigned short *)Personal_Data;
          (unsigned short *)ReadAddress = (unsigned short *)(&Personal_Data_Mirror);
          if(FLASH->CR &= FLASH_CR_LOCK) //解鎖flash
          {
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY1);
          FLASH->KEYR = (FLASH_KEYR_FKEYR & FLASH_KEY2);
          while(FLASH->CR & FLASH_CR_LOCK);
          }
          FLASH->CR |= FLASH_CR_PG; //功能選擇,寫入
          while(Length > 0)
          {
          *WriteAddress = *ReadAddress; //Ram to Flash program, 16bit each
          while(FLASH->SR &= FLASH_SR_BSY);
          WriteAddress += 1;
          ReadAddress += 1;
          Length -= 1;
          }
          FLASH->CR &= ~FLASH_CR_PG; //Clear PG bit
          FLASH->CR |= FLASH_CR_LOCK; //Lock flash
          以上的寫入過程之前,必須確保要寫入的 flash 位置首先擦除過,或者說要保證要寫入數(shù)據(jù)的地方的值為0xFFFFFFFF,否則無法寫入,硬件會有標志位來報錯。
          這樣以結(jié)構(gòu)體為單位擦寫 flash 的好處是,如果需要修改要儲存的數(shù)據(jù)數(shù)量或類型的話,只需要修改結(jié)構(gòu)體定義就可以了,而且用結(jié)構(gòu)體來管理變量,程序的可讀性較好。
          最后就是 flash 的擦寫次數(shù)問題了,最少10k次的擦寫壽命,對于某些需要頻繁更新的內(nèi)容還是太少了,比EEPROM 通常的 100k 少了一個數(shù)量級,而且即使是改動一個變量,也必須首先擦除整個 flash 塊,更加速了 flash 的消耗。但是 stm32 的 flash 容量還是不錯的,動輒 256Kbytes,所以我們可以用容量換壽命,
          具體思路就是不要在同一個地址重復(fù)擦寫,寫的時候不停的變換地址,寫滿以后再擦除。比如,需要儲存的結(jié)構(gòu)體長度為 20x16bit,那么一個 2Kbytes 的 flash 頁就可以儲存 50 個相同的結(jié)構(gòu)體,那么執(zhí)行完 50 次寫操作以后才需要執(zhí)行一次擦除操作,flash 的使用壽命隨之大為延長。
          還是以儲存一個結(jié)構(gòu)體為例說明如何實現(xiàn)這種儲存方式,首先定義結(jié)構(gòu)體,除了你需要儲存的數(shù)據(jù)以外,還要額外增加一個變量,用于識別你當前讀寫的 flash 地址:
          typedef struct //構(gòu)造結(jié)構(gòu)體
          {
          unsigned char flag; //用于識別當前地址的標記
          unsigned char name[16];
          unsigned char male;
          float height;
          float weight;
          }Personal_Information_TypeDef;
          #define Length (6) //結(jié)構(gòu)體總長(16bit單位)
          #define FLASH_ADDRESS (0x0803F800) //flash地址
          #define Personal_Data ((Personal_Information_TypeDef *) FLASH_ADDRESS )
          Personal_Information_TypeDef Personal_Data_Mirror; //ram中的結(jié)構(gòu)體
          這部分除了結(jié)構(gòu)體中增加了一個標記(flag)變量以外,其它部分相同,但是思想上,我們其實是在 flash 中定義了一個結(jié)構(gòu)體數(shù)組,只不過沒有使用通常的[]來遍歷數(shù)組變量,取而代之的是直接使用指針來操作。每次寫入時,將 flag 變量固定寫為 0x00。需要讀取 flash 數(shù)據(jù)時,就可以根據(jù)標記變量 flag 的值找到最新的 flash 數(shù)據(jù)地址:
          #define FLASH_ADDRESS_MAX; //最大偏移量,防止跨區(qū)塊操作
          unsigned short FlashAddress_Offset = 0; //用于儲存flash地址偏移量的臨時變量
          while( (Personal_Data + FlashAddress_Offset) -> flag == 0x00)
          {
          FlashAddress_Offset += 1;
          if( (FlashAddress_Offset + FlashAddress_Offset) > MAX_OFFSET)
          {
          break;
          }
          }
          找到寫有數(shù)據(jù)的 flash 地址以后,后繼的寫操作和讀操作和單個結(jié)構(gòu)體的操作相似,寫的地址變?yōu)椋?Personal_Data + FlashAddress_Offset)
          讀的地址是:
          (Personal_Data + FlashAddress_Offset - 1)
          具體實現(xiàn)時要注意,結(jié)構(gòu)體的長度要算好,不能出現(xiàn)兩個結(jié)構(gòu)體交叉寫入;擦除 flash 需要時間,此間最好不進行需要讀取 flash 的操作。



          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();