www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 嵌入式IoT

1.介紹

本文主要針對如何合理的使用STM32的RAM角度入手,對STM32的RAM進行分配與計算。目的是降低RAM的使用率,將RAM的使用情況都弄清楚,從而合理的規(guī)劃及分配內(nèi)存。

本文涉及到一些堆棧方面的思考,在MDK中查看MAP文件及堆棧使用情況的文件進行分析,得出當前程序RAM的分配情況,同時對可以縮減的地方進行分析。

2.內(nèi)存的基本構(gòu)成

可編程內(nèi)存基本上可以分為以下幾個部分:靜態(tài)存儲區(qū)bss段、堆區(qū)和棧區(qū)。他們的功能不同,使用方法也就不同。

靜態(tài)存儲區(qū):

靜態(tài)存儲區(qū)也就是BSS段,英文是Block Started by Symbol的簡稱,通常是指存放在未初始化的全局變量的一塊內(nèi)存區(qū)域,在程序載入時由內(nèi)核清零。靜態(tài)存儲區(qū)在程序編譯好的時候就已經(jīng)分配好,這塊內(nèi)存在程序的整個運行期間都存在,主要存放靜態(tài)數(shù)據(jù)全局數(shù)據(jù)和常量。

棧區(qū):

在執(zhí)行函數(shù)時,函數(shù)內(nèi)局部變量的存儲單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時,這些存儲單元自動被釋放,棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)容容量有限。

堆區(qū):

也稱動態(tài)內(nèi)存分配。一般由程序員分配和釋放,若程序員不釋放,程序結(jié)束時可能由操作系統(tǒng)回收。分配方式類似于數(shù)據(jù)結(jié)構(gòu)中的鏈表。程序在運行時候調(diào)用malloc或者new申請任意大小的內(nèi)存,程序員自己負責(zé)在適當?shù)臅r候free或者delete釋放內(nèi)存。動態(tài)內(nèi)存的生存期可以由程序決定。良好的使用方法是:如果某動態(tài)內(nèi)存不再使用,需要將其釋放掉,否則,很容易出現(xiàn)內(nèi)存泄漏現(xiàn)象。

其內(nèi)存的分配規(guī)律:

如果系統(tǒng)調(diào)用了malloc,從0X20000000開始依次為:靜態(tài)存儲區(qū)+堆區(qū)+棧區(qū)

如果系統(tǒng)未調(diào)用了malloc,從0X20000000開始依次為:靜態(tài)存儲區(qū)+棧區(qū)

2.1 STM32的堆棧機制

要搞清楚stm32的堆棧機制,需要理清楚stm32的存儲結(jié)構(gòu)。

在stm32中,flash,SRAM寄存器和輸入輸出端口被組織在同一個4GB的線性地址空間內(nèi)??稍L問的存儲器分為8個主要塊,每個塊為512MB。

C語言上分為棧、堆、bss、data、code段。重點分析一下STM32以及在MDK里面段的劃分。

MDK下Code,RO-data,RW-data,ZI-data這幾個段:

Code是存儲程序代碼的。

RO-data是存儲const常量和指令。

RW-data是存儲初始化值不為0的全局變量。

ZI-data是存儲未初始化的全局變量或初始化值為0的全局變量。

Flash=Code + RO Data + RW Data;

RAM= RW-data+ZI-data;

這個是MDK編譯之后能夠得到的每個段的大小,也就能得到占用相應(yīng)的FLASH和RAM的大小,但是還有兩個數(shù)據(jù)段也會占用RAM,但是是在程序運行的時候,才會占用,那就是堆和棧。在stm32的啟動文件.s文件里面,就有堆棧的設(shè)置,其實這個堆棧的內(nèi)存占用就是在上面RAM分配給RW-data+ZI-data之后的地址開始分配的。

在stm32的啟動文件中,statrtup_stm32l151xba.s文件中,有一句這樣的函數(shù)

Stack_Size EQU 0x400

表示棧的大小為0x400也就是1024字節(jié)。這樣CPU在處理任務(wù)的時候,函數(shù)局部變量最多可以占用的空間大小為1024字節(jié)。這里的棧大小包括函數(shù)的嵌套,遞歸等等,都是從這個棧里面分配出來的。

所以如果一個函數(shù)的局部變量過多,或者嵌套層數(shù)越深,那么程序非常容易出現(xiàn)崩潰的情況。所以一定不要在函數(shù)里放過多的局部變量。

堆的增長方向時向上的,而棧的增長方向時向下的,并且沒有固定的界限,一旦堆棧沖突,函數(shù)就會崩潰。總體上也就是說,在使用堆棧的過程中,一定要確保堆棧的大小及使用情況。

2.2 OS系統(tǒng)內(nèi)存分配策略

