您当前的位置: 首页 >  stm32

正点原子

暂无认证

  • 5浏览

    0关注

    382博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【正点原子STM32连载】 第四十四章 内存管理实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

正点原子 发布时间:2022-10-09 17:46:54 ,浏览量:5

1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-336836-1-1.html 4)对正点原子STM32感兴趣的同学可以加群讨论:879133275

第四十四章 内存管理实验

如果我们所用的内存都是直接定义一个数组来使用,灵活性会比较差,很多时候不能满足实际使用需求。为了解决这些问题,我们来学习内存管理,实现对内存的动态管理。 本章分为如下几个小节: 44.1 内存管理简介 44.2 硬件设计 44.3 程序设计 44.4 下载验证

44.1 内存管理简介

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种,其实最终都是要实现两个函数:malloc和free。malloc函数用来内存申请,free函数用于内存释放。 本章,我们介绍一种比较简单的办法来实现:分块式内存管理。下面我们介绍一下该方法的实现原理,如图44.1.1所示: 在这里插入图片描述

图44.1.1 分块式内存管理原理 从上图可以看出,分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为了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个内存管理表项目的值都清零,标记释放,完成一次内存释放。 接下来,我们简单介绍一下STM32H7的内存分配情况,如表44.1.1所示: 在这里插入图片描述

表44.1.1 STM32H7内存分配表 从上表可以看出,STM32H7的内存分成:ITCM、DTCM、AXI SRAM、SRAM1~4等几个部分,其中地址连续的区域分成5大块:ITCM(64KB)、DTCM(128KB)、AXI SRAM(512KB)、SRAM1~3(288KB)和SRAM4(64KB), 因为内存管理的内存池,必须是地址连续的内存空间,因此,STM32H7内部内存需要5个内存池来管理。 另外,需要注意: 1、ITCM和DTCM这两个内存块,仅CPU和MDMA可以直接访问,其他外设不可以直 接访问! 2、以太网的DMA描述符等必须是定义在SRAM3里面才可以正常工作,因此我们一般把 SRAM3(32KB)独立给以太网使用,并不用作内存管理! 44.2 硬件设计

  1. 例程功能 开机后,显示提示信息,等待外部输入。KEY0用于申请内存,每次申请2K字节内存,KEY1用于释放内存,KEY_UP用于切换操作内存区(SRAMIN/SRAM12/SRAM4 /SRAMDTCM/SRAMITCM,总共管理5个内存块)。还可以通过USMART调试,测试内存管理函数。 LED0闪烁用于提示程序正在运行。
  2. 硬件资源 1)RGB灯 LED0 – PB4 2)独立按键 KEY0 - PA1 KEY1 - PA15 WK_UP - PA0 3)串口1 (PA9/PA10连接在板载USB转串口芯片CH340上面) 4)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动) 44.3 程序设计 44.3.1 程序流程图 在这里插入图片描述

图44.3.1.1 内存管理实验程序流程图 44.3.2 程序解析

  1. MALLOC代码 这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。MALLOC驱动源码包括两个文件:malloc.c和malloc.h。 下面我们介绍malloc.h中比较重要的一些结构体和内存参数宏定义,其定义如下:
/* 定义六个内存池 */
#define SRAMIN      0       /* AXI内存池,AXI共512KB */
#define SRAM12      1       /* SRAM1/2内存池,SRAM1+SRAM2,共256KB */
#define SRAM4       2       /* SRAM4内存池,SRAM4共64KB */
/* DTCM内存池,DTCM共128KB,此部分内存仅CPU和MDMA(通过AHBS)可以访问!!!! */
#define SRAMDTCM    3      
/* ITCM内存池,DTCM共64 KB,此部分内存仅CPU和MDMA(通过AHBS)可以访问!!!! */
#define SRAMITCM    4      

#define SRAMBANK    5      /* 定义支持的SRAM块数. */

/* 定义内存管理表类型,当外扩SDRAM的时候,必须使用uint32_t类型,
否则可以定义成uint16_t,以节省内存占用 */
#define MT_TYPE     uint16_t

/* 单块内存,内存管理所占用的全部空间大小计算公式如下:
 * size=MEM1_MAX_SIZE+(MEM1_MAX_SIZE/MEM1_BLOCK_SIZE)*sizeof(MT_TYPE)
 * 以SRAMIN为例,size=474*1024+(474*1024/64)*2=500544≈489KB

 * 已知总内存容量(size),最大内存池的计算公式如下:
 * MEM1_MAX_SIZE=(MEM1_BLOCK_SIZE*size)/(MEM1_BLOCK_SIZE+sizeof(MT_TYPE))
 * 以SRAM12为例,MEM1_MAX_SIZE=(64*256)/(64+2)=248.24KB≈248KB
 */
 
