前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(44)STM32——内存管理实验

(44)STM32——内存管理实验

作者头像
小点点
发布2022-12-12 15:14:04
5970
发布2022-12-12 15:14:04
举报
文章被收录于专栏:小点点

目录

学习目标

原理

分配

释放

代码

内存管理控制器

内存管理宏定义

内存管理数组

总结 


学习目标

        本节我们要来学习的是内存管理实验,主要用来解决的问题其实和C语言中数组遇到的问题类似,就是我们不知道到底需要多少内存,为了避免数组越界,我们只能定义一个很大的数组,但是在单片机这种“寸土寸金”的地方就很不现实了,所以我们必须引入C语言中的内存管理函数malloc和free了。

原理

        我们采用的原理是分块式内存管理,主要就是通过内存管理表来得知哪些内存是空的,我们可以去占用,哪些内存已经用完了,需要被释放。具体内容如下所示:

        从上图可以看出,分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为 n 块,对应的内存管理表,大小也为 n,内存管理表的每一个项对应内存池的一块内存。         内存管理表的项值代表的意义为:当该项值为 0 的时候,代表对应的内存块未被占用,当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。比如某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。         内存分配方向如图所示,是从顶到底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。 

分配

        当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 项开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。注意,如果当内存不够的时候(找到最后也没找到连续的 m 块空闲内存),则返回 NULL 给 p,表示分配失败。

释放

        当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。

代码

内存管理控制器

代码语言:javascript
复制
//内存管理控制器
struct _m_mallco_dev
{
       void (*init)(u8);			//初始化
       u8 (*perused)(u8);		//内存使用率
       u8 *membase[SRAMBANK];	//SRAMBANK定义管理的内存片数
        //内存池 管理SRAMBANK个区域的内存,
       u16 *memmap[SRAMBANK];  	//内存管理状态表
       u8  memrdy[SRAMBANK]; 	//内存管理是否就绪
};
extern struct _m_mallco_dev mallco_dev;//在mallco.c里面定义

  • init,函数指针,指向内存初始化函数,用于初始化内存管理,带一个参数,表示要初始化的内存片。
  • perused,函数指针,指向内存使用率函数,用于获取内存使用率,,带一个参数,表示要获取内存使用率的内存片。
  • membase,内存池指针,指向内存池。最多有SRAMBANK个内存池。
  • memmap,内存管理表指针,指向内存管理表。最多有SRAMBANK个内存管理表。该指针为16位类型,因此,最大可以分配65535*内存块这么大的内存区域。 假定内存块大小为32字节,那么一次性最大可以申请的内存就是2M-32字节。
  • memrdy,内存管理表就绪标志,用于表示内存管理表是否已经初始化(清零)。最多有 SRAMBANK个内存管理表就绪标志。

内存管理宏定义

代码语言:javascript
复制
#define SRAMIN	 0		//内部内存池
#define SRAMEX   1		//外部内存池
#define SRAMCCM  2		//CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
#define SRAMBANK 	3	//定义支持的SRAM块数.	
 
//mem1内存参数设定.mem1完全处于内部SRAM里面.
#define MEM1_BLOCK_SIZE			32  	  						//内存块大小为32字节
#define MEM1_MAX_SIZE			100*1024  						//最大管理内存 100K
#define MEM1_ALLOC_TABLE_SIZE	MEM1_MAX_SIZE/MEM1_BLOCK_SIZE 	//内存表大小
 
//mem2内存参数设定.mem2的内存池处于外部SRAM里面
#define MEM2_BLOCK_SIZE			32  	  						//内存块大小为32字节
#define MEM2_MAX_SIZE			960 *1024  						//最大管理内存960K
#define MEM2_ALLOC_TABLE_SIZE	MEM2_MAX_SIZE/MEM2_BLOCK_SIZE 	//内存表大小
		 
//mem3内存参数设定.mem3处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!)
#define MEM3_BLOCK_SIZE			32  	  						//内存块大小为32字节
#define MEM3_MAX_SIZE			60 *1024  						//最大管理内存60K
#define MEM3_ALLOC_TABLE_SIZE	MEM3_MAX_SIZE/MEM3_BLOCK_SIZE 	//内存表大小

        探索者开发板外扩了SRAM,同时片内有SRAM和CCM之分,所以有3片内存区域,MEM1表示内部SRAM内存池(128K字节),MEM2表示外扩内存池(1024K字节)。MEM3表示内部CCM内存池(64K字节)。

内存管理数组

