STM32内存管理

Posted 果果小师弟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32内存管理相关的知识,希望对你有一定的参考价值。

动态内存管理

  • 根据需要分配內存和回收内存
    通常在一块较大且连续的内存空间上进行分配和回收
  • 动态内存管理解决的问题
    内存资源稀缺,通过内存复用增加任务的并发性
  • 动态内存管理的本质
    时间换空间,通过动态分配和回收“扩大”物理内存

动态内存管理的关键

  • 时间效率
    从发出内存申请到得内存的时间越短越好
  • 空间效率
    为了管理内存而占用的内存越少越好
  • 碎片化
    最大可分配内存占空闲内存总和的比例越大越好

动态内存管理的分类

  • 定长内存管理
    将内存分为大小相同的单元,每次申请一个单元的内存

  • 变长内存管理
    每次申请需要的内存(大小不固定,以字节为单位)

  • 定长内存管理的设计与实现
    将内存分为两部分:管理单元&分配单元
    管理单元与分配单元——对应

uint8_t mem2base[960*1024] __attribute__((at(0X68000000)));

表示在外部SRAM内存区域定义一个数组,这里使用的是FSMC的BANK1的NE3,对应的外部地址就是0X68000000。不明白的去看一看STM32的FSMC章节。

//内存池(32字节对齐)   
__align(32) uint8_t mem1base[100*1024];										//内部SRAM内存池 100K
__align(32) uint8_t mem2base[960*1024] __attribute__((at(0X68000000)));		//外部SRAM内存池 960K
__align(32) uint8_t mem3base[60 *1024] __attribute__((at(0X10000000)));		//内部CCM内存池 60K

//内存管理表
uint16_t mem1mapbase[100*1024/32];											//内部SRAM内存池MAP
uint16_t mem2mapbase[960*1024/32] __attribute__((at(0X68000000+960*1024)));	//外部SRAM内存池MAP
uint16_t mem3mapbase[60 *1024/32] __attribute__((at(0X10000000+60*1024)));	//内部CCM内存池MAP

//内存管理参数	   
const uint32_t memtblsize[SRAMBANK]={100*1024/32,960*1024/32,60 *1024/32};	//内存表大小
const uint32_t memblksize[SRAMBANK]={32,32,32};					            //内存分块大小
const uint32_t memsize   [SRAMBANK]={100*1024,960*1024,60*1024};			//内存总大小

malloc.c

#include "malloc.h"	   


//内存管理就绪
#define  MEMRDY   1
#define  MEMBSY   0

//内存池(32字节对齐)   
__align(32) uint8_t mem1base[100*1024];										//内部SRAM内存池 100K
__align(32) uint8_t mem2base[960*1024] __attribute__((at(0X68000000)));		//外部SRAM内存池 960K
__align(32) uint8_t mem3base[60 *1024] __attribute__((at(0X10000000)));		//内部CCM内存池 60K

//内存管理表 32字节为单位
uint16_t mem1mapbase[100*1024/32];											//内部SRAM内存池MAP
uint16_t mem2mapbase[960*1024/32] __attribute__((at(0X68000000+960*1024)));	//外部SRAM内存池MAP
uint16_t mem3mapbase[60 *1024/32] __attribute__((at(0X10000000+60*1024)));	//内部CCM内存池MAP

//内存管理参数	   
const uint32_t memtblsize[SRAMBANK]={100*1024/32,960*1024/32,60 *1024/32};	//内存表大小
const uint32_t memblksize[SRAMBANK]={32,32,32};					            //内存分块大小
const uint32_t memsize   [SRAMBANK]={100*1024,960*1024,60*1024};			//内存总大小

//内存管理控制器
MALLOC_EDV_T  g_mallcoDev = 
{
	my_mem_init,					    	      //内存初始化
	my_mem_perused,						      //内存使用率
	mem1base, mem2base, mem3base,			     //内存池
	mem1mapbase, mem2mapbase, mem3mapbase,  //内存管理状态表
	MEMBSY, MEMBSY, MEMBSY,	    	           //内存管理未就绪
};