/* mem1内存参数设定.mem1是H7内部的AXI内存. */
#define MEM1_BLOCK_SIZE  64          /* 内存块大小为64字节 */
#define MEM1_MAX_SIZE    474*1024   /* 最大管理内存 474K,H7的AXI内存总共512KB */
#define MEM1_ALLOC_TABLE_SIZE   MEM1_MAX_SIZE/MEM1_BLOCK_SIZE   /* 内存表大小 */

/* mem2内存参数设定.mem3是H7内部的SRAM1+SRAM2内存 */
#define MEM2_BLOCK_SIZE  64          /* 内存块大小为64字节 */
#define MEM2_MAX_SIZE    248 *1024  /* 最大管理内存248K,H7的SRAM1+SRAM2共256KB */
#define MEM2_ALLOC_TABLE_SIZE   MEM2_MAX_SIZE/MEM2_BLOCK_SIZE   /* 内存表大小 */

/* mem3内存参数设定.mem4是H7内部的SRAM4内存 */
#define MEM3_BLOCK_SIZE   64         /* 内存块大小为64字节 */
#define MEM3_MAX_SIZE     62 *1024  /* 最大管理内存62K,H7的SRAM4共64KB */
#define MEM3_ALLOC_TABLE_SIZE   MEM3_MAX_SIZE/MEM3_BLOCK_SIZE   /* 内存表大小 */

/* mem4内存参数设定.mem5是H7内部的DTCM内存,此部分内存仅CPU和MDMA可以访问!!!!!! */
#define MEM4_BLOCK_SIZE   64         /* 内存块大小为64字节 */
#define MEM4_MAX_SIZE     124 *1024 /* 最大管理内存124K,H7的DTCM共128KB */
#define MEM4_ALLOC_TABLE_SIZE   MEM4_MAX_SIZE/MEM4_BLOCK_SIZE   /* 内存表大小 */

/* mem5内存参数设定.mem6是H7内部的ITCM内存,此部分内存仅CPU和MDMA可以访问!!!!!! */
#define MEM5_BLOCK_SIZE  64         /* 内存块大小为64字节 */
#define MEM5_MAX_SIZE    62 *1024  /* 最大管理内存62K,H7的ITCM共64KB */
#define MEM5_ALLOC_TABLE_SIZE   MEM5_MAX_SIZE/MEM5_BLOCK_SIZE   /* 内存表大小 */

/* 如果没有定义NULL, 定义NULL */
#ifndef NULL
#define NULL 0
#endif

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

这部分代码,定义了很多关键数据,比如内存块大小的定义:

MEM1_BLOCK_SIZE、MEM2_BLOCK_SIZE、MEM3_BLOCK_SIZE、MEM4_BLOCK_SIZE和MEM5_BLOCK_SIZE,都是64字节。
MEM1_ALLOC_TABLE_SIZE ~ MEM5_ALLOC_TABLE_SIZE,

则分别代表内存池1 ~ 5的内存管理表大小。 从这里可以看出,如果内存分块越小,那么内存管理表就越大,当分块为4字节1个块的时候,内存管理表就和内存池一样大了(管理表的每项都是uint32_t类型)。显然是不合适的,我们这里取64字节,比例为1:16,内存管理表相对就比较小了。 通过这个内存管理控制器_m_malloc_dev结构体,我们把分块式内存管理的相关信息,其初始化函数、获取使用率、内存池、内存管理表以及内存管理的状态保存下来,实现对内存池的管理控制。 下面介绍malloc.c文件,其中,内存池、内存管理表、内存管理参数和内存管理控制器的定义如下:

/* 内存池(64字节对齐) */
static __align(64) uint8_t mem1base[MEM1_MAX_SIZE];  /* 内部SRAM内存池 */
static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] 
__attribute__((at(0x30000000))); /* 内部SRAM1+SRAM2内存池 */
static __align(64) uint8_t mem3base[MEM3_MAX_SIZE] 
__attribute__((at(0x38000000))); /* 内部SRAM4内存池 */
static __align(64) uint8_t mem4base[MEM4_MAX_SIZE] 
__attribute__((at(0x20000000))); /* 内部DTCM内存池 */
static __align(64) uint8_t mem5base[MEM5_MAX_SIZE] 
__attribute__((at(0x00000000))); /* 内部ITCM内存池 */

/* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE];                                                  /* 内部SRAM内存池MAP */
static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0x30000000
+ MEM2_MAX_SIZE)));  /* 内部SRAM1+SRAM2内存池MAP */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0x38000000
+ MEM3_MAX_SIZE)));  /* 内部SRAM4内存池MAP */
static MT_TYPE mem4mapbase[MEM4_ALLOC_TABLE_SIZE] __attribute__((at(0x20000000
+ MEM4_MAX_SIZE)));  /* 内部DTCM内存池MAP */
static MT_TYPE mem5mapbase[MEM5_ALLOC_TABLE_SIZE] __attribute__((at(0x00000000
+ MEM5_MAX_SIZE)));  /* 内部ITCM内存池MAP */

