STM32F4中的内存管理的实现

更多交流欢迎关注作者抖音号:81849645041

目的

        了解内部内存,外部内存,实现对内存的动态管理。

原理

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种,他们其实最终都是要实现 2 个函数:malloc 和 free;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 个内存管理表项目的值都清零,标记释放,完成一次内存释放。

准备

        MDK5 开发环境。

        STM32F4xx HAL库。

        STM32F407 开发板。

        STM32F4xx 参考手册。

        STM32F4xx 数据手册。

        STM32F407 开发板电路原理图。

步骤

  • 创建头文件bsp_mallco.h并定义引脚和相关读写函数。
#ifndef __BSP_SRAM_H
#ifndef __BSP_MALLOC_H
#define __BSP_MALLOC_H

#include "stm32f4xx.h"

#ifndef NULL
#define NULL 0
#endif

//定义三个内存池
#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 	//内存表大小

//内存管理控制器
struct _m_mallco_dev
{
	void (*init)(uint8_t);					//初始化
	uint8_t (*perused)(uint8_t);		  	    	//内存使用率
	uint8_t 	*membase[SRAMBANK];				//内存池 管理SRAMBANK个区域的内存
	uint16_t *memmap[SRAMBANK]; 				//内存管理状态表
	uint8_t  memrdy[SRAMBANK]; 				//内存管理是否就绪
};
extern struct _m_mallco_dev mallco_dev;	 //在mallco.c里面定义

void mymemset(void *s, uint8_t c, uint32_t count);	//设置内存
void mymemcpy(void *des, void *src, uint32_t n);//复制内存     
void my_mem_init(uint8_t memx);				//内存管理初始化函数(外/内部调用)
uint32_t my_mem_malloc(uint8_t memx, uint32_t size);	//内存分配(内部调用)
uint8_t my_mem_free(uint8_t memx, uint32_t offset);		//内存释放(内部调用)
uint8_t my_mem_perused(uint8_t memx);				//获得内存使用率(外/内部调用) 

//用户调用函数
void myfree(uint8_t memx, void *ptr);  			//内存释放(外部调用)
void *mymalloc(uint8_t memx, uint32_t size);			//内存分配(外部调用)
void *myrealloc(uint8_t memx, void *ptr, uint32_t size);//重新分配内存(外部调用)
#endif
  • 创建源文件bsp_mallco.c并实现读写操作函数
#include "bsp_mallco.h"

/***内存池(32字节对齐)***/
// 内部SRAM内存池
__align(32) uint8_t mem1base[MEM1_MAX_SIZE];
// 外部SRAM内存池
__align(32) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0x68000000)));
// 内部CCM内存池
__align(32) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0x10000000)));


/***内存管理表***/
// 内部SRAM内存池MAP
uint16_t mem1mapbase[MEM1_ALLOC_TABLE_SIZE];		
// 外部SRAM内存池MAP
uint16_t mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));	
// 内部CCM内存池MAP
uint16_t mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE)));	


/***内存管理参数***/
// 内存表大小 总字节/分块大小(32字节)
const uint32_t memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE, MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE};	
// 内存分块大小 32字节
const uint32_t memblksize[SRAMBANK]={MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE, MEM3_BLOCK_SIZE};					
// 内存总大小 总字节
const uint32_t memsize[SRAMBANK]={MEM1_MAX_SIZE, MEM2_MAX_SIZE, MEM3_MAX_SIZE};							


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

/**
*  函数名:mymemset
*  描述:写内存
*  输入:*s:内存首地址 
*				c :要设置的值 
*				count:需要设置的内存大小(字节为单位)
*  输出:void
*/
void mymemset(void *s, uint8_t c, uint32_t count)	//写内存
{
	uint8_t *xs = s;
	while(count--)
	{ 
		*xs++ = c;
	}
}

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

/**
*  函数名:my_mem_init
*  描述:内存管理初始化函数(外/内部调用)
*  输入:memx 所属内存块
*  输出:void
*/
void my_mem_init(uint8_t memx)
{
	// 内存状态表数据清零 
	mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx]*2);
	// 内存池所有数据清零
	mymemset(mallco_dev.membase[memx], 0, memsize[memx]);
	// 内存管理初始化OK
	mallco_dev.memrdy[memx] = 1;
}

