新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 詳解STM32單片機(jī)的堆棧

詳解STM32單片機(jī)的堆棧

作者:睡夢(mèng)中的雄師 時(shí)間:2025-07-15 來(lái)源:EEPW 收藏

學(xué)習(xí)單片機(jī)的時(shí)候,總是能遇到“堆棧”這個(gè)概念。分享本文,希望對(duì)你理解堆棧有幫助。對(duì)于了解一點(diǎn)匯編編程的人,就可以知道,堆棧是內(nèi)存中一段連續(xù)的存儲(chǔ)區(qū)域,用來(lái)保存一些臨時(shí)數(shù)據(jù):嵌入式開(kāi)發(fā)中更接近底層的匯編與C 語(yǔ)言。堆棧操作由PUSH、POP兩條指令來(lái)完成。而程序內(nèi)存可以分為幾個(gè)區(qū):

●   棧區(qū)(stack)

●   堆區(qū)(Heap)

●   全局區(qū)(static)

1   文字常量區(qū)程序代碼區(qū)

程序編譯之后,全局變量、靜態(tài)變量已經(jīng)分配好內(nèi)存空間,在函數(shù)運(yùn)行時(shí),程序需要為局部變量分配棧空間,當(dāng)中斷來(lái)時(shí),也需要將函數(shù)指針入棧,保護(hù)現(xiàn)場(chǎng),以便于中斷處理完之后再回到之前執(zhí)行的函數(shù)。

棧是從高到低分配,堆是從低到高分配。

2   普通單片機(jī)與單片機(jī)中堆棧的區(qū)別

普通單片機(jī)啟動(dòng)時(shí),不需要用bootloader將代碼從ROM搬移到RAM。

但是單片機(jī)需要。

這里我們可以先看看單片機(jī)程序執(zhí)行的過(guò)程,單片機(jī)執(zhí)行分三個(gè)步驟:

●   取指令

●   分析指令

●   執(zhí)行指令

根據(jù)PC的值從程序存儲(chǔ)器讀出指令,送到指令寄存器。然后分析執(zhí)行執(zhí)行。這樣單片機(jī)就從內(nèi)部程序存儲(chǔ)器去代碼指令,從RAM存取相關(guān)數(shù)據(jù)。

RAM取數(shù)的速度是遠(yuǎn)高于ROM的,但是普通單片機(jī)因?yàn)楸旧磉\(yùn)行頻率不高,所以從ROM取指令慢并不影響。

而STM32的CPU運(yùn)行的頻率高,遠(yuǎn)大于從ROM讀寫(xiě)的速度。所以需要用bootloader 將代碼從ROM 搬移到RAM。

使用棧就象我們?nèi)ワ堭^里吃飯,只管點(diǎn)菜(發(fā)出申請(qǐng))、付錢(qián)、和吃(使用),吃飽了就走,不必理會(huì)切菜、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。使用堆就象是自己動(dòng)手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

其實(shí)堆棧就是單片機(jī)中的一些存儲(chǔ)單元,這些存儲(chǔ)單元被指定保存一些特殊信息,比如地址(保護(hù)斷點(diǎn))和數(shù)據(jù)(保護(hù)現(xiàn)場(chǎng))。

如果非要給他加幾個(gè)特點(diǎn)的話那就是:這些存儲(chǔ)單元中的內(nèi)容都是程序執(zhí)行過(guò)程中被中斷打斷時(shí),事故現(xiàn)場(chǎng)的一些相關(guān)參數(shù)。如果不保存這些參數(shù),單片機(jī)執(zhí)行完中斷函數(shù)后就無(wú)法回到主程序繼續(xù)執(zhí)行了。

這些存儲(chǔ)單元的地址被記在了一個(gè)叫做堆棧指針(SP)的地方。

3   結(jié)合STM32 的開(kāi)發(fā)講述堆棧

從上面的描述可以看得出來(lái),在代碼中是如何占用堆和棧的??赡芎芏嗳诉€是無(wú)法理解,這里再結(jié)合STM32的開(kāi)發(fā)過(guò)程中與堆棧相關(guān)的內(nèi)容來(lái)進(jìn)行講述。

如何設(shè)置STM32的堆棧大?。?/p>

在基于MDK的啟動(dòng)文件開(kāi)始,有一段匯編代碼是分配堆棧大小的。

1752582205437792.png

這里重點(diǎn)知道堆棧數(shù)值大小就行。還有一段AREA(區(qū)域),表示分配一段堆棧數(shù)據(jù)段。數(shù)值大小可以自己修改,也可以使用STM32CubeMX數(shù)值大小配置,如下圖所示。

image.png

STM32F1默認(rèn)設(shè)置值0×400,也就是1K大小。

Stack_Size EQU 0×400

函數(shù)體內(nèi)局部變量:

void Fun(void){ char i; int Tmp[256]; //...}

局部變量總共占用了256*4 +1字節(jié)的??臻g。所以,在函數(shù)內(nèi)有較多局部變量時(shí),就需要注意是否超過(guò)我們配置的堆棧大小。

函數(shù)參數(shù):

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)

這里要強(qiáng)調(diào)一點(diǎn):傳遞指針只占4 字節(jié),如果傳遞的是結(jié)構(gòu)體,就會(huì)占用結(jié)構(gòu)大小空間。提示:在函數(shù)嵌套,遞歸時(shí),系統(tǒng)仍會(huì)占用棧空間。

堆(Heap)的默認(rèn)設(shè)置0×200(512)字節(jié)。

Heap_Size EQU 0×200

大部分人應(yīng)該很少使用malloc 來(lái)分配堆空間。雖然堆上的數(shù)據(jù)只要程序員不釋放空間就可以一直訪問(wèn),但是,如果忘記了釋放堆內(nèi)存,那么將會(huì)造成內(nèi)存泄漏,甚至致命的潛在錯(cuò)誤。

4   MDK 中RAM占用大小分析

經(jīng)常在線調(diào)試的人,可能會(huì)分析一些底層的內(nèi)容。這里結(jié)合MDK-ARM來(lái)分析一下RAM占用大小的問(wèn)題。在MDK編譯之后,會(huì)有一段RAM大小信息:

1752582436478072.png

這里4+6=1640,轉(zhuǎn)換成16進(jìn)制就是0×668,在進(jìn)行在調(diào)試時(shí),會(huì)出現(xiàn):

1752582475460064.png

這個(gè)MSP就是主堆棧指針,一般我們復(fù)位之后指向的位置,復(fù)位指向的其實(shí)是棧頂:

1752582520287457.png

而MSP指向地址0×20000668是0×20000000偏移0×668而得來(lái)。具體哪些地方占用了RAM,可以參看map文件中【Image Symbol Table】處的內(nèi)容:

1752582572989699.png

(本文來(lái)源于《EEPW》


關(guān)鍵詞: 202507 STM32 STM單片機(jī) MCU

評(píng)論


相關(guān)推薦

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

關(guān)閉