天天看點

詳解STM32單片機的堆棧

    學習STM32單片機的時候,總是能遇到“堆棧”這個概念。分享本文,希望對你了解堆棧有幫助。

    對于了解一點彙編程式設計的人,就可以知道,堆棧是記憶體中一段連續的存儲區域,用來儲存一些臨時資料。堆棧操作由PUSH、POP兩條指令來完成。而程式記憶體可以分為幾個區:

  • 棧區(stack)
  • 堆區(Heap)
  • 全局區(static)

    程式編譯之後,全局變量,靜态變量已經配置設定好記憶體空間,在函數運作時,程式需要為局部變量配置設定棧空間,當中斷來時,也需要将函數指針入棧,保護現場,以便于中斷處理完之後再回到之前執行的函數。

    棧是從高到低配置設定,堆是從低到高配置設定。

普通單片機與STM32單片機中堆棧的差別

    普通單片機啟動時,不需要用bootloader将資料 從ROM搬移到RAM。但是STM32單片機需要。這裡我們可以先看看單片機程式執行的過程,單片機執行分三個步驟:

  • 取執行
  • 分析指令
  • 執行指令

    根據PC的值從程式存儲器讀出指令,送到指令寄存器。然後分析執行執行。這樣單片機就從内部程式存儲器去代碼指令,從RAM存取相關資料。

    RAM取數的速度是遠高于ROM的,但是普通單片機因為本身運作頻率不高,是以從ROM取指令慢并不影響。

    而STM32的CPU運作的頻率高,遠大于從ROM讀寫的速度。是以需要用bootloader将資料 從ROM搬移到RAM。

    使用棧就像我們去飯館裡吃飯,隻管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

    其實堆棧就是單片機中的一些存儲單元,這些存儲單元被指定儲存一些特殊資訊,比如位址(保護斷點)和資料(保護現場)。

    如果非要給他加幾個特點的話那就是:

  • 這些存儲單元中的内容都是程式執行過程中被中斷打斷時,事故現場的一些相關參數。如果不儲存這些參數,單片機執行完中斷函數後就無法回到主程式繼續執行了。
  • 這些存儲單元的位址被記在了一個叫做堆棧指針(SP)的地方。

結合STM32的開發講述堆棧

    從上面的描述可以看得出來,在代碼中是如何占用堆和棧的。可能很多人還是無法了解,這裡再結合STM32的開發過程中與堆棧相關的内容來進行講述。

    如何設定STM32的堆棧大小?

    在基于MDK的啟動檔案開始,有一段彙編代碼是配置設定堆棧大小的。

詳解STM32單片機的堆棧

    這裡重點知道堆棧數值大小就行。還有一段AREA(區域),表示配置設定一段堆棧資料段。數值大小可以自己修改,也可以使用STM32CubeMX數值大小配置,如下圖所示。

詳解STM32單片機的堆棧

    在IAR中,是通過工程配置堆棧大小,如下圖所示。

詳解STM32單片機的堆棧

    STM32F1預設設定值0x400,也就是1K大小。

Stack_Size EQU 0x400      

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

    局部變量總共占用了256*4 + 1位元組的棧空間。是以,在函數内有較多局部變量時,就需要注意是否超過我們配置的堆棧大小。

    函數參數:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)      

    這裡要強調一點:傳遞指針隻占4位元組,如果傳遞的是結構體,就會占用結構大小空間。提示:在函數嵌套,遞歸時,系統仍會占用棧空間。

    堆(Heap)的預設設定0x200(512)位元組。

Heap_Size EQU 0x200      

    大部分人應該很少使用malloc來配置設定堆空間。雖然堆上的資料隻要程式員不釋放空間就可以一直通路,但是,如果忘記了釋放堆記憶體,那麼将會造成記憶體洩漏,甚至緻命的潛在錯誤。

MDK中RAM占用大小分析

    經常線上調試的人,可能會分析一些底層的内容。這裡結合MDK-ARM來分析一下RAM占用大小的問題。在MDK編譯之後,會有一段RAM大小資訊:

詳解STM32單片機的堆棧

    這裡4+6=1640,轉換成16進制就是0x668,在進行在調試時,會出現:

詳解STM32單片機的堆棧

    這個MSP就是主堆棧指針,一般我們複位之後指向的位置,複位指向的其實是棧頂:

詳解STM32單片機的堆棧

    而MSP指向位址0x20000668是0x20000000偏移0x668而得來。具體哪些地方占用了RAM,可以參看map檔案中【Image Symbol Table】處的内容:

詳解STM32單片機的堆棧