首页 > 系统相关 >《MiniPRO H750开发指南》第四十四章 内存管理实验

《MiniPRO H750开发指南》第四十四章 内存管理实验

时间:2022-10-10 10:03:18浏览次数:53  
标签:__ MiniPRO 16 第四十四章 uint8 内存 H750 memx SIZE

第四十四章内存管理实验​


如果我们所用的内存都是直接定义一个数组来使用,灵活性会比较差,很多时候不能满足实际使用需求。为了解决这些问题,我们来学习内存管理,实现对内存的动态管理。​

本章分为如下几个小节:​

44.1 内存管理简介​

44.2 硬件设计​

44.3 程序设计​

44.4 下载验证​



44.1 内存管理简介

内存管理,是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种,其实最终都是要实现两个函数:malloc和free。malloc函数用来内存申请,free函数用于内存释放。​

本章,我们介绍一种比较简单的办法来实现:分块式内存管理。下面我们介绍一下该方法的实现原理,如图44.1.1所示:​

《MiniPRO H750开发指南》第四十四章 内存管理实验_#define


图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所示:​

《MiniPRO H750开发指南》第四十四章 内存管理实验_#define_02


表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灯​

– 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 程序流程图

《MiniPRO H750开发指南》第四十四章 内存管理实验_内存管理_03


图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代码,具体如下:​

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

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

/**​
内存管理初始化​
所属内存块​
无​
*/​
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; ​
}​

/**​
获取内存使用率​
所属内存块​
使用率(扩大了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 < memtblsize[memx]; i++)​
{​
if (mallco_dev.memmap[memx][i])used++;​
}​

return (used * 1000) / (memtblsize[memx]);​
}​

/**​
内存分配(内部调用)​
所属内存块​
要分配的内存大小(字节)​
内存偏移地址​
有效的内存偏移地址​
无效的内存偏移地址​
*/​
static 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; /* 未找到符合分配条件的内存块 */​
}​

/**​
释放内存(内部调用)​
所属内存块​
内存地址偏移​
释放结果​
释放成功;​
释放失败;​
超区域了(失败);​
*/​
static uint8_t my_mem_free(uint8_t memx, uint32_t 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; /* 偏移超区了. */​
}​
}​

/**​
释放内存(外部调用)​
所属内存块​
内存首地址​
无​
*/​
void myfree(uint8_t memx, void *ptr)​
{​
uint32_t offset;​

if (ptr == NULL)return; /* 地址为0. */​

offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];​
my_mem_free(memx, offset); /* 释放内存 */​
}​

/**​
分配内存(外部调用)​
所属内存块​
要分配的内存大小(字节)​
分配到的内存首地址.​
*/​
void *mymalloc(uint8_t memx, uint32_t size)​
{​
uint32_t offset;​
offset = my_mem_malloc(memx, size);​

if (offset == 0XFFFFFFFF) /* 申请出错 */​
{​
return NULL; /* 返回空(0) */​
}​
else /* 申请没问题, 返回首地址 */​
{​
return (void *)((uint32_t)mallco_dev.membase[memx] + offset);​
}​
}​

/**​
重新分配内存(外部调用)​
所属内存块​
旧内存首地址​
要分配的内存大小(字节)​
新分配到的内存首地址.​
*/​
void *myrealloc(uint8_t memx, void *ptr, uint32_t size)​
{​
uint32_t offset;​
offset = my_mem_malloc(memx, size);​

if (offset == 0XFFFFFFFF) /* 申请出错 */​
{​
return NULL; /* 返回空(0) */​
}​
else /* 申请没问题, 返回首地址 */​
{​
my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset), ​
ptr, size); /* 拷贝旧内存内容到新内存 */​
myfree(memx, ptr); /* 释放旧内存 */​
return (void *)((uint32_t)mallco_dev.membase[memx] ​
+ offset); /* 返回新内存首地址 */​
}​
}