/**
*  函数名:my_mem_malloc
*  描述:内存分配(内部调用)
*  输入: memx:所属内存块 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(!mallco_dev.memrdy[memx]) //未初始化,先执行初始化
	{ 
		mallco_dev.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(!mallco_dev.memmap[memx][offset]) // 连续空内存块数增加
		{ 
			cmemb++;
		}
		else // 连续内存块清零
		{ 
			cmemb=0;
		}
		if(cmemb == nmemb) // 找到了连续nmemb个空内存块
		{ 
			for(i=0;i<nmemb;i++) // 标注内存块非空
		  { 
				mallco_dev.memmap[memx][offset+i]=nmemb;
			}
			return offset*memblksize[memx]; // 返回偏移地址
		}
	}
	
	return 0xFFFFFFFF; // 未找到符合分配条件的内存块
}

/**
*  函数名:my_mem_free
*  描述:内存释放(内部调用)
*  输入: memx:所属内存块 offset:内存地址偏移
*  输出: 0,释放成功;1,释放失败;
*/
uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{
	uint32_t i;
	if(!mallco_dev.memrdy[memx]) // 未初始化,先执行初始化
	{ 
		mallco_dev.init(memx);
		return 1;
	}
	if(offset < memsize[memx]) // 偏移在内存池内. 
	{ 
		uint32_t index = offset/memblksize[memx]; // 偏移所在内存块号码
		uint32_t nmemb = mallco_dev.memmap[memx][index]; // 内存块数量
		for(i=0;i<nmemb;i++) // 内存块清零
	  { 
			mallco_dev.memmap[memx][index+i]=0;
		}
		return 0;
	}
	else{
		return 2; //偏移超区了
	}
}

/**
*  函数名:my_mem_perused
*  描述:获得内存使用率(外/内部调用)
*  输入:memx:所属内存块
*  输出:使用率(0~100)
*/
uint8_t my_mem_perused(uint8_t memx)
{
	uint32_t used = 0,i;
	for(i=0;i<memtblsize[memx];i++)
	{
		if(mallco_dev.memmap[memx][i])
		{
			used++;
		}
	}
	
	return (used*100)/memblksize[memx];
}


//用户调用函数
/**
*  函数名:myfree
*  描述:内存释放(外部调用)
*  输入:memx:所属内存块
*  输出:ptr:内存首地址
*/
void myfree(uint8_t memx, void *ptr)
{
	uint32_t offset;
	if(ptr == NULL)
	{
		return;
	}
	offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];
	my_mem_free(memx, offset);
}

/**
*  函数名:mymalloc
*  描述:内存分配(外部调用)
*  输入:memx:所属内存块 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)mallco_dev.membase[memx]+offset);
	}
}

/**
*  函数名:myrealloc
*  描述:重新分配内存(外部调用)
*  输入:memx:所属内存块 *ptr:旧内存首地址 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)mallco_dev.membase[memx]+offset), ptr, size);
		//释放旧内存
		myfree(memx, ptr);
		//返回新内存首地址
		return (void *)((uint32_t)mallco_dev.membase[memx]+offset);
	}
}
  • main.c中切换不同的内存,读写指定地址数据。
#include "bsp_clock.h"
#include "bsp_uart.h"
#include "bsp_key.h"
#include "bsp_sram.h" 
#include "bsp_led.h"
#include "bsp_mallco.h"
#include <string.h>

int main(void)
{
	uint8_t i=0;	   
	uint8_t *p=0;
	uint8_t sramx=0;					//默认为内部sram
	uint8_t tx[] = "SRAMEX Malloc Test";
	uint32_t n = sizeof(tx);
	uint8_t rx[n];
	
	CLOCLK_Init(); // 初始化系统时钟
	UART_Init(); // 串口初始化
	KEY_Init(); // 按键初始化
	LED_Init(); // LED初始化
	SRAM_Init(); // SRAM初始化
	
	my_mem_init(SRAMIN); //初始化内部内存池
	my_mem_init(SRAMEX); //初始化外部内存池
	my_mem_init(SRAMCCM);//初始化CCM内存池
	
	while(1)
	{
		uint8_t key = KEY_Scan(0);
		 if(key == 1) // KEY0按下
		 { 
			 p = mymalloc(sramx,2048); // 申请2K字节
			  
			 if(p!=NULL && sramx == 0) // 内部内存池
			 { 
				strcpy((char*)p,"SRAMIN Malloc Test"); // 向内部内存池写入一些内容
				HAL_Delay(100);
			  printf("%s \n",p); 
			 }
			 else if(p!=NULL && sramx == 2) // CCM内存池
			 { 
				strcpy((char*)p,"SRAMCCM Malloc Test"); // 向CCM内存池写入一些内容
				HAL_Delay(100);
			  printf("%s \n",p);  
			 }
			 else if(p!=NULL && sramx == 1) // 外部内存池
			 { 
				SRAM_WriteBuff(p,tx,n); // 向CCM内存池写入一些内容
				HAL_Delay(100);
				SRAM_ReadnBuff(p,rx,n);
				printf("%s \n",rx);
			 }
			 
			 myfree(sramx,p);
			  p=0;
			 i++;
		 }
		 
		 if(key == 2)
		 { // KEY1按下 切换SRAM
			sramx++;
			 if(sramx>2)
			 {
				 sramx=0;
			 }
			 
			 if(sramx == 0) printf("SRAMIN \n");
			 if(sramx == 1) printf("SRAMEX \n");
			 if(sramx == 2) printf("SRAMCCM \n"); 	
		 }
		 
		 HAL_Delay(50);
	}
}

现象

        把编译好的程序下载到开发板。Key1切换内存,key0在对应内存中写入数据后在读出数据打印出来。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奚海蛟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值