STM32学习心得三十六:内存管理实验

记录一下,方便以后翻阅~
主要内容:
1)内存管理概述及原理;
2)相关实验代码解读。
官方资料:《STM32中文参考手册_V10》-第19章 灵活的静态存储器控制器(FSMC)。
实验要求:系统启动后,等待外部输入。KEY0用于申请内存,每次申请2K字节内存。KEY1用于写数据到申请到的内存里面。KEY2用于释放内存。KEY_UP用于切换操作内存区(内部SRAM内存/外部SRAM内存)。DS0用于指示程序运行状态。

1. 内存管理概述及原理
1.1 什么是内存管理
内存管理,是指软件运行时对MCU内存资源的分配和使用的技术。目的是:如何高效,快速的分配,并且在适当的时候释放和回收内存资源。
内存管理的实现方法最终基于2个函数:malloc和free:
malloc函数用于内存申请;
free函数用于内存释放。

1.2 分快式内存管理——原理
分块式内存管理由内存池和内存管理表两部分组成:
内存池被等分为n块,对应的内存管理表,大小也为n,内存管理表的每一个项对应内存池的一块内存。
内存管理表的项值意义:当该项值为0时,代表对应的内存块未被占用,当该项值为非零时,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。
举例:比如某项值为10,说明包括本项对应的内存块在内,总共分配了10个内存块给外部的某个指针。
在这里插入图片描述
内寸分配方向:从顶à底的分配方向:即先从最末端开始找空内存。
当内存管理刚初始化的时候,内存管理表全部清零,表示没有任何内存块被占用。

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

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

2. 内存管理控制器函数意义
在这里插入图片描述
1)*init(),函数指针,指向内存初始化函数,用于初始化内存管理,带一个入口参数,表示要初始化的内存片;
2)u8 (*perused) (u8),函数指针,指向内存使用率函数,用于获取内存使用率,,带一个入口参数,表示要获取内存使用率的内存片;
3)*membase,内存池指针,指向内存池。最多有SRAMBANK个内存池(本次使用的开发版有2个);
4)*memmap,内存管理表指针,指向内存管理表。最多有SRAMBANK个内存管理表。该指针为u16类型,因此,最大可以分配216个内存块。设一个内存块为32字节,那么一次性最大可以申请的内存就是2M-32字节;
5)memrdy,内存管理表就绪标志,用于表示内存管理表是否已经初始化(清零)。最多有SRAMBANK个内存管理表就绪标志。
在这里插入图片描述
本开发板外扩一个SRAM,共有2片内存区域,MEM1表示内部内存池(64K字节),MEM2表示外扩内存池(1024K字节)。
在这里插入图片描述
mem1base:内部内存池。u8类型,32字节对齐;
mem2base:外部内存池。u8类型,32字节对齐;
mem1mapbase:内部内存管理表。u16类型;
mem2mapbase:外部内存管理表。u16类型;
memtblsize:内存表大小;
memblksize:内存分块大小;
memsize:内存总大小。

3. 部分源码讲解
3.1 malloc.h头文件代码解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: malloc.h
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年10月28日
 * @摘要简述: MALLOC头文件
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
#ifndef __MALLOC_H
#define __MALLOC_H
#include "stm32f10x.h"
#ifndef NULL
#define NULL 0
#endif
/* 宏定义 -两个内存池---------------------------------------------------------------*/
#define SRAMIN   0    /* 内部内存池 */
#define SRAMEX    1   /* 外部内存池 */ 
#define SRAMBANK  2   /* 因为有两个内存池,所以SRAM块数为2 */ 
/* mem1 内部内存池参数设定 ---------------------------------------------------------------*/
#define MEM1_BLOCK_SIZE   32                                 /* 一个内存块大小为32字节 */
#define MEM1_MAX_SIZE     40*1024                            /* 最大管理内存 40K 字节 */
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE  /* 对应内存表大小为40*1024/32 = 1280 */
/* mem2 外部内存池参数设定 ---------------------------------------------------------------*/
#define MEM2_BLOCK_SIZE   32                                 /* 一个内存块大小为32字节 */
#define MEM2_MAX_SIZE     960 *1024                          /* 最大管理内存 960K 字节 */
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE  /* 对应内存表大小为960*1024/32 = 30720 */ 
/* 内存管理结构体 ---------------------------------------------------------------*/
struct _m_mallco_dev
{
    
    
 void (*init)(u8);         /* 初始化 */
 u8 (*perused)(u8);        /* 内存使用率 */
 u8  *membase[SRAMBANK];   /* 内存池,管理SRAMBANK个区域的内存 */
 u16 *memmap[SRAMBANK];    /* 内存管理状态表 */
 u8  memrdy[SRAMBANK];     /* 内存管理是否就绪 */
};
extern struct _m_mallco_dev mallco_dev;  /* 在mallco.c里面定义 */
/* 函数申明 -------------------------------------------------------------------*/
void mymemset(void *s,u8 c,u32 count);            /* 设置内存 */
void mymemcpy(void *des,void *src,u32 n);         /* 复制内存 */     
void my_mem_init(u8 memx);                        /* 内存管理初始化函数(外/内部调用) */
u32 my_mem_malloc(u8 memx,u32 size);              /* 内存分配(内部调用) */
u8 my_mem_free(u8 memx,u32 offset);               /* 内存释放(内部调用) */
u8 my_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                                            /* __MALLOC_H */
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

