加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一,什么是堆棧和隊列
    • 二,STM32 的FLASH與RAM的存儲
    • 三,SMT32的堆棧自留地
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

STM32的堆棧及其內(nèi)存存儲結(jié)構(gòu)

05/27 11:00
8406
閱讀需 9 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

上一篇文章中,為了提取jlink-ob的bin文件,需要到j(luò)linkARM.dll中獲取,因此必須要了解STM32編譯后的指令特點及其存儲特性,所以這一篇先來看看堆棧和存儲,以及SP這個棧頂?shù)刂肥窃趺创_定的。

如何獲取Jlink-ob的固件

今天來科普一下堆棧的概念,以及SMT32存儲器是如何存儲代碼的。

堆和棧的概念其實是漸進式的來看,首先堆棧是一種數(shù)據(jù)結(jié)構(gòu),其次程序運行的時候利用了這樣的數(shù)據(jù)結(jié)構(gòu),在MCU的內(nèi)存中營造出了這兩個區(qū)域來配合程序執(zhí)行。

一,什么是堆棧和隊列

堆棧對應(yīng)到英文單詞就是heap和stack。

這里要注意,在數(shù)據(jù)結(jié)構(gòu)中,隊列和的堆棧是不同類型的數(shù)據(jù)結(jié)構(gòu)。

我記得初學計算機的時候,有一個很有意思的題目--摞盤子。

我現(xiàn)在每天的生活中都在親歷這個題目,晚飯后,我會把廚房收拾干凈,所有的盤子都洗干凈摞在一起。

旁邊那一摞盤子其實就是我設(shè)計的一個棧,他有一個特點,那就是我做飯的時候一定先要從最上面拿起一個盤子乘菜。而當我刷鍋洗碗時,我會把洗好的盤子再放回最上面。如果覺察到下面的盤子積灰了,那說明我們的棧深度還足夠,否則我會認為最近做的菜有點多了。好了,上面就是一個數(shù)據(jù)結(jié)構(gòu) — 棧的問題,下面我們看一下雞蛋的問題。額,不是雞蛋,是隊列的問題。

我最近買了一個儲存雞蛋的盒子,設(shè)計的很巧妙,買來雞蛋我就從上面一個一個的放進去,然后放在冰箱的最上面。需要雞蛋的時候,我就從下面取一顆,源源不斷。你會發(fā)現(xiàn),我吃的永遠是剩的最陳舊的那一顆雞蛋,這就是隊列。

二,STM32 的FLASH與RAM的存儲

到了計算機系統(tǒng)中,堆和棧被分別了開來。其中棧保持原有的特性,也可以叫做堆棧,它是一種運算受限的線性表,限制僅允許在表的一段進行插入和刪除操作,可以被操作的這一端叫做棧頂,另一端叫做棧底,也就是容易積灰的那一端。棧在程序運行的過程中主要負責存儲局部變量,比如我們函數(shù)中定義的變量,如果控制器內(nèi)核的寄存器不夠用了,它就會把這個變量放到棧里面先存一會。在操作系統(tǒng)中,任務(wù)切換時的現(xiàn)場參數(shù)保存也是存放到對應(yīng)任務(wù)的棧(這里的棧其實放在STM32的全局變量里面)里面。上面有點迷糊,這里我們主要討論STM32裸機程序的堆棧情況。在STM32中,堆是用來給分配動態(tài)內(nèi)存的,系統(tǒng)只有用到malloc的時候才會使用這個區(qū)間,后面在講這個位置。我先看看,每次編譯完程序,編譯器鏈接后,會提供一個生成信息。

代碼編譯后提示信息可以看到的內(nèi)容

    Code :是代碼占用的空間,存儲到Flash【ROM】中的程序代碼。RO-data:是 Read Only 只讀常量的大小,如const修飾的變量。用來存儲程序的指令和常量,保存在Flash【ROM】中。RW-data:是(Read Write) RW是可讀可寫變量,就是初始化時候就已經(jīng)賦值了的(上電前就已經(jīng)確定值的),RW + ZI就是你的程序總共使用的RAM字節(jié)數(shù)。ZI-data:是(Zero Initialize) 沒有初始化的可讀寫變量的大小,就是程序中用到的變量并且被系統(tǒng)初始化為0的變量的字節(jié)數(shù)。

