1、編譯失敗時
很多人從事嵌入式單片機開發,不怎么關心內存分配問題。尤其是現在STM32大行其道之時,型號眾多,可選擇性大,而且RAM也是極大的增加,不像開發51單片機那樣要僅僅盯著RAM的分配及使用情況。對于簡單的項目應用,功能單一的情況下,不用過于操心RAM的使用情況。隨著功能的增多,模塊增多之后,尤其編譯時提示內存不足之時,如下圖所示
內存RAM不足編譯失敗
發生這種情況,就要左看看右看看,這邊減少一些那邊減少一些,或者做剛做的改動調整一下,做出妥協的做法了。
2、弄清RAM使用情況
作為一個認真執著的開發工程師(碼農)對于RAM的使用情況是否需要弄清它呢?怎么樣弄清它呢?有沒有一個方法可以讓程序自動告訴我們RAM用來多少?還剩余多少呢?答案是有的。
2.1、RAM結構
RAM結構很簡單,如下圖所示。
已STM32F103RB為例
在實踐中,變量得需要時動態增加或者減少的,因此上述分區基本不變,變化的是具體區域的地址;我們只要拿捏住芯片RAM大小與堆棧區的大小及棧定的地址就可以很方便地知道其他區域的使用情況了。那么我們怎么知道棧頂地址呢?做過STM32應用編程(IAP)的朋友應該很容易想到,它就保存在0x08000000地址處(對此不清楚的朋友可以查下相關資料)。題外話:系統是怎么知道堆棧(上圖紅線)越界了呢?知道的朋友可以留言告訴我一下,謝謝。
2.2、驗證棧頂地址
驗證棧頂地址的方法有3種,
第一是看項目的map文件,如下圖所示。
map文件查找棧頂地址
第二是仿真看MSP寄存器,如下圖所示。
軟件仿真查看MSP,僅啟動仿真功能不運行
第三種是查看BIN文件的第一個字,如下圖所示。
生成工程BIN文件查看第一個字,這是IAP功能的基礎
通過上面三中方法驗證了棧頂地址是一致的。但是這些我能只能人工去做,現在就讓軟件自動告訴我們RAM分配清單。
3、讓軟件自動輸出RAM分配清單
關鍵點:一是,到.s文件查看堆與棧分配的大小,定義如下圖所示。
定義芯片RAM內存相關信息
當更改S文件中的堆棧配置時這里的宏定義也要跟著改變(要是有知道在C文件中引用S文件定義的朋友請留言告訴作者,謝謝)。
下面簡單地寫一個測試公共變量得函數,測試堆內存得函數,同時實現寫一個輸出RAM分配清單函數,如下:
#define App_START_ADDRESS 0x08000000 //開始地址(棧頂地址)
#define RAM_START_ADDRESS 0x20000000 //RAM開始地址
#define APP_STACK_SIZE 0x1000 //棧大小(字節數量)4KB
#define APP_HEAP_SIZE 0x1000 //堆大小(字節數量)4KB
#define MCU_RAM_SIZE 0x5000 //20KB
// --------------------------------------------------
#define BUFF_SIZE 1024 //定義公共大小
char buff[BUFF_SIZE]; //定義公共變量
int GetGloablVarSum(void)
{
int i,sum=0; //局部變量
for(i=0;i<BUFF_SIZE;i++) //公共變量初始化
{
buff[i]=i;
sum+=buff[i]; //計算功能變量的和
}
return sum; //返回計算和
}
// --------------------------------------------------
int GetHeapVarSum(void)
{
int i,sum=0; //定義公共變量
char *p1,*p2=(char*)malloc(BUFF_SIZE); //從堆區申請內存
p1=p2;
memcpy(p2,buff,BUFF_SIZE); //將公共變量的值拷貝到申請的內存中
p1=p2;
for(i=0;i<BUFF_SIZE;i++) //計算申請內存中值的和
{
sum+=*p2++;
}
return sum; //返回計算和
}
// --------------------------------------------------
void TellAboutRam()
{
unsigned int StackTopAddr=(*(__IO u32*)APP_START_ADDRESS); //獲取棧頂地址
unsigned int HeapTopAddr=StackTopAddr-APP_STACK_SIZE; //獲取堆頂/棧底地址
unsigned int GlobalVarSize=HeapTopAddr-APP_HEAP_SIZE-RAM_START_ADDRESS; //計算公共變量區域大小
unsigned int RamFreeSize=MCU_RAM_SIZE-(StackTopAddr-RAM_START_ADDRESS); //計算空閑區大小
printf ("棧 頂t=t%08XH n",StackTopAddr);
printf ("堆 頂t=t%08XH n",HeapTopAddr);
printf ("未分配區t=t%d Byten",RamFreeSize);
printf ("棧 區t=t%d Byten",APP_STACK_SIZE);
printf ("堆 區t=t%d Byten",APP_HEAP_SIZE);
printf ("公共變量區t=t%0d Byten",GlobalVarSize);
}
// --------------------------------------------------
int main (void)
{
SER_Init ();
printf ("------------- Hello World ------------n");
printf ("測試公共變量t=t%d tn",GetGloablVarSum());
printf ("測 堆區t=t%d tn",GetHeapVarSum());
TellAboutRam();
while (1)
{
;
}
}
仿真輸出如下圖所示:
總之弄清RAM分配情況很簡單,首先抓住棧地址特別是0x08000000這個地址得值,其次是弄清S文件中堆與棧的大小。還有什么好方法歡迎留言。