整个malloc代码的核心函数:my_mem_malloc和my_mem_free,分别用于内存申请和内存释放。思路就是前面44.1所介绍的分配内存和释放内存,不过在这里,这两个函数知识内部调用,外部调用我们另外定义了mymalloc和myfree两个函数,其他函数我们就不多介绍了。​

2. main.c代码

main.c代码如下:​

const char *SRAM_NAME_BUF[SRAMBANK] = {"SRAMIN", "SRAM12", "SRAM4 ", ​
"SRAMDTCM", "SRAMITCM"};​

int main(void)​
{​
uint8_t paddr[20]; /* 存放P Addr:+p地址的ASCII值 */​
uint16_t memused = 0;​
uint8_t key;​
uint8_t i = 0;​
uint8_t *p = 0;​
uint8_t *tp = 0;​
uint8_t sramx = 0; /* 默认为内部sram */​

sys_cache_enable(); /* 打开L1-Cache */​
HAL_Init(); /* 初始化HAL库 */​
sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */​
delay_init(480); /* 延时初始化 */​
usart_init(115200); /* 串口初始化为115200 */​
usmart_dev.init(240); /* 初始化USMART */​
mpu_memory_protection(); /* 保护相关存储区域 */​
led_init(); /* 初始化LED */​
lcd_init(); /* 初始化LCD */​
key_init(); /* 初始化按键 */​
my_mem_init(SRAMIN); /* 初始化内部内存池(AXI) */​
my_mem_init(SRAM12); /* 初始化SRAM12内存池(SRAM1+SRAM2) */​
my_mem_init(SRAM4); /* 初始化SRAM4内存池(SRAM4) */​
my_mem_init(SRAMDTCM); /* 初始化DTCM内存池(DTCM) */​
my_mem_init(SRAMITCM); /* 初始化ITCM内存池(ITCM) */​

lcd_show_string(30, 40, 200, 16, 16, "STM32", RED);​
lcd_show_string(30, 60, 200, 16, 16, "MALLOC TEST", RED);​
lcd_show_string(30, 80, 200, 16, 16, "ATOM@ALIENTEK", RED);​
lcd_show_string(30, 100, 200, 16, 16, "KEY0:Malloc & WR & Show", RED);​
lcd_show_string(30, 120, 200, 16, 16, "KEY_UP:SRAMx KEY1:Free", RED);​

lcd_show_string(30, 140, 200, 16, 16, "SRAMIN ", BLUE);​
lcd_show_string(30, 156, 200, 16, 16, "SRAMIN USED:", BLUE);​
lcd_show_string(30, 172, 200, 16, 16, "SRAM12 USED:", BLUE);​
lcd_show_string(30, 188, 200, 16, 16, "SRAM4 USED:", BLUE);​
lcd_show_string(30, 204, 200, 16, 16, "SRAMDTCM USED:", BLUE);​
lcd_show_string(30, 220, 200, 16, 16, "SRAMITCM USED:", BLUE);​

while (1)​
{​
key = key_scan(0); /* 不支持连按 */​

switch (key)​
{​
case KEY0_PRES: /* KEY0按下 */​
p = mymalloc(sramx, 2048);/* 申请2K字节,并写入内容,显示在lcd屏幕上面*/​

if (p != NULL)​
{​
/* 向p写入一些内容 */​
sprintf((char *)p, "Memory Malloc Test%03d", i);​
/* 显示P的内容 */​
lcd_show_string(30, 280, 209, 16, 16, (char *)p, BLUE); ​
}​
break;​

case KEY1_PRES: /* KEY1按下 */​
myfree(sramx, p); /* 释放内存 */​
p = 0; /* 指向空地址 */​
break;​

case WKUP_PRES: /* KEY UP按下 */​
sramx++;​
if (sramx > 4)sramx = 0;​
lcd_show_string(30, 140, 200, 16, 16, ​
(char *)SRAM_NAME_BUF[sramx], BLUE);​
break;​
}​

if (tp != p && p != NULL)​
{​
tp = p;​
sprintf((char *)paddr, "P Addr:0X%08X", (uint32_t)tp);​
/* 显示p的地址 */​
lcd_show_string(30, 260, 209, 16, 16, (char *)paddr, BLUE);​

if (p)​
{​
/* 显示P的内容 */​
lcd_show_string(30, 280, 280, 16, 16, (char *)p, BLUE); ​
}​
else ​
{​
lcd_fill(30, 280, 209, 296, WHITE); /* p=0,清除显示 */​
}​
}​

delay_ms(10);​
i++;​

if ((i % 20) == 0) /* DS0闪烁. */​
{​
memused = my_mem_perused(SRAMIN);​
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);​
/* 显示内部内存使用率 */​
lcd_show_string(30 + 112, 156, 200, 16, 16, (char *)paddr, BLUE); ​

memused = my_mem_perused(SRAM12);​
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);​
/* 显示TCM内存使用率 */​
lcd_show_string(30 + 112, 172, 200, 16, 16, (char *)paddr, BLUE); ​

memused = my_mem_perused(SRAM4);​
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);​
/* 显示内部内存使用率 */​
lcd_show_string(30 + 112, 188, 200, 16, 16, (char *)paddr, BLUE);​