對于OS中對堆棧的管理,主要分為兩種情況:

(1)用龐大的全局變量數(shù)組來圈住一塊內(nèi)存,然后將這個內(nèi)存拿來進行內(nèi)存管理和分配。這種情況下,堆棧占用的內(nèi)存就是上面說的:如果沒有初始化數(shù)組,或者數(shù)組的初始化值為0,堆棧就是占用的RAM的ZI-data部分;如果數(shù)組初始化值不為0,堆棧就占用的RAM的RW-data部分。這種方式的好處是容易從邏輯上知道數(shù)據(jù)的來由和去向。目前在rtthread的內(nèi)存管理上就是采用這種方式。

在board.c中,可以看到如下的代碼

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) #define RT_HEAP_SIZE 1024 static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4) 

用這種方式也有一定的弊端,就是在操作系統(tǒng)創(chuàng)建任務(wù)或者申請內(nèi)存時,會多占用一些內(nèi)存資源。

(2)就是把編譯器沒有用掉的RAM部分拿來做內(nèi)存分配,也就是除掉RW-data+ZI-data+編譯器堆+編譯器棧后剩下的RAM內(nèi)存中的一部分或者全部進行內(nèi)存管理和分配。這樣的情況下就只需要知道內(nèi)存剩下部分的首地址和內(nèi)存的尾地址,然后要用多少內(nèi)存,就用首地址開始挖,做一個鏈表,把內(nèi)存獲取和釋放相關(guān)信息鏈接起來,就能及時的對內(nèi)存進行管理了。

3.STM32內(nèi)存使用情況分析

如果要分析出STM32的內(nèi)存使用情況,可借助KEIL中編譯出來的map文件進行查閱

3.1 總體情況一覽

首先可以在map的末尾看總的內(nèi)存使用情況

RO-data是 Read Only 只讀常量的大小,如const型;

RW-data是(Read Write) 初始化了的可讀寫變量的大小;

ZI-data是(Zero Initialize) 沒有初始化的可讀寫變量的大小。ZI-data不會被算做代碼里因為不會被初始化;

其中RW Data + ZI Data表示總共需要占用的RAM的大小。而Code + RO Data + RW Data表示ROM需要的大小,根據(jù)這兩個值,可以根據(jù)程序合理的選擇相應(yīng)的MCU。

3.2 各函數(shù)所需要的內(nèi)存

以下是函數(shù)分配時的內(nèi)存分配情況,可根據(jù)下面的map文件定位到具體的函數(shù)的內(nèi)存使用情況,其中比較重要的是ZI Data,因為這些內(nèi)存都是分配在RAM空間中的。

Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name .
.
.

---------------------------------------------------------------------- 31376 2302 650 400 6848 668739 Object Totals 0 0 32 0 0 0 (incl. Generated) 54 0 3 10 0 0 (incl. Padding)

從上面的表格,可以分析出(ZI Data+RW Data)正好是7248,也就是RAM的空間大小。

對于以上的數(shù)據(jù),可以從占用RAM最大的開始計時

board.c

該文件是RT-THREAD操作系統(tǒng)里面的,劃分了一個4KB的靜態(tài)數(shù)組作為操作系統(tǒng)分配的內(nèi)存區(qū)域。

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP) #define RT_HEAP_SIZE 3072 static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4) 

該文件里定義了一個全局的數(shù)組作為操作系統(tǒng)分配的內(nèi)存區(qū)域,這塊區(qū)域作為系統(tǒng)線程的??臻g使用,也可以用來動態(tài)的申請內(nèi)存區(qū)域。在這塊RAM中,合理的估算每個線程的棧大小可以有效的對該大小進行規(guī)劃。

startup_stm32l151xba.S

啟動函數(shù)中,會分配堆棧,這個堆棧是供C語言使用的,在進行程序跳轉(zhuǎn)或者中斷到來時,都會進行入棧及出棧的操作。目前分配給系統(tǒng)的堆??臻g時1KB。

Stack_Size EQU 0x400

usart.c

該函數(shù)是HAL庫函數(shù)中定義的,其中用到了幾個全局的結(jié)構(gòu)體,該結(jié)構(gòu)體的用處主要是供其他函數(shù)調(diào)用UART的操作句柄。這里消耗的RAM資源為600K。

idle.c

idle線程與其他的線程不一樣的地方就是該線程的棧不是存在于rt_heap里面,而是單獨為其申請了一塊靜態(tài)數(shù)組作為線程使用的堆棧。該函數(shù)消耗的RAM資源為384KB。

僅僅這四個文件就占用了6KB左右的資源。下面來分析一下具體的內(nèi)存使用情況。

3.3 操作系統(tǒng)RAM的使用情況

在操作系統(tǒng)中,使用RAM的情況可以通過對每個線程棧的最大深度來進行計算。