/************************************************************************
** 函数名称: mymemcpy									
** 函数功能: 复制内存
** 入口参数: void *des:目的地址
**           void *src:原地址
**           uint32_t n:  需要复制的内存长度(以字节为单位) 
** 出口参数: 												
************************************************************************/
void mymemcpy(void *des,void *src,uint32_t n)  
{  
	uint8_t *xdes=des;
	uint8_t *xsrc=src; 
	while(n--)
		*xdes++=*xsrc++;  
} 


/************************************************************************
** 函数名称: mymemset									
** 函数功能: 设置内存
** 入口参数: void *s:内存首地址
**           uint8_t c:要设置的值
**           uint32_t count:  需要设置的内存长度(以字节为单位) 
** 出口参数: 												
************************************************************************/
void mymemset(void *s,uint8_t c,uint32_t count)  
{  
	uint8_t *xs = s;  
	while(count--)
		*xs++=c;  
}	   


/************************************************************************
** 函数名称: my_mem_init									
** 函数功能: 内存管理初始化  
** 入口参数: uint8_t memx:所属内存块 
** 出口参数: 												
************************************************************************/
void my_mem_init(uint8_t memx)  
{  
	//清除内存状态表-每一个内存块对应状态表中的一项
	//因为内存管理状态表示16位的,所以需要乘以2
	mymemset( g_mallcoDev.memmap[memx], 0, memtblsize[memx]*2); 
	//把所管理的内存池全部清理
	mymemset(g_mallcoDev.membase[memx], 0, memsize[memx]); 
	//表明对应的内存管理已经就绪
	g_mallcoDev.memrdy[memx] = MEMRDY;
}  

/************************************************************************
** 函数名称: my_mem_perused									
** 函数功能: 计算内存使用率
** 入口参数: uint8_t memx:所属内存块 
** 出口参数: 使用率(0~100)												
************************************************************************/
uint8_t my_mem_perused(uint8_t memx)  
{  
	uint32_t used=0;  
	uint32_t i;  
	
	//逐一对每个表项进行统计
	for(i=0;i<memtblsize[memx];i++)  
	{  
		  //如果表项值不为0就表示对应内存块被使用了
			if(g_mallcoDev.memmap[memx][i])
				used++; 
	} 
	
	//计算百分比
	return (used*100)/(memtblsize[memx]);  
}

/************************************************************************
** 函数名称: my_mem_malloc									
** 函数功能: 内存分配(内部调用)
** 入口参数: uint8_t memx:所属内存块 
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 0xFFFFFFFF-分配错误 其他-内存偏移地址 											
************************************************************************/
uint32_t my_mem_malloc(uint8_t memx,uint32_t size)  
{  
	signed long offset=0;  
	uint32_t nmemb;	//需要的内存块数  
	uint32_t cmemb = 0;//连续空内存块数
	uint32_t i;  
	
	//未初始化,先执行初始化 
	if(g_mallcoDev.memrdy[memx] == MEMBSY)
		g_mallcoDev.init(memx);
	
	//不需要分配
  if(size == 0)
		return 0xFFFFFFFF;
	
	//获取需要分配的连续内存块数
  nmemb = size/memblksize[memx];  
	//超出不满一个内存块的部分也要申请一个内存块
  if(size%memblksize[memx])
		nmemb++;  
	
	//搜索内存块,从后往前搜索
	for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区  
	{    
		//如果内存块没有被使用就代表找到一个空块
		if(g_mallcoDev.memmap[memx][offset] == 0)
		{
			//连续空内存块数增加
			cmemb++;
		}
		else 
		{
			//连续内存块清零--只要找到一个被使用的就表示不连续了,要重新开始找
			cmemb=0;								
		}
		
		//找到了连续nmemb个空内存块
		if(cmemb == nmemb)							
		{
			//标注内存块非空--并且是连续nmemb个非空块
			for(i=0;i<nmemb;i++)  					
			{  
				g_mallcoDev.memmap[memx][offset+i] = nmemb;  
			}  
			//返回偏移地址 
			return (offset*memblksize[memx]); 
		}
	}  
	//未找到符合分配条件的内存块 
	return 0xFFFFFFFF; 
}  