memused = my_mem_perused(SRAMDTCM);​
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);​
/* 显示外部内存使用率 */​
lcd_show_string(30 + 112, 204, 200, 16, 16, (char *)paddr, BLUE);​

memused = my_mem_perused(SRAMITCM);​
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);​
/* 显示TCM内存使用率 */​
lcd_show_string(30 + 112, 220, 200, 16, 16, (char *)paddr, BLUE);​

LED0_TOGGLE(); /* LED0闪烁 */​
}​
}​
}

该部分代码比较简单,主要是对mymalloc和myfree的应用。不过这里提醒大家,如果对一个指针进行多次内存申请,而之前的申请又没释放,那么将造成“内存泄露”,这是内存管理所不希望发生的,久而久之,可能导致无内存可用的情况!所以,在使用的时候,请大家一定记得,申请的内存在用完以后,一定要释放。​

另外,本章希望利用USMART调试内存管理,所以在USMART里面添加了mymalloc和myfree两个函数,用于测试内存分配和内存释放。大家可以通过USMART自行测试。​

44.4 下载验证

将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。​

刚开始,所有内存的使用率均为0%,说明还没有任何内存被使用,此时我们按下KEY0,就可以看到内部SRAM内存被使用0.4%了,如下图所示:​

《MiniPRO H750开发指南》第四十四章 内存管理实验_#define_04


图44.4.1 内存管理实验测试图​

同时看到下面提示了指针p所指向的地址(其实就是被分配到的内存地址)和内容。多按几次KEY0,可以看到内存使用率持续上升(注意对比p的值,可以发现是递减的,说明是从顶部开始分配内存!),此时如果按下KEY1,可以发现内存使用率降低了0.4%,但是再按KEY1将不再降低,说明“内存泄露”了。这就是前面提到的对一个指针多次申请内存,而之前申请的内存又没释放,导致的“内存泄露”。​

按KEY_UP按键,可以切换当前操作内存(内部SRAM、内部SRAM12、内部SRAM4、内部DTCM和内部ITCM),KEY1键用于更新指针p的内容,更新后的内容将重新显示在LCD模块上面。

本章,我们还可以借助USMART,测试内存的分配和释放,有兴趣的朋友可以动手试试。如图44.4.2所示:​

《MiniPRO H750开发指南》第四十四章 内存管理实验_#define_05


图44.4.2 USMART测试内存管理图​

图中,我们先申请了4660字节的内存,然后得到申请到的内存首地址为:0x24075940,说明我们申请内存成功(如果不成功,则会收到0),然后释放内存的时候,参数是指针的地址,即执行:myfree(0x24075940),就可以释放我们申请到的内存。其他情况,大家可以自行测试并分析。​


标签:__,MiniPRO,16,第四十四章,uint8,内存,H750,memx,SIZE
From: https://blog.51cto.com/u_15046463/5742904

相关文章