基于STM32原子戰(zhàn)艦板內(nèi)存管理源碼
struct _m_mallco_dev //內(nèi)存管理控制器
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //內(nèi)存使用率
u8 *membase[2]; //內(nèi)存池 管理2個區(qū)域的內(nèi)存
u16 *memmap[2]; //內(nèi)存管理狀態(tài)表
u8 memrdy[2]; //內(nèi)存管理是否就緒
};
extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定義,定義全局變量,結(jié)構(gòu)體變量mallco_dev
void mymemset(void *s,u8 c,u32 count); //設(shè)置內(nèi)存
void mymemcpy(void *des,void *src,u32 n);//復(fù)制內(nèi)存
void mem_init(u8 memx); //內(nèi)存管理初始化函數(shù)(外/內(nèi)部調(diào)用)
u32 mem_malloc(u8 memx,u32 size); //內(nèi)存分配(內(nèi)部調(diào)用)
u8 mem_free(u8 memx,u32 offset); //內(nèi)存釋放(內(nèi)部調(diào)用)
u8 mem_perused(u8 memx); //獲得內(nèi)存使用率(外/內(nèi)部調(diào)用)
////////////////////////////////////////////////////////////////////////////////
//用戶調(diào)用函數(shù)
void myfree(u8 memx,void *ptr); //內(nèi)存釋放(外部調(diào)用)
void *mymalloc(u8 memx,u32 size); //內(nèi)存分配(外部調(diào)用)
void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配內(nèi)存(外部調(diào)用)
#endif
這部分代碼,定義了很多關(guān)鍵數(shù)據(jù),比如內(nèi)存塊大小的定義:MEM1_BLOCK_SIZE和MEM2_BLOCK_SIZE,都是32字節(jié)。內(nèi)存池總大小,內(nèi)部為40K,外部為200K(最大支持到近1M字節(jié),不過為了方便演示,這里只管理200K內(nèi)存)。MEM1_ALLOC_TABLE_SIZE和MEM2_ALLOC_TABLE_SIZE,則分別代表內(nèi)存池1和2的內(nèi)存管理表大小。
從這里可以看出,如果內(nèi)存分塊越小,那么內(nèi)存管理表就越大,當分塊為2字節(jié)1個塊的時候,內(nèi)存管理表就和內(nèi)存池一樣大了(管理表的每項都是u16類型)。顯然是不合適的,我們這里取32字節(jié),比例為1:16,內(nèi)存管理表相對就比較小了。
主函數(shù)部分:
int main(void)
{
u8 key;
u8 i=0;
u8 *p=0;
u8 *tp=0;
u8 paddr[18]; //存放的內(nèi)容“P Addr:+p地址的ASCII值”
u8 sramx=0; //默認為內(nèi)部sram
Stm32_Clock_Init(9); //系統(tǒng)時鐘設(shè)置
uart_init(72,9600); //串口初始化為9600
delay_init(72); //延時初始化
led_init(); //初始化與LED連接的硬件接口
LCD_Init(); //初始化LCD
usmart_dev.init(72); //初始化USMART
Key_Init(); //按鍵初始化
FSMC_SRAM_Init(); //初始化外部SRAM,因為用到了外部sram
mem_init(SRAMIN); //初始化內(nèi)部內(nèi)存池,SRAMIN==0
mem_init(SRAMEX); //初始化外部內(nèi)存池,SRAMEX==1
POINT_COLOR=RED;//設(shè)置字體為紅色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"MALLOC TEST");
LCD_ShowString(60,90,200,16,16,"WANG YAN");
LCD_ShowString(60,110,200,16,16,"2013/12/16");
LCD_ShowString(60,130,200,16,16,"key_right:Malloc key_left:Free");
LCD_ShowString(60,150,200,16,16,"wake_up:SRAMx key_down:Read");
POINT_COLOR=BLUE;//設(shè)置字體為藍色
LCD_ShowString(60,170,200,16,16,"SRAMIN");
LCD_ShowString(60,190,200,16,16,"SRAMIN USED: %");
LCD_ShowString(60,210,200,16,16,"SRAMEX USED: %");
while(1)
{
key=Key_Scan(0);//不支持連按
switch(key)
{
case 0://沒有按鍵按下
break;
case key_right: //KEY0按下
p=mymalloc(sramx,2048);//申請2K字節(jié),即64個內(nèi)存塊的空間
if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p寫入一些內(nèi)容
break;
case key_down: //KEY1按下
if(p!=NULL) //NULL==0;
{
sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內(nèi)容
// LCD_ShowString(60,270,200,16,16,p);
LCD_ShowString(60,250,200,16,16,p);//顯示P的內(nèi)容
printf("Memory Malloc Test%03d",i);//將“Memory Malloc Test”用串口輸出,利用串口助手可以看到輸出的結(jié)果
//"03"表示參數(shù)“i”的值只顯示3位,%-輸出控制符;d-將“i”以十進制的形式輸出;i的范圍0--255;輸出參數(shù)可以是多個,可以參考郝斌老師的相關(guān)視頻;
//輸出控制符包含:%Ld--L代表long類型;%c--代表字符類型;:%X--代表16進制并大寫;
}
break;
case key_left: //KEY2按下
myfree(sramx,p);//釋放內(nèi)存
p=0; //指向空地址
break;
case wake_up: //KEY UP按下
sramx=!sramx;//切換當前malloc/free操作對象
if(sramx)LCD_ShowString(60,170,200,16,16,"SRAMEX");
else LCD_ShowString(60,170,200,16,16,"SRAMIN");
break;
}
if(tp!=p)
{//在內(nèi)存paddr值處顯示:“P Addr:0X%08X”,“0X%08X”以大寫16進制顯示參數(shù)tp(32位),“08”表示8位數(shù)。0X AAAA AAAA
//剛進入程序時,因為執(zhí)行了“mem_init(SRAMIN);”初始化函數(shù),所以p==0;所以LCD不會有顯示
//因為程序一開始就有“u8 *tp=0;”,所以若不按下任何按鍵LCD就不會顯示下面的內(nèi)容(即“if(tp!=p)”控制的顯示內(nèi)容);
tp=p;//PAddr顯示的是指針p本身的地址值;指針值是u32類型
sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);//將指針p本身的地址值在LCD上打印出來即顯示
LCD_ShowString(60,230,200,16,16,paddr); //顯示p的地址
if(p)
LCD_ShowString(60,250,200,16,16,p);//顯示P的內(nèi)容,即指針p內(nèi)存儲的數(shù)據(jù)“Memory Malloc Test%03d”
else LCD_Fill(60,250,239,266,WHITE); //p=0,清除顯示
}
delay_ms(10);
i++;
if((i%20)==0)//DS0閃爍.
{
LCD_ShowNum(60+96,190,mem_perused(SRAMIN),3,16);//顯示內(nèi)部內(nèi)存使用率
LCD_ShowNum(60+96,210,mem_perused(SRAMEX),3,16);//顯示外部內(nèi)存使用率
led0=!led0;
}
}
}
總結(jié):通過內(nèi)存管理的學(xué)習(xí),更加深刻的領(lǐng)會到指針是c語言的靈魂,對c語言的知識是一個鞏固和提高;同時也學(xué)習(xí)到了sprintf()函數(shù)的運用技巧。
本章希望利用USMART調(diào)試內(nèi)存管理,所以在USMART里面添加了mymalloc和myfree兩個函數(shù),用于測試內(nèi)存分配和內(nèi)存釋放。大家可以通過USMART自行測試。
4,下載驗證:
在代碼編譯成功之后,我們通過下載代碼到ALIENTEK戰(zhàn)艦STM32開發(fā)板上,得到如圖所示界面:
可以看到,內(nèi)外內(nèi)存的使用率均為0%,說明還沒有任何內(nèi)存被使用,此時我們按下KEY0,就可以看到內(nèi)部內(nèi)存被使用5%(每按下一次申請2K的空間,lcd上顯示的使用率遞增5%;20*2K==40K)了,同時看到下面提示了指針p所指向的地址(其實就是被分配到的內(nèi)存地址)和內(nèi)容。多按幾次KEY0,可以看到內(nèi)存使用率持續(xù)上升(注意對比p的值,可以發(fā)現(xiàn)是遞減的,說明是從頂部開始分配內(nèi)存?。?,此時如果按下KEY2,可以發(fā)現(xiàn)內(nèi)存使用率降低了5%,但是再按KEY2將不再降低,說明“內(nèi)存泄露”了。這就是前面提到的對一個指針多次申請內(nèi)存,而之前申請的內(nèi)存又沒釋放,導(dǎo)致的“內(nèi)存泄露”。
按KEY_UP按鍵,可以切換當前操作內(nèi)存(內(nèi)部內(nèi)存/外部內(nèi)存),KEY1鍵用于更新p的內(nèi)容,更新后的內(nèi)容將重新顯示在LCD模塊上面。
本章,我們還可以借助USMART,測試內(nèi)存的分配和釋放,有興趣的朋友可以動手試試。如右圖USMART測試內(nèi)存管理函數(shù)所示。
/////////////////////////插補:printf和sprintf函數(shù)的用法////////////////////////////
printf和sprintf函數(shù)的用法非常重要,用于程序參數(shù)調(diào)試。這兩個函數(shù)都包含在系統(tǒng)啟動代碼“stdio.h”頭文件中;
1,例:printf("Memory Malloc Test%03d",i);//將“Memory Malloc Test”用串口輸出,利用串口助手可以看到輸出的結(jié)果;
"03"表示參數(shù)“i”的值只顯示3位,%d-輸出控制符;d-將“i”以十進制的形式輸出;i的范圍0--255(因為是u8類型);輸出參數(shù)可以是多個,可以參考郝斌老師的相關(guān)視頻;輸出控制符包含:%Ld--L代表long類型;%c--代表字符類型;:%X--代表16進制并大寫;%s-字符串類型
2,如何理解字符串打印函數(shù)int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));?
在內(nèi)存管理實驗中例如,sprintf((char*)p,"Memory Malloc Test%03d",i)函數(shù)的使用問題:
1),第一個形參(char*)p的意思是(第一個形參必須是指針類型),第二個形參即字符串“Memory Malloc Test%03d”存儲在內(nèi)存中的具體指針值,因為字符串是u8類型即char*類型,所以“(char*)p”與之呼應(yīng);因為第二個形參“Memory Malloc Test%03d”中有輸出控制符“%03d”,所以第一個形參(char*)p的值是變化的(因為參數(shù)“i”的值在變);這里輸出控制符“%03d”的意思可以參考printf()函數(shù);
也就是說,sprintf函數(shù)的第一個形參必須是指針類型,它是第二個形參(輸出內(nèi)容)在存儲器中存儲的首地址,是一個指針變量,第三個形參就是要輸出的參數(shù);所以說sprintf函數(shù)包含的內(nèi)容很多,作用很大。
2),sprintf函數(shù)的作用是在顯示屏中顯示相關(guān)參數(shù),即向p寫入一些內(nèi)容即Memory Malloc Test%03d”;
結(jié)合LCD_ShowString(60,270,200,16,16,p)的顯示結(jié)果更好理解,即顯示P的存儲內(nèi)容即在相應(yīng)的坐標處“Memory Malloc Test%03d”;”
3),例子:
u8 s[8];
char* who = "I"; //將字符“I”賦給char* 類型變量who;
char* whom = "STM32"; //將字符串“STM32”賦給char* 類型變量whom;
sprintf(s, "%s love %s.", who, whom); //產(chǎn)生:"I love STM32. " 這字符串寫到s中
LCD_ShowString(60,250,200,16,16,s);
//sprintf(s, "%10.3f", 3.1415626); //產(chǎn)生:" 3.142",浮點型顯示
4),sprintf函數(shù)一般情況下是用在需要字符顯示的場合,比如你要顯示一個數(shù)字,通常的做法是取出某一位然后加上0x30這個數(shù),這樣一位一位來比較麻煩,用sprintf這個函數(shù)呢,一次性就給你搞定了
比如你想打印3.1415926這個數(shù)值到液晶上顯示,通常的做法代碼就很多而且亂,有了這個函數(shù)呢,直接這樣
float PI=3.1415926;
u16 strbuffer[10];
sprintf(strbuffer,"PI=:%09d",PI);
然后直接將strbuffer這個數(shù)組送去顯示即可,或者打印到串口,這樣就可以直接字符顯示了
注意:sprintf函數(shù)必須結(jié)合LCD顯示函數(shù)使用才能有效!并且形參必須定義好合適的數(shù)據(jù)類型;sprintf()函數(shù)的最大作用就是非常方便的在LCD顯示屏上顯示自己想要的數(shù)據(jù)類型!參考關(guān)于sprintf函數(shù)的實驗。
3,疑問?
a,在51單片機中,如何將sprintf函數(shù)包含進51的啟動代碼中?如果不將sprintf函數(shù)包含進51的頭文件,顯示屏肯定不能用sprintf函數(shù)顯示數(shù)據(jù)。
b,在stdio.h中,找到的是int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));怎么看不到函數(shù)內(nèi)容?
sprintf是C語言標準庫提供的函數(shù), 包含在stdio.h中, 只要在文件頭#include 即可.
原型為int sprintf ( char * str, const char * format, ... );
關(guān)鍵詞:
STM32原子戰(zhàn)艦板內(nèi)存管
評論