STM32內部FLASH介紹


一、介紹

首先我們需要了解一個內存映射: 

stm32的flash地址起始於0x0800 0000,結束地址是0x0800 0000加上芯片實際的flash大小,不同的芯片flash大小不同。

RAM起始地址是0x2000 0000,結束地址是0x2000 0000加上芯片的RAM大小。不同的芯片RAM也不同。

Flash中的內容一般用來存儲代碼和一些定義為const的數據,斷電不丟失, 
RAM可以理解為內存,用來存儲代碼運行時的數據,變量等等。掉電數據丟失。

STM32將外設等都映射為地址的形式,對地址的操作就是對外設的操作。 
stm32的外設地址從0x4000 0000開始,可以看到在庫文件中,是通過基於0x4000 0000地址的偏移量來操作寄存器以及外設的。

一般情況下,程序文件是從 0x0800 0000 地址寫入,這個是STM32開始執行的地方,0x0800 0004是STM32的中斷向量表的起始地址。 
在使用keil進行編寫程序時,其編程地址的設置一般是這樣的: 

程序的寫入地址從0x08000000(數好零的個數)開始的,其大小為0x80000也就是512K的空間,換句話說就是告訴編譯器flash的空間是從0x08000000-0x08080000,RAM的地址從0x20000000開始,大小為0x10000也就是64K的RAM。這與STM32的內存地址映射關系是對應的。

M3復位后,從0x08000004取出復位中斷的地址,並且跳轉到復位中斷程序,中斷執行完之后會跳到我們的main函數,main函數里邊一般是一個死循環,進去后就不會再退出,當有中斷發生的時候,M3將PC指針強制跳轉回中斷向量表,然后根據中斷源進入對應的中斷函數,執行完中斷函數之后,再次返回main函數中。大致的流程就是這樣。

1.1、內部Flash的構成:

STM32F429 的內部 FLASH 包含主存儲器、系統存儲器、 OTP 區域以及選項字節區域,它們的地址分布及大小如下: 

STM32F103的中容量內部 FLASH 包含主存儲器、系統存儲器、 OTP 區域以及選項字節區域,它們的地址分布及大小如下: 

  

 

注意STM32F105VC的是有64K或128頁x2K=256k字節的內置閃存存儲器,用於存放程序和數據。

 

  1. 主存儲器:一般我們說 STM32 內部 FLASH 的時候,都是指這個主存儲器區域它是存儲用戶應用程序的空間,芯片型號說明中的 1M FLASH、 2M FLASH 都是指這個區域的大小。與其它 FLASH 一樣,在寫入數據前,要先按扇區擦除,
  2. 系統存儲區:系統存儲區是用戶不能訪問的區域,它在芯片出廠時已經固化了啟動代碼,它負責實現串口、 USB 以及 CAN 等 ISP 燒錄功能。
  3. OTP 區域:OTP(One Time Program),指的是只能寫入一次的存儲區域,容量為 512 字節,寫入后數據就無法再更改, OTP 常用於存儲應用程序的加密密鑰。
  4. 選項字節:選項字節用於配置 FLASH 的讀寫保護、電源管理中的 BOR 級別、軟件/硬件看門狗等功能,這部分共 32 字節。可以通過修改 FLASH 的選項控制寄存器修改。

1.2、對內部Flash的寫入過程:

1. 解鎖 (固定的KEY值)
(1) 往 Flash 密鑰寄存器 FLASH_KEYR 中寫入 KEY1 = 0x45670123 
(2) 再往 Flash 密鑰寄存器 FLASH_KEYR 中寫入 KEY2 = 0xCDEF89AB 
2. 數據操作位數 
最大操作位數會影響擦除和寫入的速度,其中 64 位寬度的操作除了配置寄存器位外,還需要在 Vpp 引腳外加一個 8-9V 的電壓源,且其供電間不得超過一小時,否則 FLASH可能損壞,所以 64 位寬度的操作一般是在量產時對 FLASH 寫入應用程序時才使用,大部分應用場合都是用 32 位的寬度。 
3. 擦除扇區 
在寫入新的數據前,需要先擦除存儲區域, STM32 提供了扇區擦除指令和整個FLASH 擦除(批量擦除)的指令,批量擦除指令僅針對主存儲區。 
扇區擦除的過程如下: 
(1) 檢查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY”,以確認當前未執行任何 
Flash 操作; 
(2) 在 FLASH_CR 寄存器中,將“激活扇區擦除寄存器位 SER ”置 1,並設置“扇 
區編號寄存器位 SNB”,選擇要擦除的扇區; 
(3) 將 FLASH_CR 寄存器中的“開始擦除寄存器位 STRT ”置 1,開始擦除; 
(4) 等待 BSY 位被清零時,表示擦除完成。 
4. 寫入數據 
擦除完畢后即可寫入數據,寫入數據的過程並不是僅僅使用指針向地址賦值,賦值前還還需要配置一系列的寄存器,步驟如下: 
(1) 檢查 FLASH_SR 中的 BSY 位,以確認當前未執行任何其它的內部 Flash 操作; 
(2) 將 FLASH_CR 寄存器中的 “激活編程寄存器位 PG” 置 1; 
(3) 針對所需存儲器地址(主存儲器塊或 OTP 區域內)執行數據寫入操作; 
(4) 等待 BSY 位被清零時,表示寫入完成。