代码语言:javascript
复制
//内存池(32字节对齐),__align(32)是32位对齐的意思
__align(32) u8 mem1base[MEM1_MAX_SIZE];	
//内部SRAM内存池,__attribute__,是指定内存地址的意思
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));	
//外部SRAM内存池
__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000)));					
//内部CCM内存池
//内存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];													
//内部SRAM内存池MAP
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));	//外部SRAM内存池MAP
u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE)));	//内部CCM内存池MAP
//内存管理参数	   
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE};	//内存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE};					//内存分块大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE};							
//内存总大小

  • mem1base:内部SRAM内存池。u8类型,32字节对齐。      
  • mem2base:外部SRAM内存池。u8类型,32字节对齐。        
  • mem3base:内部CCM内存池。u8类型,32字节对齐。            
  • mem1mapbase:内部SRAM内存管理表。u16类型。        
  • mem2mapbase:外部SRAM内存管理表。u16类型。        
  • mem3mapbase:内部CCM内存管理表。u16类型。      
  • memtblsize:内存表大小。      
  • memblksize:内存分块大小。      
  • memsize:内存总大小。
代码语言:javascript
复制
// main.c
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

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);  //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	LED_Init();					//初始化LED 
 	LCD_Init();					//LCD初始化  
 	KEY_Init();					//按键初始化 
 	FSMC_SRAM_Init();			//初始化外部SRAM  
	
	my_mem_init(SRAMIN);		//初始化内部内存池
	my_mem_init(SRAMEX);		//初始化外部内存池
	my_mem_init(SRAMCCM);		//初始化CCM内存池
	
 	POINT_COLOR=RED;//设置字体为红色 
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");	
	LCD_ShowString(30,70,200,16,16,"MALLOC TEST");	   
	LCD_ShowString(30,90,200,16,16,"KEY0:Malloc  KEY2:Free");
	LCD_ShowString(30,110,200,16,16,"KEY_UP:SRAMx KEY1:Read"); 
 	POINT_COLOR=BLUE;//设置字体为蓝色 
	LCD_ShowString(30,130,200,16,16,"SRAMIN");
	LCD_ShowString(30,150,200,16,16,"SRAMIN  USED:   %");
	LCD_ShowString(30,170,200,16,16,"SRAMEX  USED:   %");
	LCD_ShowString(30,190,200,16,16,"SRAMCCM USED:   %");
 	while(1)
	{	
		key=KEY_Scan(0);//不支持连按	
		switch(key)
		{
			case 0://没有按键按下	
				break;
			case KEY0_PRES:	//KEY0按下
				p=mymalloc(sramx,2048);//申请2K字节
				if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容
				break;
			case KEY1_PRES:	//KEY1按下	   
				if(p!=NULL)
				{
					sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容 	 
					LCD_ShowString(30,270,200,16,16,p);			 //显示P的内容
				}
				break;
			case KEY2_PRES:	//KEY2按下	  
				myfree(sramx,p);//释放内存
				p=0;			//指向空地址
				break;
			case WKUP_PRES:	//KEY UP按下 
				sramx++; 
				if(sramx>2)sramx=0;
				if(sramx==0)LCD_ShowString(30,170,200,16,16,"SRAMIN ");
				else if(sramx==1)LCD_ShowString(30,170,200,16,16,"SRAMEX ");
				else LCD_ShowString(30,170,200,16,16,"SRAMCCM");
				break;
		}
		if(tp!=p)
		{
			tp=p;
			sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);
			LCD_ShowString(30,250,200,16,16,paddr);	//显示p的地址
			if(p)LCD_ShowString(30,270,200,16,16,p);//显示P的内容
		    else LCD_Fill(30,270,239,266,WHITE);	//p=0,清除显示
		}
		delay_ms(10);   
		i++;
		if((i%20)==0)//DS0闪烁.
		{
			LCD_ShowNum(30+104,150,my_mem_perused(SRAMIN),3,16);//显示内部内存使用率
			LCD_ShowNum(30+104,170,my_mem_perused(SRAMEX),3,16);//显示外部内存使用率
			LCD_ShowNum(30+104,190,my_mem_perused(SRAMCCM),3,16);//显示CCM内存使用率
 			LED0=!LED0;
 		}
	}	   
}

        如果对一个指针进行多次内存申请,而之前的申请又没释放,那么将造成“内存泄露”,这是内存管理所不希望发生的,久而久之,可能导致无内存可用的情况!所以,在使用的时候,一定记得,申请的内存在用完以后,一定要释放。 

总结 

        内存管理在单片机里面属于十分重要的内容,在面对内存空间不足的时候更是如此,所以对这部分内容还是得多加使用。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 学习目标
  • 原理
    • 分配
      • 释放
      • 代码
        • 内存管理控制器
          • 内存管理宏定义
            • 内存管理数组
            • 总结 
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档