3.2 malloc.c源文件解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: malloc.c
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年10月28日
 * @摘要简述: MALLOC源文件
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
 /* 包含头文件------------------------------------------------------------------*/
#include "malloc.h" 
/* 内存管理数组参数-内存池大小(32字节对齐)-----------------------------------------*/
__align(32) u8 mem1base[MEM1_MAX_SIZE];                                   /* 内部SRAM内存池,MEM1_MAX_SIZE = 40*1024 */
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));   /* 外部SRAM内存池,MEM2_MAX_SIZE = 960 *1024,起始地址从0x68000000开始 */
/* 内存管理数组参数-内存管理表大小--------------------------------------------------*/
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];                                                /* 内部SRAM内存池MAP,1280 */
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));  /* 外部SRAM内存池MAP,30720 */ 
/* 内存管理数组参数----------------------------------------------------------------*/
const u32 memtblsize[SRAMBANK]={
    
    MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};   /* 内存表大小 */
const u32 memblksize[SRAMBANK]={
    
    MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE};               /* 内存分块大小 */
const u32 memsize[SRAMBANK]={
    
    MEM1_MAX_SIZE,MEM2_MAX_SIZE};                      /* 内存总大小 */
/* 内存管理结构体 ---------------------------------------------------------------*/
struct _m_mallco_dev mallco_dev=
{
    
    
 my_mem_init,              /* 内存初始化 */
 my_mem_perused,           /* 内存使用率 */
 mem1base,mem2base,        /* 内存池 */
 mem1mapbase,mem2mapbase,  /* 内存管理状态表 */
 0,0,                      /* 内存管理未就绪,0未就绪,1就绪 */
};
/******************************************************************
函数名称:mymemcpy(void *des,void *src,u32 n)  
函数功能:复制内存
入口参数:*des:目的地址,*src:源地址,n:需要复制的内存长度(字节为单位)
返回参数:无
修改作者:Aaron
*******************************************************************/
void mymemcpy(void *des,void *src,u32 n)  
{
    
      
 u8 *xdes=des;             
 u8 *xsrc=src; 
 while(n--)
  *xdes++=*xsrc++;  
}  
/************************************************************************
函数名称:mymemset(void *s,u8 c,u32 count)   
函数功能:设置内存
入口参数:*s:内存首地址,c :要设置的值,count:需要设置的内存大小(字节为单位)
返回参数:无
修改作者:Aaron
*************************************************************************/
void mymemset(void *s,u8 c,u32 count)  
{
    
      
 u8 *xs = s;  
 while(count--)
  *xs++=c;  
}  
/************************************************************************
函数名称:my_mem_init(u8 memx)   
函数功能:内存管理初始化
入口参数:u8 memx:所属内存块
返回参数:无
修改作者:Aaron
*************************************************************************/
void my_mem_init(u8 memx)  
{
    
      
 mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx]*2);   /* 内存状态表数据清零,内存池单位为u8, 管理表单位为u16,要清空管理表,则要统计管理表占用的实际字节数 */  
 mymemset(mallco_dev.membase[memx], 0, memsize[memx]);       /* 内存池所有数据清零 */  
 mallco_dev.memrdy[memx]=1;                                  /* 内存管理初始化OK */  
}  
/************************************************************************
函数名称:u8 my_mem_perused(u8 memx)     
函数功能:获取内存使用率
入口参数:u8 memx:所属内存块
返回参数:u8 使用率(0~100)
修改作者:Aaron
*************************************************************************/
u8 my_mem_perused(u8 memx)  
{
    
      
  u32 used=0;                              /* 初始化使用率为0 */
  u32 i;  
  for(i=0;i<memtblsize[memx];i++)          /* 所以内存表里的所有项 */
  {
    
      
  if(mallco_dev.memmap[memx][i])           /* 判断某个内存表里的第i个项是否为非零(及被占用)*/
   used++;                              
  } 
  return (used*100)/(memtblsize[memx]);  
} 
/************************************************************************
函数名称:u32 my_mem_malloc(u8 memx,u32 size)       
函数功能:内存分配(内部调用)
入口参数:u8 memx:所属内存块,u32 size:要分配的内存大小(字节)
返回参数:0XFFFFFFFF,代表错误;其他,内存偏移地址
修改作者:Aaron
*************************************************************************/
u32 my_mem_malloc(u8 memx,u32 size)  
{
    
      
 signed long offset=0;  
 u32 nmemb;                                          /* 需要的内存块数 */  
 u32 cmemb=0;                                        /* 连续空的内存块数 */
 u32 i;  
 if(!mallco_dev.memrdy[memx])
  mallco_dev.init(memx);                             /* 若内存管理未就绪,则先执行初始化函数 */ 
 if(size==0)
  return 0XFFFFFFFF;                                 /* 若size=0,则不需要分配,返回0XFFFFFFFF */
 nmemb=size/memblksize[memx];                        /* 获取需要分配的连续内存块数 */
 if(size%memblksize[memx])
  nmemb++;                                           /* 如果有余数,则实际需要的内存块数再+1 */
 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;                                /* 未找到符合分配条件的内存块 */ 
} 
/************************************************************************
函数名称:u8 my_mem_free(u8 memx,u32 offset)      
函数功能:释放内存(内部调用) 
入口参数:u8 memx:所属内存块,u32 offset:内存地址偏移
返回参数:0,释放成功;1,释放失败; 
修改作者:Aaron
*************************************************************************/
u8 my_mem_free(u8 memx,u32 offset)  
{
    
      
 int i;  
 if(!mallco_dev.memrdy[memx])                      /* 未初始化,先执行初始化 */
 {
    
    
  mallco_dev.init(memx);    
  return 1;                                        /* 未初始化 */  
 }  
 if(offset<memsize[memx])                          /* 偏移在内存池内 */ 
 {
    
      
  int index=offset/memblksize[memx];               /* 偏移所在内存块号码 */  
  int nmemb=mallco_dev.memmap[memx][index];        /* 内存块数量 */
  for(i=0;i<nmemb;i++)                             /* 内存块清零 */
  {
    
      
   mallco_dev.memmap[memx][index+i]=0;  
  }  
  return 0;  
 }
 else 
  return 2;                                       /* 偏移超区 */  
}  
/************************************************************************
函数名称:myfree(u8 memx,void *ptr)      
函数功能:释放内存(外部调用) 
入口参数:u8 memx:所属内存块,ptr:内存首地址 
返回参数:无
修改作者:Aaron
*************************************************************************/
void myfree(u8 memx,void *ptr)  
{
    
      
 u32 offset;   
 if(ptr==NULL)
  return;                                          /* 地址为0 */  
  offset=(u32)ptr-(u32)mallco_dev.membase[memx];     
 my_mem_free(memx,offset);                         /* 释放内存 */      
}  
/************************************************************************
函数名称:*mymalloc(u8 memx,u32 size)      
函数功能:分配内存(外部调用)
入口参数:u8 memx:所属内存块,u32 size:内存大小(字节)
返回参数:分配到的内存首地址
修改作者:Aaron
*************************************************************************/
void* mymalloc(u8 memx,u32 size)  
{
    
      
 u32 offset;   
 offset=my_mem_malloc(memx,size);            
 if(offset==0XFFFFFFFF)
  return NULL;  
 else 
  return (void*)((u32)mallco_dev.membase[memx]+offset);  
}  
/***************************************************************************
函数名称:u32 my_mem_malloc(u8 memx,u32 size)       
函数功能:重新分配内存(外部调用),*ptr:旧内存首地址,size:要分配的内存大小(字节)
入口参数:u8 memx:所属内存块
返回参数:新分配到的内存首地址
修改作者:Aaron
****************************************************************************/
void *myrealloc(u8 memx,void *ptr,u32 size)  
{
    
      
    u32 offset;    
    offset=my_mem_malloc(memx,size);    
    if(offset==0XFFFFFFFF)
   return NULL;     
    else  
    {
    
                  
     mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size);  /* 拷贝旧内存内容到新内存 */   
     myfree(memx,ptr);                                                  /* 释放旧内存 */
     return (void*)((u32)mallco_dev.membase[memx]+offset);              /* 返回新内存首地址 */
    }  
}
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