/************************************************************************
** 函数名称: my_mem_free									
** 函数功能: 释放内存(内部调用) 
** 入口参数: uint8_t memx:所属内存块
**           uint32_t offset:内存地址偏移
** 出口参数: 0-释放成功;1-释放失败										
************************************************************************/
uint8_t my_mem_free(uint8_t memx,uint32_t offset)  
{  
	int i; 

  //未初始化,先执行初始化	
	if(g_mallcoDev.memrdy[memx] == MEMBSY)
	{
		g_mallcoDev.init(memx);    
    return 1;//未初始化  
   }  
	
	//偏移在内存池内. 
	if(offset < memsize[memx])
	{  
		int index = offset/memblksize[memx];			//偏移所在内存块号码  
		int nmemb = g_mallcoDev.memmap[memx][index];	//内存块数量
		
		for(i=0;i<nmemb;i++)  						//内存块清零
		{  
			g_mallcoDev.memmap[memx][index+i] = 0;  
		}  
		return 0;  
	}
	else 
		return 2;//偏移超区了.  
}  

/************************************************************************
** 函数名称: myfree									
** 函数功能: 释放内存(外部调用) 
** 入口参数: uint8_t memx:所属内存块
**           void *ptr:内存地址偏移
** 出口参数: 无									
************************************************************************/
void myfree(uint8_t memx,void *ptr)  
{  
	uint32_t offset; 
	
  //地址为0. 
	if(ptr==NULL)
		return; 
	
	//计算指针在相应内存块中的偏移量
 	offset = (uint32_t)ptr-(uint32_t)g_mallcoDev.membase[memx]; 

  //释放内存 	
  my_mem_free(memx,offset);	     
}

/************************************************************************
** 函数名称: mymalloc									
** 函数功能: 内存分配(内部调用)
** 入口参数: uint8_t memx:所属内存块 
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 分配到的内存首地址.											
************************************************************************/
void *mymalloc(uint8_t memx,uint32_t size)  
{  
	uint32_t offset;   
	
	offset=my_mem_malloc(memx,size); 
	
	if(offset == 0xFFFFFFFF)
		return NULL;  
	else 
		return (void*)((uint32_t)g_mallcoDev.membase[memx]+offset);  
}  

/************************************************************************
** 函数名称: myrealloc									
** 函数功能: 重新分配内存(外部调用)
** 入口参数: uint8_t memx:所属内存块 
**           void *ptr:旧内存首地址
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 新分配到的内存首地址.										
************************************************************************/
void *myrealloc(uint8_t memx,void *ptr,uint32_t size)  
{  
	uint32_t offset;    
	
	//先申请一段内存
	offset = my_mem_malloc(memx,size);  

	//申请失败
	if(offset == 0xFFFFFFFF)
		return NULL;     
	else  
	{  		
    //拷贝旧内存内容到新内存  		
		mymemcpy((void*)((uint32_t)g_mallcoDev.membase[memx]+offset),ptr,size);	 
		
		//释放旧内存
		myfree(memx,ptr);  			
		
		//返回新内存首地址		
		return (void*)((uint32_t)g_mallcoDev.membase[memx]+offset);  				
	}  
}

malloc.h

#ifndef __MALLOC_H
#define __MALLOC_H

#include "main.h"

#ifndef NULL
    #define NULL 0
#endif

//定义三个内存池
#define SRAMIN	 0		//内部内存池
#define SRAMEX   1		//外部内存池
#define SRAMCCM  2		//CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
#define SRAMBANK 	3	//定义支持的SRAM块数.	

//内存管理控制器
typedef struct
{
    void      (*init)(uint8_t);					      //初始化
    uint8_t   (*perused)(uint8_t);		  	    //内存使用率
    uint8_t    *membase[SRAMBANK];				//内存池 管理SRAMBANK个区域的内存
    uint16_t  *memmap[SRAMBANK]; 				//内存管理状态表
    uint8_t   memrdy[SRAMBANK]; 			   	//内存管理是否就绪
}MALLOC_EDV_T;

