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

當(dāng)前位置:首頁(yè) > > 嵌入式IoT

1.介紹

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

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

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

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

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

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

棧區(qū):

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

堆區(qū):

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

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

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

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

2.1 STM32的堆棧機(jī)制

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

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

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

MDK下Code,RO-data,RW-data,ZI-data這幾個(gè)段:

Code是存儲(chǔ)程序代碼的。

RO-data是存儲(chǔ)const常量和指令。

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

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

Flash=Code + RO Data + RW Data;

RAM= RW-data+ZI-data;

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

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

Stack_Size EQU 0x400

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

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

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

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

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

(1)用龐大的全局變量數(shù)組來圈住一塊內(nèi)存,然后將這個(gè)內(nèi)存拿來進(jìn)行內(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ù)或者申請(qǐng)內(nèi)存時(shí),會(huì)多占用一些內(nèi)存資源。

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

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

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

3.1 總體情況一覽

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

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

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

ZI-data是(Zero Initialize) 沒有初始化的可讀寫變量的大小。ZI-data不會(huì)被算做代碼里因?yàn)椴粫?huì)被初始化;

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

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

以下是函數(shù)分配時(shí)的內(nèi)存分配情況,可根據(jù)下面的map文件定位到具體的函數(shù)的內(nèi)存使用情況,其中比較重要的是ZI Data,因?yàn)檫@些內(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的空間大小。

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

board.c

該文件是RT-THREAD操作系統(tǒng)里面的,劃分了一個(gè)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) 

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

startup_stm32l151xba.S

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

Stack_Size EQU 0x400

usart.c

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

idle.c

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

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

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

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

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

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

那么如何計(jì)算線程的棧的最大值?

就拿main線程來分析

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

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

然后是rt_thread_suspend

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

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

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

目前操作系統(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ù)最少也需要分配的棧空間大小


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

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

還有郵箱與時(shí)間消耗的rt_heap空間

事件統(tǒng)計(jì):

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

所以需要的大小為


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

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

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

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

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

縮小OS的堆,目前來看給OS內(nèi)存空間4K還算比較的合理。但是也可以縮減到3K左右,這個(gè)就根據(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線程

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

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

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

對(duì)于系統(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é)即可。

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

4.總結(jié)

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

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


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