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,16,p);             //显示P的内容
                }
                break;
            case KEY2_PRES:    //KEY2按下      
                myfree(SRAMEX,p);//释放内存
                p=0;            //指向空地址
                break;
        }
}

关注微信公众号:[果果小师弟],获取更多精彩内容!
智果芯—服务于百万大学生和电子工程师

相关文章
|
存储 算法 小程序
|
存储 程序员 编译器
STM32-内存五区
一个由C/C++编译的程序占用的内存分为以下几个部分 * 栈区(stack)— **由编译器自动分配释放,存放函数的参数值,局部变量的值等**。 * 堆区(heap) — **由程序员分配和释放,若程序员不释放,程序结束时可能由OS回收**。 * 全局区(静态区)(static)—**全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量、未初始化的静态变量在相邻的另一块区域**。 * 文字常量区 — **常量字符串就是放在这里的**。 * 程序代码区 — **存放函数体的二进制代码**。
165 0
STM32-内存五区
|
存储 程序员 编译器
STM32的内存管理相关(内存架构,内存管理,map文件分析)
STM32 的内存架构,内存管理以及 map 文件分析
349 0
STM32的内存管理相关(内存架构,内存管理,map文件分析)
|
存储 算法 芯片
STM32寄存器版的基础知识—内存映射
STM32F429 采用的是 Cortex-M4 内核,内核即 CPU,由 ARM公司设计。ARM 公司并不生产芯片,而是出售其芯片技术授权。芯片生产厂商(SOC)如 ST、TI、Freescale,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。如 GPIO、USART(串口)、I2C、SPI等都叫做片上外设。
391 0
STM32寄存器版的基础知识—内存映射
STM32/GD32上内存堆栈溢出探测研究
无数次遭受堆栈溢出折磨,随着系统变得复杂,故障点越来越难以查找!主要溢出情况如下:1,一般RAM最后两块空间是堆Heap和栈Stack,堆从下往上用,栈从上往下用,任意一个用完,都会进入对方的空间2,如果栈用完,进入堆的空间,这个时候系统是不会有任何异常的,也就是说,栈底没有什么意义。
1069 0
|
5月前
使用STM32F103标准库实现定时器控制LED点亮和关闭
通过这篇博客,我们学习了如何使用STM32F103标准库,通过定时器来控制LED的点亮和关闭。我们配置了定时器中断,并在中断处理函数中实现了LED状态的切换。这是一个基础且实用的例子,适合初学者了解STM32定时器和中断的使用。 希望这篇博客对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。
394 2
|
4月前
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
stm32f407探索者开发板(十七)——串口寄存器库函数配置方法
646 0
|
6月前
|
传感器
STM32标准库ADC和DMA知识点总结-1
STM32标准库ADC和DMA知识点总结
|
5月前
|
IDE 开发工具
使用STM32F103标准库实现自定义键盘
通过本文,我们学习了如何使用STM32F103标准库实现一个简单的自定义键盘。我们首先初始化了GPIO引脚,然后实现了一个扫描函数来检测按键状态。这个项目不仅能够帮助我们理解STM32的GPIO配置和按键扫描原理,还可以作为进一步学习中断处理和低功耗设计的基础。希望本文对你有所帮助,祝你在嵌入式开发的道路上不断进步!
483 4
|
5月前
|
传感器
【经典案例】STM32F407使用HAL库配置I2C详解
STM32F407是一个强大的微控制器,广泛应用于嵌入式系统中。在许多应用中,我们需要使用I2C总线来与传感器、EEPROM、显示屏等外设进行通信。本文将详细介绍如何使用STM32 HAL库来配置和使用I2C接口。
627 2