1.3、查看工程內存的分布:

由於內部 FLASH 本身存儲有程序數據,若不是有意刪除某段程序代碼,一般不應修改程序空間的內容,所以在使用內部 FLASH 存儲其它數據前需要了解哪一些空間已經寫入了程序代碼,存儲了程序代碼的扇區都不應作任何修改。通過查詢應用程序編譯時產生的“ *.map”后綴文件, 
打開 map 文件后,查看文件最后部分的區域,可以看到一段以“ Memory Map of the 
image”開頭的記錄(若找不到可用查找功能定位), 

【注】ROM加載空間

這一段是某工程的 ROM 存儲器分布映像,在 STM32 芯片中, ROM 區域的內容就是 指存儲到內部 FLASH 的代碼。 
在上面 map 文件的描述中,我們了解到加載及執行空間的基地址(Base)都是0x08000000,它正好是 STM32 內部 FLASH 的首地址,即 STM32 的程序存儲空間就直接是執行空間;它們的大小(Size)分別為 0x00000b50 及 0x00000b3c,執行空間的 ROM 比較小的原因就是因為部分 RW-data 類型的變量被拷貝到 RAM 空間了;它們的最大空間(Max)均為 0x00100000,即 1M 字節,它指的是內部 FLASH 的最大空間。

計算程序占用的空間時,需要使用加載區域的大小進行計算,本例子中應用程序使用 
的內部 FLASH 是從 0x08000000 至(0x08000000+0x00000b50)地址的空間區域。 
所以從扇區 1(地址 0x08004000)后的存儲空間都可以作其它用途,使用這些存儲空間時不會篡改應用程序空間的數據。 
具體可參考原子的例程:實驗四十一:FLASH 模擬 EEPROM 實驗

文章引用地址:https://blog.csdn.net/qq_33559992/article/details/77676716

感謝原文作者

二、代碼拆分介紹(以STM32F105系列為例,如上圖表5所示)

 2.1 讀/寫入數據流程

                       

                                      寫數據流程

 2.1.1、Flash 解鎖,直接調用#include "stm32f10x_flash.h"中的void FLASH_Unlock(void)函數,這個函數是官方提供的,其內部代碼如下: 

  View Code

 

2.1.2、擦除扇區,也是直接調用固件庫官方的函數FLASH_Status FLASH_ErasePage(uint32_t Page_Address),這個官方函數代碼也貼出來看看,代碼如下:

  View Code

 注意這個擦除扇區函數是你提供一個STM32f105系列扇區的開始地址即可,擦除是按照頁擦除(每頁2KB=1024Byte)或者整個擦除(見STM32參考手冊的第二章2.3.3嵌入式閃存部分介紹)

 比如我們要擦除互聯網型的127頁,我們只需要FLASH_ErasePage(0x0803f800);執行后,第127頁的0x0803f800-0x0803FFFF數據都將被擦除。

當然官方提供的也不知一個擦除函數,而是三個,具體如下,對於32位系統:一個是字節=4byte=32bite;一個是半字=2byte=16bite;一個是字節=1byte=8bite;進行擦除。

FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

FLASH_Status FLASH_EraseAllPages(void);

FLASH_Status FLASH_EraseOptionBytes(void);

 2.1.3、接下來是寫/讀數據函數,該函數也是官方給出的,我們只需要用就好了。但要注意,這個是個半字的寫操作,威少是uint16_t 的數據算半字呢,因為單片機是32的,對於32位單片機系統來說,一個字是4個字節的,8位的比如51單片機系統一個字就是2位的,64位單片機系統一個字就是8個字節,脫離單片機系統說字是多少個字節是沒意義的。所以這里寫入/讀出半字也就是一次寫入2個字節,寫完/讀出一次地址會加2。

寫數據操作:

  View Code

 

 當然官方給的不止是這一個函數寫數據,官方提供了3個

FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//一次寫一個字,對於32系統,一次寫的是4個字節,uint32_t 變量大小,32bit

FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//一次寫一個半字,對於32系統,一次寫的是2個字節,uint16_t 變量大小,16bit

FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//一次寫一個字節,對於32系統,一次寫的是1個字節,uint8_t 變量大小,8bit

讀數據操作:

