STM32F4讀寫內部FLASH【使用庫函數】

STM32F4Discovery開發幫使用的STM32F407VGT6芯片,內部FLASH有1M之多。平時寫的代碼,燒寫完之後還有大量的剩餘。有效利用這剩餘的FLASH能存儲不少數據。因此研究了一下STM32F4讀寫內部FLASH的一些操作。

【STM32F4 內部Flash的一些信息】

STM32F407VG的內部FLASH的地址是:0x08000000,大小是0x00100000。

寫FLASH的時候,如果發現寫入地址的FLASH沒有被擦出,數據將不會寫入。FLASH的擦除操作,只能按Sector進行。不能單獨擦除一個地址上的數據。因此在寫數據之前需要將地址所在Sector的所有數據擦除。

在STM32F4的編程手冊上可找到FLASH的Sector劃分,我們現在只操作Main memory:

image

參考Demo中的例子,將FLASH的頁的其實地址(基地址)可定義如下:

/* Base address of the Flash sectors */
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */

在庫裏邊,FLASH的Sector編號定義如下,這是供庫裏邊的幾個函數使用的。需要將地址轉換成Sector編號:

#define FLASH_Sector_0     ((uint16_t)0x0000) /*!< Sector Number 0 */
#define FLASH_Sector_1     ((uint16_t)0x0008) /*!< Sector Number 1 */
#define FLASH_Sector_2     ((uint16_t)0x0010) /*!< Sector Number 2 */
#define FLASH_Sector_3     ((uint16_t)0x0018) /*!< Sector Number 3 */
#define FLASH_Sector_4     ((uint16_t)0x0020) /*!< Sector Number 4 */
#define FLASH_Sector_5     ((uint16_t)0x0028) /*!< Sector Number 5 */
#define FLASH_Sector_6     ((uint16_t)0x0030) /*!< Sector Number 6 */
#define FLASH_Sector_7     ((uint16_t)0x0038) /*!< Sector Number 7 */
#define FLASH_Sector_8     ((uint16_t)0x0040) /*!< Sector Number 8 */
#define FLASH_Sector_9     ((uint16_t)0x0048) /*!< Sector Number 9 */
#define FLASH_Sector_10    ((uint16_t)0x0050) /*!< Sector Number 10 */
#define FLASH_Sector_11    ((uint16_t)0x0058) /*!< Sector Number 11 */

Demo中有將地址轉換成Sector的代碼:

uint32_t GetSector(uint32_t Address)
{
  uint32_t sector = 0;
  
  if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
  {
    sector = FLASH_Sector_0;  
  }
  else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
  {
    sector = FLASH_Sector_1;  
  }
  else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
  {
    sector = FLASH_Sector_2;  
  }
  else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
  {
    sector = FLASH_Sector_3;  
  }
  else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
  {
    sector = FLASH_Sector_4;  
  }
  else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
  {
    sector = FLASH_Sector_5;  
  }
  else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
  {
    sector = FLASH_Sector_6;  
  }
  else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
  {
    sector = FLASH_Sector_7;  
  }
  else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
  {
    sector = FLASH_Sector_8;  
  }
  else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
  {
    sector = FLASH_Sector_9;  
  }
  else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
  {
    sector = FLASH_Sector_10;  
  }
  else/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11))*/
  {
    sector = FLASH_Sector_11;  
  }

  return sector;
}

 

有了這些定義之後,我們就可以開始正式操作FLASH了

首先,要向FLASH寫入數據需要先將FLASH解鎖。根據手冊定義,解鎖FLASH需要先向寄存器FLASH_KEYR寫入0x45670123之後再向這個寄存器寫入0xCDEF89AB。這兩個數據在庫中已經定義成了:FLASH_KEY1和FLASH_KEY2.

使用庫函數不用這麼麻煩,函數FLASH_Unlock()即可完成對FLASH的解鎖。

解鎖FLASH之後,使用函數FLASH_ClearFlag清除FLASH的狀態寄存器。然後就可以對FLASH進行寫操作了。我按照示例工程,擦除兩塊FLASH。

下邊是操作FLASH的代碼,首先擦除兩塊FLASH,然後向這兩塊FLASH中寫入數據。最後進行校驗:

要寫入的數據定義:

#define DATA_32                 ((uint32_t)0x12345678)

開始FLASH操作:

  FLASH_Unlock(); //解鎖FLASH後才能向FLASH中寫數據。


  FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
                  FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);

  /* Get the number of the start and end sectors */
  StartSector = GetSector(FLASH_USER_START_ADDR);  //獲取FLASH的Sector編號
  EndSector = GetSector(FLASH_USER_END_ADDR);
  
  //擦除FLASH
  for (i = StartSector; i < EndSector; i += 8)  //每次FLASH編號增加8,可參考上邊FLASH Sector的定義。
  {
    /* Device voltage range supposed to be [2.7V to 3.6V], the operation will
       be done by word */ 
    if (FLASH_EraseSector(i, VoltageRange_3) != FLASH_COMPLETE)
    { 
      while (1)
      {
      }
    }
  }
  
  /*擦除完畢*/
  /*開始寫入*/
  Address = FLASH_USER_START_ADDR;
  
    while (Address < FLASH_USER_END_ADDR)
  {
    if (FLASH_ProgramWord(Address, DATA_32) == FLASH_COMPLETE)   //將DATA_32寫入相應的地址。
    {
      Address = Address + 4;
    }
    else
    { 
      /* Error occurred while writing data in Flash memory. 
         User can add here some code to deal with this error */
      while (1)
      {
      }
    }
  }
  
  FLASH_Lock();  //讀FLASH不需要FLASH處於解鎖狀態。
  
//讀出數據 檢查寫入值是否正確
  Address = FLASH_USER_START_ADDR;
  MemoryProgramStatus = 0x0;
   while (Address < FLASH_USER_END_ADDR)
  {
    data32 = *(__IO uint32_t*)Address;   //讀FLASH中的數據,直接給出地址就行了。跟從內存中讀數據一樣。

    if (data32 != DATA_32)
    {
      MemoryProgramStatus++;  
    }

    Address = Address + 4;
  }  
 

下邊是使用STLink Utility讀出的數據,檢查一下,確實寫進去數據了:

image

參考文檔是ST的 STM32F40xxx and STM32F41xxx Flash programming manual。可在ST網站下載。文檔編號:PM0081。FLASH的有不少寄存器,各個含義手冊上有詳細介紹。我只是簡單地看了下。使用庫函數操作,好像不大需要詳細理解這些寄存器了。

 

PS:這個實驗主要代碼來自ST的Demo。這裏我只是加入了個人的註釋。不當之處,望高人指點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章