0 引言
在嵌入式系統(tǒng)設(shè)計中,需要根據(jù)系統(tǒng)的功能需求選擇相應(yīng)的單片機。筆者參與開發(fā)的一款中央空調(diào)主控制板選用了意法半導(dǎo)體公司的 STM32F407 單片機,這一系列的單片機具有高集成度、高性能、嵌入式存儲器和外設(shè),適合作為主控制板的核心單片機使用。STM32F407 主要特征如下:提供了工作頻率為 168 MHz 的 Cortex-M4 內(nèi)核(具有浮點單元)的性能,在168MHz 頻率下,從 Flash 存儲器執(zhí)行時,STM32F407 能夠提供 210 DMIPS/566 CoreMark 性能,并且利用意法半導(dǎo)體的 ART 加速器實現(xiàn)了 Flash 零等待狀態(tài)。DSP 指令和浮點單元擴大了產(chǎn)品的應(yīng)用范圍。
STM32F407產(chǎn)品系列具有 512 KB~1MB Flash 和 192 KBSRAM,采用尺寸小至 10 mm×10mm 的 100~ 176 引腳封裝。在開發(fā)該控制板的軟件時發(fā)現(xiàn),由于 STM32F407 的 SRAM 地址不連續(xù),造成動態(tài)內(nèi)存分配存在問題,不能利用全部的片內(nèi) 192 KB SRAM 資源,有必要深入分析并解決。
1 EWARM 7.40C/C++編譯器的數(shù)據(jù)存儲
筆者在開發(fā)該控制板的軟件時,使用IAR公司的EWARM7.40作為 C/C++ 編譯器,該編譯器提供了靜態(tài)內(nèi)存分配與動態(tài)內(nèi)存分配2種不同的內(nèi)存分配機制。
1.1 簡介
ARM內(nèi)核可處理4GB 的連續(xù)內(nèi)存,范圍從0x00000000到0xFFFF FFFF。不同類型的物理內(nèi)存可以放置在上述內(nèi)存范圍中。典型的應(yīng)用程序同時具有只讀存儲器(ROM)和隨機存取內(nèi)存(RAM)。此外,內(nèi)存范圍的某些部分包含處理器控制寄存器和外圍單元。
在典型的應(yīng)用程序中,數(shù)據(jù)可以通過以下3種不同的方式存儲在內(nèi)存中:
(1)自動變量
除已聲明為靜態(tài)變量者外,所有函數(shù)的局部變量都存儲在寄存器或堆棧上。這些變量在函數(shù)執(zhí)行時可以使用。當(dāng)函數(shù)返回到其調(diào)用者時,內(nèi)存空間不再有效。
(2)全局變量、模塊靜態(tài)變量和聲明為 static的局部變量
在這種情況下,內(nèi)存的分配是一勞永逸的。在此語境中,“靜態(tài)”一詞表示應(yīng)用程序運行時分配給此類變量的內(nèi)存數(shù)量不會改變。ARM 內(nèi)核有一個單一的地址空間且編譯器支持完整的內(nèi)存尋址。
(3)動態(tài)分配的數(shù)據(jù)
應(yīng)用程序可以在堆上分配數(shù)據(jù),此數(shù)據(jù)一直有效,直到它被應(yīng)用程序顯式地釋放回系統(tǒng)。對于在應(yīng)用程序執(zhí)行之前不知道對象數(shù)量的場合,這種類型的內(nèi)存是有用的。注意:就內(nèi)存量有限的系統(tǒng)或預(yù)期運行很久的系統(tǒng)而言,存在著與使用動態(tài)分配的數(shù)據(jù)相關(guān)的潛在風(fēng)險。
1.2 自動變量和參數(shù)的存儲
按照 C 語言標(biāo)準(zhǔn),函數(shù)內(nèi)定義且未聲明為靜態(tài)的變 量被命名為自動變量。其中一些變量被放置在處理器寄存器中,其余的放在堆棧上。從語義的角度來看,這是等價的。與放在堆棧上的變量相比,主要區(qū)別在于訪問寄存器更快,并且需要的內(nèi)存更少。自動變量只能在函數(shù)執(zhí)行時存活;當(dāng)函數(shù)返回時,分配在堆棧上的內(nèi)存被釋放。
(1)堆棧
堆棧可以包含:未存儲在寄存器中的局部變量和參數(shù);表達(dá)式的臨時結(jié)果;函數(shù)的返回值(在寄存器中傳遞的除外);中斷期間的處理器狀態(tài);應(yīng)在函數(shù)返回前恢復(fù)的處理器寄存器(被調(diào)用者保存的寄存器)。
堆棧是一個固定的內(nèi)存塊,分為兩部分:第一部分包含為調(diào)用當(dāng)前函數(shù)的函數(shù),以及調(diào)用該函數(shù)的函數(shù)所使用而分配的內(nèi)存;第二部分包含可分配的自由內(nèi)存。這兩個區(qū)域之間的邊界線稱為棧頂,由一個專門的處理器寄存器——堆棧指針來表示。通過移動堆棧指針,內(nèi)存被分配到堆棧上。
函數(shù)絕不應(yīng)指向包含自由內(nèi)存的堆棧區(qū)域。原因是,如果發(fā)生中斷,被調(diào)用的中斷函數(shù)會分配、修改,當(dāng)然還有在堆棧上去分配內(nèi)存。
(2)優(yōu)點
堆棧的主要優(yōu)點是:程序不同部分的函數(shù)可以使用相同的內(nèi)存空間存儲其數(shù)據(jù)。不同于堆,堆棧永遠(yuǎn)不會變成碎片或出現(xiàn)內(nèi)存泄漏。
函數(shù)可以直接或間接地調(diào)用自己(遞歸函數(shù)),每個調(diào)用可以在堆棧上存儲自己的數(shù)據(jù)。
(3)潛在的問題
堆棧的工作方式使得臆想在函數(shù)返回后存儲數(shù)據(jù)變得不可能。以下函數(shù)演示了常見的編程錯誤。它返回指向變量 x 的指針,該變量在函數(shù)返回后已不存在。
int * MyFunction(){
int x;
/*在此處理一些事情*/
return &.x; /* 不正確*/
}
另一個問題是堆棧耗盡的風(fēng)險。這個問題在一個函數(shù)調(diào)用另一個、被調(diào)函數(shù)繼續(xù)調(diào)用第3個函數(shù)等,每個函數(shù)使用堆棧的總和大于堆棧的大小時會發(fā)生。當(dāng)大型數(shù)據(jù)對象存儲在堆棧上或使用遞歸函數(shù)時,風(fēng)險更高。
1.3 堆上的動態(tài)內(nèi)存
分配在堆上的對象的內(nèi)存將一直存在,直到對象被顯式釋放。這種類型的內(nèi)存存儲對于直到運行時才知道數(shù)據(jù)量的應(yīng)用程序是非常有用的。
在 C 語言中,使用標(biāo)準(zhǔn)庫函數(shù) malloc或相關(guān)函數(shù) calloc 和 realloc 中的一個來分配內(nèi)存,使用free 來釋放內(nèi)存。
在 C++ 語言中,一個特殊的關(guān)鍵字new 分配內(nèi)存和運行構(gòu)造器。通過 new 分配的內(nèi)存必須使用關(guān)鍵字 delete 來釋放。
設(shè)計使用堆分配對象的應(yīng)用程序時必須非常仔細(xì),因為很容易出現(xiàn)無法在堆上分配對象的情形。如果你的應(yīng)用程序使用過多的內(nèi)存,堆可能會耗盡。如果不再使用的內(nèi)存未被釋放,則堆也會用完。
對于每個分配的內(nèi)存塊,若干字節(jié)用于管理目的的數(shù)據(jù)是必需的。對于分配大量小塊的應(yīng)用程序,此管理開銷可能很大。
還存在碎片化的問題。這意味著在堆中,一小部分自由內(nèi)存被分配對象使用的內(nèi)存所分隔。如果沒有一塊足夠大的自由內(nèi)存給對象,即使自由內(nèi)存大小的總和超過對象的大小,還是無法分配一個新的對象。不幸的是,隨著內(nèi)存的分配和釋放,碎片化往往會增加?;诖?,設(shè)計長時間運行的應(yīng)用程序時應(yīng)盡量避免使用分配在堆上的內(nèi)存。
2 問題分析與解決方案
以下介紹的程序?qū)嵗诰幾g器EWARM7.40、Fre-eRTOS 10.0.0 下驗證通過。
2.1 STM32F407 的片內(nèi) SRAM
STM32F407 具有192 KB 的片內(nèi) SRAM 。片內(nèi) SRAM 可以字節(jié)、半字(16位)或全字(32位)的形式訪問。讀取和寫人操作以 CPU 速度執(zhí)行,等待狀態(tài)為0。片內(nèi) SRAM 最多分為兩個模塊:SRAM1 和 SRAM2 映射地址0x20000000,可以被所有 AHB 主設(shè)備存取;CCM(核心耦合存儲器)映射到地址0x10000000, 只能由 CPU 通過 D 總線存取。
STM32F407 的內(nèi)存映射如圖1所示,應(yīng)用程序可以使用的系統(tǒng)SRAM 地址為0x20000000~0x2001 FFFF(128KB),CCM地址為0x10000000~0x1000 FFFF(64 KB)。
由于兩塊內(nèi)存的地址不連續(xù),使用編譯器 EWARM 7.40時,new 運算符只能夠在一片連續(xù)的空間分配內(nèi)存,無法同時使用另外一片連續(xù)的空間。換言之,堆的實現(xiàn)只能在128 KB 的 SRAM 內(nèi)存空間內(nèi),另外1/3的片內(nèi) SRAM 被白白浪費了,這在應(yīng)用程序較為復(fù)雜時是個嚴(yán)重的缺陷,必須設(shè)法解決。
圖1 STM32F407 的內(nèi)存映射
2.2 解決方案
由于編譯器 EWARM7.40 的局限性,堆無法在兩片不連續(xù)的內(nèi)存空間實現(xiàn)。為了解決此問題,筆者聯(lián)想到了免費的實時操作系統(tǒng) FreeRTOS, 該操作系統(tǒng)在2014年8月發(fā)布的 V8.1.0中提供了新的內(nèi)存管理文件 heap_5.c, 允許堆跨越多個不連續(xù)的內(nèi)存區(qū)域。后續(xù)版本對此功能做了優(yōu)化與改進(jìn)。筆者嘗試用此方案解決堆的實現(xiàn)問題,取得了成功。具體方法如下:
①把FreeRTOS源程序包中的heap_5.c添加到軟件工程中。
②在頭文件中定義堆的大小。
#define configTOTAL_HEAP1_SIZE((size_t)(64*1024))
//HEAP164KB
#define configTOTAL_HEAP2_SIZE((size_t)(100*1024))
//HEAP2100KB
說明:HEAP1 使用CCM 的全部 64KB;HEAP2 使用 SRAM 的100 KB, 剩余部分留給操作系統(tǒng)使用。此數(shù)值可根據(jù)應(yīng)用程序的需求靈活調(diào)整。
③在main.c中定義兩片內(nèi)存區(qū)域:
#pragmalocation=".ccmram"
uint8_tucHeapl[configTOTAL_HEAP1_SIZE];
uint8_tucHeap2[configTOTAL_HEAP2_SIZE];
constHeapRegion_txHeapRegions[]={
/*Startaddress with dummy offsets Size */
{ucHeapl,configTOTAL_HEAP1_SIZE},
{ucHeap2,configTOTAL_HEAP2_SIZE},
{NULL,0}
};
④ 在 main.c 中重載new 和delete:
void *operator new(size_t size){
void *p=0;
p=pvPortMalloc(size); //調(diào)用FreeRTOS 的內(nèi)存分配函數(shù)
return p;
void operator delete(void *p){
vPortFree(p); // 調(diào)用 FreeRTOS 的內(nèi)存釋放函數(shù)
經(jīng)過上述改進(jìn)后,應(yīng)用程序可以使用的堆的大小為 164KB, 徹底擺脫了編譯器 EWARM7.40 的局限性,滿足了復(fù)雜應(yīng)用程序的需求。
3 結(jié)語
本文詳細(xì)介紹了使用多塊不連續(xù)內(nèi)存空間實現(xiàn)堆的軟件方法,以及在 STM32F407 單片機上的軟件實現(xiàn)方法。使用該技術(shù)可以在多塊不連續(xù)內(nèi)存空間實現(xiàn)堆,更好 地實現(xiàn)了內(nèi)存的動態(tài)分配。使用該軟件的控制器產(chǎn)品至今已在現(xiàn)場穩(wěn)定運行18個月。
審核編輯:劉清
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3593瀏覽量
129466 -
加速器
+關(guān)注
關(guān)注
2文章
799瀏覽量
37867 -
Flash存儲器
+關(guān)注
關(guān)注
3文章
104瀏覽量
25762 -
STM32F407
+關(guān)注
關(guān)注
15文章
187瀏覽量
29454 -
靜態(tài)變量
+關(guān)注
關(guān)注
0文章
13瀏覽量
6650
原文標(biāo)題:使用多塊不連續(xù)空間實現(xiàn)堆的軟件方法
文章出處:【微信號:麥克泰技術(shù),微信公眾號:麥克泰技術(shù)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論