extern MALLOC_EDV_T  g_mallcoDev;	 //在mallco.c里面定义
/************************************************************************
** 函数名称: mymemcpy
** 函数功能: 复制内存
** 入口参数: void *des:目的地址
**           void *src:原地址
**           uint32_t n:  需要复制的内存长度(以字节为单位)
** 出口参数:
************************************************************************/
void mymemcpy(void *des, void *src, uint32_t n);

/************************************************************************
** 函数名称: mymemset
** 函数功能: 设置内存
** 入口参数: void *s:内存首地址
**           uint8_t c:要设置的值
**           uint32_t count:  需要设置的内存长度(以字节为单位)
** 出口参数:
************************************************************************/
void mymemset(void *s, uint8_t c, uint32_t count);

/************************************************************************
** 函数名称: my_mem_init
** 函数功能: 内存管理初始化
** 入口参数: uint8_t memx:所属内存块
** 出口参数:
************************************************************************/
void my_mem_init(uint8_t memx);

/************************************************************************
** 函数名称: my_mem_perused
** 函数功能: 计算内存使用率
** 入口参数: uint8_t memx:所属内存块
** 出口参数: 使用率(0~100)
************************************************************************/
uint8_t my_mem_perused(uint8_t memx);

/************************************************************************
** 函数名称: my_mem_malloc
** 函数功能: 内存分配(内部调用)
** 入口参数: uint8_t memx:所属内存块
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 0xFFFFFFFF-分配错误 其他-内存偏移地址
************************************************************************/
uint32_t my_mem_malloc(uint8_t memx, uint32_t size);

/************************************************************************
** 函数名称: my_mem_free
** 函数功能: 释放内存(内部调用)
** 入口参数: uint8_t memx:所属内存块
**           uint32_t offset:内存地址偏移
** 出口参数: 0-释放成功;1-释放失败
************************************************************************/
uint8_t my_mem_free(uint8_t memx, uint32_t offset);

/************************************************************************
** 函数名称: myfree
** 函数功能: 释放内存(外部调用)
** 入口参数: uint8_t memx:所属内存块
**           void *ptr:内存地址偏移
** 出口参数: 无
************************************************************************/
void myfree(uint8_t memx, void *ptr);

/************************************************************************
** 函数名称: mymalloc
** 函数功能: 内存分配(内部调用)
** 入口参数: uint8_t memx:所属内存块
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 分配到的内存首地址.
************************************************************************/
void *mymalloc(uint8_t memx, uint32_t size);

/************************************************************************
** 函数名称: myrealloc
** 函数功能: 重新分配内存(外部调用)
** 入口参数: uint8_t memx:所属内存块
**           void *ptr:旧内存首地址
**           uint32_t size:要分配的内存大小(字节)
** 出口参数: 新分配到的内存首地址.
************************************************************************/
void *myrealloc(uint8_t memx, void *ptr, uint32_t size);

#endif

用法:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "lcd.h"
#include "usmart.h"
#include "sram.h"
#include "malloc.h" 


int main(void)
{
	u8 key;		 
 	u8 i=0;	    
	u8 *p=0;
	
  HAL_Init();                   	//初始化HAL库    
  Stm32_Clock_Init(336,8,2,7);  	//设置时钟,168Mhz
	delay_init(168);               	//初始化延时函数

	KEY_Init();						//初始化KEY
	SRAM_Init();					//初始化外部SRAM 
	my_mem_init(SRAMEX);			//初始化外部内存池	
 	while(1)
	{	
		key=KEY_Scan(0);//不支持连按	
		switch(key)
		{
			case 0://没有按键按下	
				break;
			case KEY0_PRES:	//KEY0按下
				p=mymalloc(SRAMEX,10240);//申请10K字节
				break;
			case KEY1_PRES:	//KEY1按下	   
				if(p!=NULL)
				{
					sprintf((char*)p,"%d",i);//更新显示内容 	 
					LCD_ShowString(30,270,200,16,STM32单片机内存管理器代码,可直接用于工程

STM32 内存分配解析及变量的存储位置

STM32内存管理

STM32内存管理

《嵌入式 - 深入剖析STM32》深入理解STM32内存管理

《嵌入式 - 深入剖析STM32》深入理解STM32内存管理