讀數據的函數,官方並沒有給出:下面我們自己給出,具體的讀法代碼如下

1 //讀取指定地址的半字(16位數據)
2 //也是按照半字讀出,即每次讀2個字節數據返回
3 uint16_t FLASH_ReadHalfWord(uint32_t address)
4 {
5   return *(__IO uint16_t*)address; 
6 }

 

如果要連續都區多個地址數據,可以進行如下代碼操作

復制代碼
1 //從指定地址開始讀取多個數據
2 void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
3 {
4   uint16_t dataIndex;
5   for(dataIndex=0;dataIndex<countToRead;dataIndex++)
6   {
7     readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
8   }
9 }
復制代碼

 

 2.1.4、這步驟應該就是再次上鎖,保護存儲區不被重寫覆蓋了,直接使用官方的函數即可:FLASH_Lock();//上鎖寫保護

具體官方代碼貼出如下

  View Code

 

三、簡單的小例程代碼實現

例子功能:

       1、將數據存儲在stm32F105單片機的主存儲區0x08036000地址開始的扇區,(0x08036000應該是該單片機大約108個扇區的開始地址位置即頁108起始地址)。

       2、將該單片機的頁108(page108=0x08036000)處的數據再讀出來;

具體實現代碼如下,作為例子,只進行了半字的讀寫操作,我們寫的數據buff為空,內容默認值為0

復制代碼
 1 #include "stm32f10x_flash.h"
 2 
 3 #define StartServerManageFlashAddress    ((u32)0x08036000)//讀寫起始地址(內部flash的主存儲塊地址從0x08036000開始)
 4 
 5 //從指定地址開始寫入多個數據
 6 void FLASH_WriteMoreData(uint32_t startAddress,uint16_t *writeData,uint16_t countToWrite)
 7 {    
 8     uint32_t offsetAddress=startAddress - FLASH_BASE;               //計算去掉0X08000000后的實際偏移地址
 9   uint32_t sectorPosition=offsetAddress/SECTOR_SIZE;            //計算扇區地址,對於STM32F103VET6為0~255
10   uint32_t sectorStartAddress=sectorPosition*SECTOR_SIZE+FLASH_BASE;    //對應扇區的首地址
11     uint16_t dataIndex;
12     
13   if(startAddress<FLASH_BASE||((startAddress+countToWrite*2)>=(FLASH_BASE + SECTOR_SIZE * FLASH_SIZE)))
14   {
15     return;//非法地址
16   }
17   FLASH_Unlock();         //解鎖寫保護
18 
19   FLASH_ErasePage(sectorStartAddress);//擦除這個扇區
20   
21   for(dataIndex=0;dataIndex<countToWrite;dataIndex++)
22   {
23     FLASH_ProgramHalfWord(startAddress+dataIndex*2,writeData[dataIndex]);
24   }
25   
26   FLASH_Lock();//上鎖寫保護
27 }
28 
29 //讀取指定地址的半字(16位數據)
30 uint16_t FLASH_ReadHalfWord(uint32_t address)
31 {
32   return *(__IO uint16_t*)address; 
33 }
34 
35 //從指定地址開始讀取多個數據
36 void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
37 {
38   uint16_t dataIndex;
39   for(dataIndex=0;dataIndex<countToRead;dataIndex++)
40   {
41     readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
42   }
43 }
44 
45 void write_to_flash(void)
46 {
47     u16 buff[1200];
48     u16 count_len = 2272 / 2;
50     FLASH_WriteMoreData(StartServerManageFlashAddress,buff,count_len);
55 }
56 
57 void read_from_flash(void)
58 {
59     u16 buff[1200];
60     u16 count_len = 2272 / 2;
61     FLASH_WriteMoreData(StartServerManageFlashAddress,buff,count_len);
66 
67  }
復制代碼

 

復制代碼
 1 void mian(void)
 2 {
 3     .........//初始化其他外設
 4     while(1)
 5     {
 6         ...........//其他外設執行函數
 7         if(滿足條件真)//寫數據操作
 8        {
 9             write_to_flash();
10         }
11         else //讀數據操作
12         {
13             read_from_flash();
14         }
15 
16     }
17  }
復制代碼

 

四、附言

值得的注意的是,我們讀寫的地址是0x08036000,讀寫方式是半字,這里地址空間對於stm32f105芯片來說是第108扇區,每個扇區2KB,stm32F105VC總共是256KB空間,128頁。所以地址能取到0x08036000,像小中容量stm32f103單片機,64KB和128KB的主存儲區地址都是到不了0x08036000,除非是stm32f103VE的256KB芯片的主存儲快,0x08036000才是有效的存儲地址,中小型這個地址都不是有效的主存儲開地址(超出了)         

注:轉自https://www.cnblogs.com/pertor/p/9484663.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM