1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html##
第四十八章 内存管理实验
本章将介绍正点原子提供的内存管理库的使用,通过使用内存管理库可以提高内存的使用效率,在大型的应用开发中,是必不可少的工具。通过本章的学习,读者将学习到正点原子内存管理库的使用。
本章分为如下几个小节:
48.1 硬件设计
48.2 程序设计
48.3 下载验证
48.1 硬件设计
48.1.1 例程功能
- 程序运行后,可通过按下KEY0和KEY_UP按键,分别进行内存的申请和释放操作,操作结果将在LCD上显示
- 可通过USMART进行内存申请和释放的操作
- LED0闪烁,指示程序正在运行
48.1.2 硬件资源 - LED
LED0 - PF9 - 按键
KEY0 - PE4
KEY_UP - PA0 - USART1(PA9、PA10连接至板载USB转串口芯片上)
- 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
- 外部SRAM
48.1.3 原理图
本章实验使用的内存管理库为软件库,因此没有对应的连接原理图。
48.2 程序设计
48.2.1 内存管理库的使用
正点原子提供的内存管理库包含两个文件,分别为:malloc.c和malloc.h,本章实验配套的实验例程中已经提供了这两个文件,并且已经针对正点原子APM32F407最小系统板进行了移植适配,用户在使用时,仅需将这两个文件添加到自己的工程即可,如下图所示:
图48.2.1.1 正点原子内存管理库文件
内存管理库中提供了内存管理初始化、申请内存和释放内存等函数,使用非常方便。
在进行内存申请和释放之前,需要使用内存初始化函数对内存进行初始化,内存管理初始化的使用示例,如下所示:
#include "apm32f4xx.h"
#include "./MALLOC/malloc.h"
void example_fun(void)
{
/* 初始化内部SRAM内存池 */
my_mem_init(SRAMIN);
}
初始化内存后,便可以在需要内存的时候申请内存,申请内存函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "./MALLOC/malloc.h"
void example_fun(void)
{
uint8_t ptr;
/* 初始化内部SRAM内存池 */
my_mem_init(SRAMIN);
/* 申请1KB内存 */
ptr = (uint8_t *)mymalloc(SRAMIN, 1024);
/* Do something. */
}
在内存使用完毕后,需要及时释放内存,否则可能产生内存泄漏,释放内存函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "./MALLOC/malloc.h"
void example_fun(void)
{
uint8_t ptr;
/* 初始化内部SRAM内存池 */
my_mem_init(SRAMIN);
/* 申请1KB内存 */
ptr = (uint8_t *)mymalloc(SRAMIN, 1024);
/* Do something. */
/* 释放无用内存 */
myfree(SRAMIN, ptr);
}
48.2.2 实验应用代码
本章实验的应用代码,如下所示:
int main(void)
{
uint8_t t = 0;
uint8_t key;
uint8_t *p_sramin = NULL;
uint8_t *p_sramccm = NULL;
uint8_t *p_sramex = NULL;
uint32_t tp_sramin = 0;
uint32_t tp_sramccm = 0;
uint32_t tp_sramex = 0;
uint8_t paddr[32];
uint16_t memused;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); /* 设置中断优先级分组为组3 */
sys_apm32_clock_init(336, 8, 2, 7); /* 配置系统时钟 */
delay_init(168); /* 初始化延时功能 */
usart_init(115200); /* 初始化串口 */
usmart_dev.init(84); /* 初始化USMART */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
lcd_init(); /* 初始化LCD */
sram_init(); /* 初始化外部SRAM */
my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */
my_mem_init(SRAMCCM); /* 初始化CCM内存池 */
my_mem_init(SRAMEX); /* 初始化外部SRAM内存池 */
lcd_show_string(30, 50, 200, 16, 16, "APM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "MALLOC TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0:Malloc & WR & Show", RED);
lcd_show_string(30, 130, 200, 16, 16, "KEY_UP:Free", RED);
lcd_show_string(30, 162, 200, 16, 16, "SRAMIN USED:", BLUE);
lcd_show_string(30, 178, 200, 16, 16, "SRAMCCM USED:", BLUE);
lcd_show_string(30, 194, 200, 16, 16, "SRAMEX USED:", BLUE);
while (1)
{
t++;
key = key_scan(0);
switch (key)
{
case KEY0_PRES:/* 申请内存 */
{
/* 从所有内存池中申请内存 */
p_sramin = mymalloc(SRAMIN, 2048);
p_sramccm = mymalloc(SRAMCCM, 2048);
p_sramex = mymalloc(SRAMEX, 2048);
/* 内存申请成功 */
if ( (p_sramin != NULL) &&
(p_sramccm != NULL) &&
(p_sramex != NULL))
{
/* 使用申请到的内存 */
sprintf((char *)p_sramin,
"SRAMIN: Malloc Test%03d",
t + SRAMIN);
lcd_show_string(30, 260, 239, 16, 16,
(char *)p_sramin, BLUE);
sprintf((char *)p_sramccm,
"SRAMCCM: Malloc Test%03d",
t + SRAMCCM);
lcd_show_string(30, 276, 239, 16, 16,
(char *)p_sramccm, BLUE);
sprintf((char *)p_sramex,
"SRAMEX: Malloc Test%03d",
t + SRAMEX);
lcd_show_string(30, 292, 239, 16, 16,
(char *)p_sramex, BLUE);
}
/* 内存申请失败 */
else
{
/* 释放申请成功的内存 */
myfree(SRAMIN, p_sramin);
myfree(SRAMCCM, p_sramccm);
myfree(SRAMEX, p_sramex);
p_sramin = NULL;
p_sramccm = NULL;
p_sramex = NULL;
}
break;
}
case WKUP_PRES:/* 释放内存 */
{
myfree(SRAMIN, p_sramin);
myfree(SRAMCCM, p_sramccm);
myfree(SRAMEX, p_sramex);
p_sramin = NULL;
p_sramccm = NULL;
p_sramex = NULL;
break;
}
}
/* 内存申请成功 */
if ((tp_sramin != (uint32_t)p_sramin) ||
(tp_sramccm != (uint32_t)p_sramccm) ||
(tp_sramex != (uint32_t)p_sramex))
{
tp_sramin = (uint32_t)p_sramin;
tp_sramccm = (uint32_t)p_sramccm;
tp_sramex = (uint32_t)p_sramex;
/* 显示申请到的内存的首地址 */
sprintf((char *)paddr, "SRAMIN: Addr: 0x%08X", (uint32_t)p_sramin);
lcd_show_string(30, 210, 239, 16, 16, (char *)paddr, BLUE);
sprintf((char *)paddr, "SRAMCCN: Addr: 0x%08X", (uint32_t)p_sramccm);
lcd_show_string(30, 226, 239, 16, 16, (char *)paddr, BLUE);
sprintf((char *)paddr, "SRAMEX: Addr: 0x%08X", (uint32_t)p_sramex);
lcd_show_string(30, 242, 239, 16, 16, (char *)paddr, BLUE);
}
/* 内存被释放了 */
else if ((p_sramin == NULL) || (p_sramccm == NULL) || (p_sramex == NULL))
{
lcd_fill(30, 210, 239, 319, WHITE);
}
if ((t % 20) == 0)
{
/* 显示内部SRAM使用率 */
memused = my_mem_perused(SRAMIN);
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);
lcd_show_string(30 + 112, 162, 200, 16, 16, (char *)paddr, BLUE);
/* 显示CCM使用率 */
memused = my_mem_perused(SRAMCCM);
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);
lcd_show_string(30 + 112, 178, 200, 16, 16, (char *)paddr, BLUE);
/* 显示外部SRAM使用率 */
memused = my_mem_perused(SRAMEX);
sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);
lcd_show_string(30 + 112, 194, 200, 16, 16, (char *)paddr, BLUE);
LED0_TOGGLE();
}
delay_ms(10);
}
}
可以看到,本实验的应用代码使用到了三个内存池,分别为内部SRAM、CCM和外部SRAM,在完成内存池初始化后,便在LCD上实时刷新显示三个内存池的使用量,以及检测按键输入,若检测到KEY0按键被按下,则从三个内存池中申请三块内存,并写入测试数据,然后将申请到的三块内存的起始地址即内存中写入的测试数据在LCD上进行显示,若检测到KEY_UP按键被按下,则释放最近一次申请的三块内存回对应的内存池中。
48.3 下载验证
在完成编译和烧录操作后,可以看到LCD上实时地显示了三个内存池的使用情况,此时按下KEY0按键申请内存,可以看到三个内存池的使用量增加,并且LCD上显示了申请到的三个内存的起始地址和内存中写入的测试数据,接着按下KEY_UP按键释放内存,可以看到,LCD上显示的内存信息消失,并且因为内存已经被释放回内存池,因此内存池的使用量较少。