在MDK中,可以查看Static Call Graph for image文件來查看棧的使用情況

可以看出,main函數(shù)的線程棧最大,為224bytes。

那么如何計算線程的棧的最大值?

就拿main線程來分析

main函數(shù)的最深的棧的最后一個函數(shù)為_rt_timer_remove

然后看一下rt_timer_stop函數(shù)

然后是rt_thread_suspend

以此類推,可以得到main函數(shù)線程最大需要消耗的??臻g大小為224bytes。

前面分析出對于操作系統(tǒng)使用的內(nèi)存,都是在rt_heap上,而這個內(nèi)存目前是4KB。

所以對于rt_malloc內(nèi)存都是在以上的資源上申請的。

目前操作系統(tǒng)的線程中,使用的線程如下

線程名稱 堆棧大小 說明
main 224 main線程
tsk_xxx1_entry 184 a線程
tsk_xxx2_entry 160 b線程
tsk_xxx3_entry 168 c線程
tsk_xxx4_entry 160 d線程
tsk_xxx5_entry 176 e線程
tsk_xxx6_entry 160 f線程

也就任務(wù)最少也需要分配的棧空間大小


對于申請在rt_heap上的不只有線程的??臻g,還有線程控制塊,每一個線程控制塊為128字節(jié),每申請一塊內(nèi)存都需要在在頭部加上12字節(jié)的頭部信息。所以在創(chuàng)建這些線程時,一共消耗rt_heap的資源為

也就是線程上消耗的rt_heap的大小為2632bytes。

還有郵箱與時間消耗的rt_heap空間

事件統(tǒng)計:

事件名稱 占用大小 說明
a_re 32 回復(fù)事件
b_te 32 發(fā)送事件
tcle 32 其他消息

所以需要的大小為


信號量統(tǒng)計
信號量名稱 占用大小 說明
a_rsem 32 a消息
b_rmsg 32 b消息信號量
c_sem 32 c信號量
d_rmsg 32 d接收信號量

總結(jié)來看,消耗的rt_heap上的內(nèi)存空間為

3.優(yōu)化內(nèi)存使用策略

如果要優(yōu)化RAM的使用,可以有以下幾個辦法:

OS的棧內(nèi)存優(yōu)化

縮小OS的堆,目前來看給OS內(nèi)存空間4K還算比較的合理。但是也可以縮減到3K左右,這個就根據(jù)需求來定。這4K的資源可以做如下的分配:

線程名稱 堆棧大小 TCB 內(nèi)存頭部 說明
main 256(min:224) 128 24 main線程
tsk_xxx1_entry 256(min:184) 128 24 a線程
tsk_xxx2_entry 256(min:160) 128 24 b線程
tsk_xxx3_entry 256(min:168) 128 24 c線程
tsk_xxx4_entry 256(min:160) 128 24 d線程
tsk_xxx5_entry 256(min:176) 128 24 e線程
tsk_xxx6_entry 256(min:160) 128 24 f線程

如果按照每個堆棧都256字節(jié)來計算,那么總共需要的RAM空間為2856字節(jié)。加上信號量與郵箱需要的資源一共是3164字節(jié)?,F(xiàn)在分配的是4096字節(jié)。

以上是一種方式,也可以不用rt_thread的內(nèi)存管理,這樣就燒了內(nèi)存頭部信息的占用。

系統(tǒng)棧上的優(yōu)化

對于系統(tǒng)棧上目前已知的消耗是2752??梢詢?yōu)化的地方是系統(tǒng)棧的空間,現(xiàn)在用的是1024字節(jié)。應(yīng)該可以縮減到512字節(jié),但是目前沒有縮減。

然后是idle線程的,現(xiàn)在是分配了256字節(jié)的靜態(tài)數(shù)組,這里用不了那么多,可以縮減到128字節(jié)即可。

另外具體的細節(jié)部分還可以調(diào)整,目前程序的至少可以縮小1K字節(jié)。

4.總結(jié)

STM32降低RAM的使用,本質(zhì)上就是降低堆棧的使用。在這個過程中,只要搞清楚內(nèi)存的分配方式以及OS的堆棧使用情況即可進行內(nèi)存的合理規(guī)劃。

對于降低RAM的過程,可以從以下方面入手,如果用局部變量,要考慮到棧的分配問題,??臻g的計算以函數(shù)最深的入棧開始,一層一層的計算累加,得到最大的棧的大小,由此,可以計算得到棧的大小。OS上的內(nèi)存有著自己的一套內(nèi)存管理方式,所以可以劃分出一塊靜態(tài)區(qū)域給OS作為堆使用。OS申請的內(nèi)存都是在這塊區(qū)域內(nèi)進行,計算各個線程的??臻g大小,從而得出最合理的大小。


本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