Total?ROM?Size?(Code?+?RO?Data?+?RW?Data)這樣所寫的程序占用的ROM的字節(jié)總數(shù),這部分在我們進行程序下載的時候,會全部存儲在SMT32的Flash中。

當系統(tǒng)上電后,內(nèi)核會將一部分數(shù)據(jù)存搬運到RAM當中,因為我們定義的很多變量是可讀取的,所以要放到RAM中。這其中包括RW-data和ZI-data。對于RW-data,需要一點一點的搬運過去,既然是搬運,那么在FLASH中本來就會存儲一份,所以RW-data會占用Flash空間。而對于ZI-data,由于它沒有初始化,所以它的初始值無所謂,所以我們只需要知道它的個數(shù),在RAM中直接畫一片地方給他用就行了,因此ZI-data無需單獨在Flash中存儲一份。我們所說的堆棧,就在上述執(zhí)行過程中同樣的被畫了一塊自留地。

三,SMT32的堆棧自留地

那么在STM32的RAM中,堆棧是如何量取確定的呢?我們先看一張圖:

上圖中指示了STM32的RAM區(qū)域存儲結(jié)構(gòu),下面是低地址,上面是高地址。編譯器在編譯和鏈接后,是可以計算出全局變量,局部變量以及一些未初始化的全局變量所占用空間的,我們把這個總和計算出來,現(xiàn)在RAM上畫一片地方來存放,也就是上圖的靜態(tài)存儲區(qū)域。然后我們再畫一片自留地給到定義好的HEAP區(qū)域,最后畫一片給到STACK,也就是我們說的棧。我們在代碼中看一下如何定義的。我手頭有一個CW32的例程,用這個來示意一下。在項目中的startupxx.s文件中,用匯編定義了堆和棧的大小。

如果我們不使用malloc進行動態(tài)分配內(nèi)存,那么這里的Heap_Size完全可以定義為0,不過編譯器直接把這個事給干了,也就是說,如果它發(fā)現(xiàn)你沒有使用malloc,編譯器會直接移除這部分空間,可以在例程編譯生成的map文件中看到。

所以,沒必要手賤的去改這個匯編文件。再說RAM空間的堆疊方式,它是先放的靜態(tài)變量,然后放的堆空間(編譯器會優(yōu)化掉),最后放的??臻g,所以只要我們改變棧空間大小,那么棧頂指針就會變化。實驗一下吧。

接下來,我們把棧改大一倍,編譯看看,棧頂?shù)刂肥遣皇窍鄳?yīng)的變大了

是不是?童叟無欺!那么,如果我們增加靜態(tài)存儲區(qū)的大小,這個棧頂?shù)刂窇?yīng)該也會改變。如何增加靜態(tài)存儲區(qū)大小呢?定義一個全局變量就好了。記得把編譯器的優(yōu)化等級去掉哦,不然編譯器會偷偷把你沒用的變量啥的都給你移除掉,實驗就沒效果了。

我注釋掉了一個4字節(jié)的數(shù)組,棧的大小也改回了0x200,編譯后,我的棧頂?shù)刂纷兂闪?x20000e60。接下來,我把注釋打開,增加一個4字節(jié)的數(shù)組。

我們可以看到,棧頂?shù)刂反_實增加了,從原來的0x20000e60增加到了0x20000e68。為什么地址一下增加了8呢?于是我有改小了一下數(shù)組,重新編譯后,棧頂?shù)刂酚肿兓亓?x20000e60。

有興趣的朋友研究下,咱們評論區(qū)聊聊。

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風險等級 參考價格 更多信息
RSE-32.768-12.5-H14-TR-10PPM 1 Raltron Electronics Corporation Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom,
暫無數(shù)據(jù) 查看
NX2012SA-32.768K-STD-MUB-1 1 Nihon Dempa Kogyo Co Ltd Parallel - Fundamental Quartz Crystal, 0.032768MHz Nom, ROHS COMPLIANT PACKAGE-2
$7.18 查看
PLR135/T5P 1 Everlight Electronics Co Ltd Receiver, 16Mbps, Panel Mount, ROHS COMPLIANT PACKAGE-3
$2.4 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

多年硬件從業(yè)經(jīng)驗,專注分享從研發(fā)到供應(yīng)鏈,再到精益制造過程中的經(jīng)驗和感悟!