/* 内存管理参数 */
const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE, 
MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE,  
                          MEM4_ALLOC_TABLE_SIZE, MEM5_ALLOC_TABLE_SIZE
                        };       /* 内存表大小 */

const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE, 
MEM3_BLOCK_SIZE, MEM4_BLOCK_SIZE, MEM5_BLOCK_SIZE
                        };        /* 内存分块大小 */

const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE, MEM2_MAX_SIZE, 
MEM3_MAX_SIZE, MEM4_MAX_SIZE, MEM5_MAX_SIZE
                        };           /* 内存总大小 */

/* 内存管理控制器 */
struct _m_mallco_dev mallco_dev =
{
    my_mem_init,                                                   	/* 内存初始化 */
    my_mem_perused,                                               	/* 内存使用率 */
    mem1base, mem2base, mem3base, mem4base, mem5base,      	/* 内存池 */
mem1mapbase, mem2mapbase, mem3mapbase, mem4mapbase,
mem5mapbase,     /* 内存管理状态表 */
    0, 0, 0, 0, 0,   /* 内存管理未就绪 */
};
我们通过内存管理控制器mallco_dev结构体,实现对六个内存池的管理控制。

第一个是内部SRAM内存池,定义为: static __align(64) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部SRAM内存池 / 第二个是SRAM1+SRAM2内存池,定义为: static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] attribute((at(0x30000000))); / 内部SRAM1+SRAM2内存池 / 第三个是SRAM4内存池,定义为: static __align(64) uint8_t mem3base[MEM3_MAX_SIZE] attribute((at(0x38000000))); / 内部SRAM4内存池 / 第四个是DTCM内存池,定义为: static __align(64) uint8_t mem4base[MEM4_MAX_SIZE] attribute((at(0x20000000))); / 内部DTCM内存池 / 第五个是ITCM内存池,定义为: static __align(64) uint8_t mem5base[MEM5_MAX_SIZE] attribute((at(0x00000000))); / 内部ITCM内存池 */ 因为STM32H7内部有5个连续的内存块,因此总共需要5个内存池,AXI SRAM内存池的首地址由编译器指定,其他几个内部内存池的首地址就是相应SRAM的首地址。 其中,__align(64)定义内存池为64字节对齐,以适应各种不同场合的需求。 这样总共有5部分内存,分成5个内存池,每个内存池需要一个内存管理表,因此又有5个内存管理表:mem1mapbase~ mem5mapbase,内存管理表所占内存,也指定在对应的内存块里面分配。因此:内存池+内存管理表,基本上就占了整个内存块的全部空间了。 下面介绍其他的malloc代码,具体如下:

/**
 * @brief       复制内存
 * @param       *des : 目的地址
 * @param       *src : 源地址
 * @param       n     : 需要复制的内存长度(字节为单位)
 * @retval      无
 */
void my_mem_copy(void *des, void *src, uint32_t n)
{
    uint8_t *xdes = des;
    uint8_t *xsrc = src;
    while (n--)*xdes++ = *xsrc++;
}

/**
 * @brief       设置内存值
 * @param       *s     : 内存首地址
 * @param       c      : 要设置的值
 * @param       count : 需要设置的内存大小(字节为单位)
 * @retval      无
 */
void my_mem_set(void *s, uint8_t c, uint32_t count)
{
    uint8_t *xs = s;
    while (count--)*xs++ = c;
}

/**
 * @brief       内存管理初始化
 * @param       memx : 所属内存块
 * @retval      无
 */
void my_mem_init(uint8_t memx)
{
/* 获取memmap数组的类型长度(uint16_t /uint32_t)*/
uint8_t mttsize = sizeof(MT_TYPE);
/* 内存状态表数据清零 */
my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx]*mttsize); 
/* 内存管理初始化OK */
    mallco_dev.memrdy[memx] = 1;        
}

/**
 * @brief       获取内存使用率
 * @param       memx : 所属内存块
 * @retval      使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
 */
uint16_t my_mem_perused(uint8_t memx)
{
    uint32_t used = 0;
    uint32_t i;

    for (i = 0; i = 0; offset--) 
    {
        if (!mallco_dev.memmap[memx][offset])
        {
            cmemb++;             /* 连续空内存块数增加 */
        }
        else 
        {
            cmemb = 0;          /* 连续内存块清零 */
        }
        
        if (cmemb == nmemb)   /* 找到了连续nmemb个空内存块 */
        {
            for (i = 0; i             
关注
打赏
1665308814
查看更多评论
0.0397s