3.3 main.c主函数解读

/**
 ********************************  STM32F10x  *********************************
 * @文件名称: main.c
 * @修改作者: Aaron
 * @库版本号: V3.5.0
 * @工程版本: V1.0.0
 * @开发日期: 2020年10月28日
 * @摘要简述: Main源文件
 ******************************************************************************/
/*-----------------------------------------------------------------------------
 * @更新日志:
 * @无
 * ---------------------------------------------------------------------------*/
 /* 包含的头文件 ---------------------------------------------------------------*/
#include "led.h"
#include "key.h"
#include "delay.h"
#include "sys.h"
#include "serial_communication.h"
#include "nvic_configuration.h"
#include "sram.h"
#include "malloc.h"
#include "string.h"
/**********************************************************************
函数名称:main()
函数功能:主函数
入口参数:无
返回参数:无
开发作者:Aaron
***********************************************************************/
 int main(void)
 {
    
      
  u8 key;   
  u8 i=0;     
  u8 *p=0;
  u8 sramx=0;           /* 默认,0为内部sram,1为外部sram */
  delay_init();          
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  My_USART1_Init();
  LED_Init();           /* 初始化与LED连接的硬件接口 */
  KEY_Init();           /* 初始化按键 */
  FSMC_SRAM_Init();     /* 初始化外部SRAM */  
  my_mem_init(SRAMIN);  /* 初始化内部内存池 */
  my_mem_init(SRAMEX);  /* 初始化外部内存池 */
  while(1)
  {
    
     
   key=KEY_Scan(0);     /* 0,不支持连按 */
   switch(key)
   {
    
    
    case 0:                                                    /* 没有按键按下 */
     break;
    case KEY0_PRES:                                            /* KEY0按下 */
     p=mymalloc(sramx,2048);                                   /* 申请2K字节,返回分配到的内存首地址*/
     if(p!=NULL)
     {
    
    
      sprintf((char*)p,"Memory Malloc Test%d",i);              /* 向p写入一些内容 */
      printf("分配的内存首地址为:0X%08X\r\n",p);
      printf("输入的内容是:%s\r\n",p);
      printf("内部内存使用率:%d,外部内存使用率:%d\r\n",my_mem_perused(SRAMIN),my_mem_perused(SRAMEX));    /* 显示内部内存使用率和外部内存使用率 */
     }
     break;
    case KEY1_PRES:                                            /* KEY1按下 */    
     if(p!=NULL)
     {
    
    
      sprintf((char*)p,"Memory Malloc Test%d",i);              /* 更新显示内容,内存不增加 */ 
      printf("分配的内存首地址为:0X%08X\r\n",p);
      printf("更新的内容是:%s\r\n",p); 
      printf("内部内存使用率:%d,外部内存使用率:%d\r\n",my_mem_perused(SRAMIN),my_mem_perused(SRAMEX));    /* 显示内部内存使用率和外部内存使用率 */     
     }
     break;
    case KEY2_PRES:                       /* KEY2按下 */   
     myfree(sramx,p);                     /* 释放内存 */
     p=0;                                 /* 指向空地址 */
     printf("内部内存使用率:%d,外部内存使用率:%d\r\n",my_mem_perused(SRAMIN),my_mem_perused(SRAMEX));     /* 显示内部内存使用率和外部内存使用率 */  
     break;
    case WKUP_PRES:                       /* KEY UP按下 */ 
     sramx=!sramx;                        /* 切换当前malloc/free操作对象 */
     if(sramx)
      printf("切换到外部内存池\r\n");      /* 切换到外部内存池 */
     else 
      printf("切换到内部内存池\r\n");      /* 切换到内部内存池 */  
     break;
  }
  delay_ms(50);   
  i++;
  if((i%20)==0)
  {
    
     
    DS0=!DS0;
   }
 }    
}
/****** Copyright (C)2020 Aaron. All Rights Reserved ****** END OF FILE *******/

实验结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/109374130