新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 基于STM32原子战舰板内存管理源码

基于STM32原子战舰板内存管理源码

作者: 时间:2016-12-01 来源:网络 收藏
struct _m_mallco_dev //内存管理控制器
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //内存使用率
u8 *membase[2]; //内存池 管理2个区域的内存
u16 *memmap[2]; //内存管理状态表
u8 memrdy[2]; //内存管理是否就绪
};
extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义,定义全局变量,结构体变量mallco_dev
void mymemset(void *s,u8 c,u32 count); //设置内存
void mymemcpy(void *des,void *src,u32 n);//复制内存
void mem_init(u8 memx); //内存管理初始化函数(外/内部调用)
u32 mem_malloc(u8 memx,u32 size); //内存分配(内部调用)
u8 mem_free(u8 memx,u32 offset); //内存释放(内部调用)
u8 mem_perused(u8 memx); //获得内存使用率(外/内部调用)
////////////////////////////////////////////////////////////////////////////////
//用户调用函数
void myfree(u8 memx,void *ptr); //内存释放(外部调用)
void *mymalloc(u8 memx,u32 size); //内存分配(外部调用)
void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配内存(外部调用)
#endif
这部分代码,定义了很多关键数据,比如内存块大小的定义:MEM1_BLOCK_SIZE和MEM2_BLOCK_SIZE,都是32字节。内存池总大小,内部为40K,外部为200K(最大支持到近1M字节,不过为了方便演示,这里只管理200K内存)。MEM1_ALLOC_TABLE_SIZE和MEM2_ALLOC_TABLE_SIZE,则分别代表内存池1和2的内存管理表大小。
从这里可以看出,如果内存分块越小,那么内存管理表就越大,当分块为2字节1个块的时候,内存管理表就和内存池一样大了(管理表的每项都是u16类型)。显然是不合适的,我们这里取32字节,比例为1:16,内存管理表相对就比较小了。
主函数部分:
int main(void)
{
u8 key;
u8 i=0;
u8 *p=0;
u8 *tp=0;
u8 paddr[18]; //存放的内容“P Addr:+p地址的ASCII值”
u8 sramx=0; //默认为内部sram
Stm32_Clock_Init(9); //系统时钟设置
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); //初始化内部内存池,SRAMIN==0
mem_init(SRAMEX); //初始化外部内存池,SRAMEX==1
POINT_COLOR=RED;//设置字体为红色
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;//设置字体为蓝色
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字节,即64个内存块的空间
if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容
break;
case key_down: //KEY1按下
if(p!=NULL) //NULL==0;
{
sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容
// LCD_ShowString(60,270,200,16,16,p);
LCD_ShowString(60,250,200,16,16,p);//显示P的内容
printf("Memory Malloc Test%03d",i);//将“Memory Malloc Test”用串口输出,利用串口助手可以看到输出的结果
//"03"表示参数“i”的值只显示3位,%-输出控制符;d-将“i”以十进制的形式输出;i的范围0--255;输出参数可以是多个,可以参考郝斌老师的相关视频;
//输出控制符包含:%Ld--L代表long类型;%c--代表字符类型;:%X--代表16进制并大写;
}
break;
case key_left: //KEY2按下
myfree(sramx,p);//释放内存
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)
{//在内存paddr值处显示:“P Addr:0X%08X”,“0X%08X”以大写16进制显示参数tp(32位),“08”表示8位数。0X AAAA AAAA
//刚进入程序时,因为执行了“mem_init(SRAMIN);”初始化函数,所以p==0;所以LCD不会有显示
//因为程序一开始就有“u8 *tp=0;”,所以若不按下任何按键LCD就不会显示下面的内容(即“if(tp!=p)”控制的显示内容);
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的内容,即指针p内存储的数据“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);//显示内部内存使用率
LCD_ShowNum(60+96,210,mem_perused(SRAMEX),3,16);//显示外部内存使用率
led0=!led0;
}
}
}
总结:通过内存管理的学习,更加深刻的领会到指针是c语言的灵魂,对c语言的知识是一个巩固和提高;同时也学习到了sprintf()函数的运用技巧。
本章希望利用USMART调试内存管理,所以在USMART里面添加了mymalloc和myfree两个函数,用于测试内存分配和内存释放。大家可以通过USMART自行测试。
4,下载验证:
在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,得到如图所示界面:
可以看到,内外内存的使用率均为0%,说明还没有任何内存被使用,此时我们按下KEY0,就可以看到内部内存被使用5%(每按下一次申请2K的空间,lcd上显示的使用率递增5%;20*2K==40K)了,同时看到下面提示了指针p所指向的地址(其实就是被分配到的内存地址)和内容。多按几次KEY0,可以看到内存使用率持续上升(注意对比p的值,可以发现是递减的,说明是从顶部开始分配内存!),此时如果按下KEY2,可以发现内存使用率降低了5%,但是再按KEY2将不再降低,说明“内存泄露”了。这就是前面提到的对一个指针多次申请内存,而之前申请的内存又没释放,导致的“内存泄露”。
按KEY_UP按键,可以切换当前操作内存(内部内存/外部内存),KEY1键用于更新p的内容,更新后的内容将重新显示在LCD模块上面。
本章,我们还可以借助USMART,测试内存的分配和释放,有兴趣的朋友可以动手试试。如右图USMART测试内存管理函数所示。
/////////////////////////插补:printf和sprintf函数的用法////////////////////////////
printf和sprintf函数的用法非常重要,用于程序参数调试。这两个函数都包含在系统启动代码“stdio.h”头文件中;
1,例:printf("Memory Malloc Test%03d",i);//将“Memory Malloc Test”用串口输出,利用串口助手可以看到输出的结果;
"03"表示参数“i”的值只显示3位,%d-输出控制符;d-将“i”以十进制的形式输出;i的范围0--255(因为是u8类型);输出参数可以是多个,可以参考郝斌老师的相关视频;输出控制符包含:%Ld--L代表long类型;%c--代表字符类型;:%X--代表16进制并大写;%s-字符串类型
2,如何理解字符串打印函数int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));?
在内存管理实验中例如,sprintf((char*)p,"Memory Malloc Test%03d",i)函数的使用问题:
1),第一个形参(char*)p的意思是(第一个形参必须是指针类型),第二个形参即字符串“Memory Malloc Test%03d”存储在内存中的具体指针值,因为字符串是u8类型即char*类型,所以“(char*)p”与之呼应;因为第二个形参“Memory Malloc Test%03d”中有输出控制符“%03d”,所以第一个形参(char*)p的值是变化的(因为参数“i”的值在变);这里输出控制符“%03d”的意思可以参考printf()函数;
也就是说,sprintf函数的第一个形参必须是指针类型,它是第二个形参(输出内容)在存储器中存储的首地址,是一个指针变量,第三个形参就是要输出的参数;所以说sprintf函数包含的内容很多,作用很大。
2),sprintf函数的作用是在显示屏中显示相关参数,即向p写入一些内容即Memory Malloc Test%03d”;
结合LCD_ShowString(60,270,200,16,16,p)的显示结果更好理解,即显示P的存储内容即在相应的坐标处“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); //产生:"I love STM32. " 这字符串写到s中
LCD_ShowString(60,250,200,16,16,s);
//sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142",浮点型显示
4),sprintf函数一般情况下是用在需要字符显示的场合,比如你要显示一个数字,通常的做法是取出某一位然后加上0x30这个数,这样一位一位来比较麻烦,用sprintf这个函数呢,一次性就给你搞定了
比如你想打印3.1415926这个数值到液晶上显示,通常的做法代码就很多而且乱,有了这个函数呢,直接这样
float PI=3.1415926;
u16 strbuffer[10];
sprintf(strbuffer,"PI=:%09d",PI);
然后直接将strbuffer这个数组送去显示即可,或者打印到串口,这样就可以直接字符显示了
注意:sprintf函数必须结合LCD显示函数使用才能有效!并且形参必须定义好合适的数据类型;sprintf()函数的最大作用就是非常方便的在LCD显示屏上显示自己想要的数据类型!参考关于sprintf函数的实验。
3,疑问?
a,在51单片机中,如何将sprintf函数包含进51的启动代码中?如果不将sprintf函数包含进51的头文件,显示屏肯定不能用sprintf函数显示数据。
b,在stdio.h中,找到的是int sprintf(char * __restrict /*s*/, const char * __restrict /*format*/, ...) __attribute__((__nonnull__(1,2)));怎么看不到函数内容?
sprintf是C语言标准库提供的函数, 包含在stdio.h中, 只要在文件头#include 即可.
原型为int sprintf ( char * str, const char * format, ... );
/////////////////////////插补:printf和sprintf函数的用法////////////////////////////
上一页 1 2 3 下一页

评论


技术专区

关闭