OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2580|回复: 0

《STM32F407 探索者开发指南》第四十九章 内存管理实验

[复制链接]

1140

主题

1152

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4895
金钱
4895
注册时间
2019-5-8
在线时间
1248 小时
发表于 2023-8-30 17:51:28 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-8-29 15:14 编辑

第四十九章 内存管理实验
1)实验平台:正点原子探索者STM32F407开发板

2) 章节摘自【正点原子】STM32F407开发指南 V1.1

3)购买链接:https://detail.tmall.com/item.htm?id=609294673401

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32f407_explorerV3.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)STM32技术交流QQ群:151941872

155537c2odj87vz1z9vj6l.jpg
155537nfqovl2gg9faaol9.png

本章,我们将介绍内存管理。我们将使用内存的动态管理减少对内存的浪费。本章分为如下几个小节:
49.1 内存管理简介
49.2 硬件设计
49.3 程序设计
49.4 下载验证

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

本章,我们介绍一种比较简单的办法来实现:分块式内存管理。下面我们介绍一下该方法的实现原理,如图49.1.1所示:                                 
image001.png
图49.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个内存管理表项目的值都清零,标记释放,完成一次内存释放。

49.2 硬件设计
1. 例程功能
每次按下按键KEY0就申请2K字节内存,每次按下KEY1就写数据到申请到的内存里面,每次按下WK_UP按键用于释放内存。LED0闪烁用于提示程序正在运行。

2. 硬件资源
1)LED灯
LED0 – PF9
2)独立按键  
KEY0 – PE4
KEY1 – PE3
WK_UP – PA0
3)串口1(USMART使用)
4)正点原子 2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
5)STM32自带的SRAM
6)开发板板载的SRAM:XM8A51216/IS62WV51216(通过FSMC控制)

49.3程序设计
49.3.1 程序流程图
QQ截图20230829151301.png
图49.3.1.1 内存管理实验程序流程图

49.3.2 程序解析
1.内存管理代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。内存管理驱动源码包括两个文件:malloc.c和malloc.h。这两个文件放在Middlewares文件夹下面的MALLOC文件夹。

下面我们直接介绍malloc.h中比较重要的一个结构体和内存参数宏定义,其定义如下:
  1. /* mem1内存参数设定.mem1完全处于内部SRAM. */
  2. #define MEM1_BLOCK_SIZE    32           /* 内存块大小为32字节 */
  3. #define MEM1_MAX_SIZE      100 * 1024  /* 最大管理内存 100K */
  4. #define MEM1_ALLOC_TABLE_SIZE   MEM1_MAX_SIZE/MEM1_BLOCK_SIZE   /* 内存表大小 */
  5. /* mem2内存参数设定.mem2处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问 */
  6. #define MEM2_BLOCK_SIZE    32           /* 内存块大小为32字节 */
  7. #define MEM2_MAX_SIZE      60 *1024    /* 最大管理内存60K */
  8. #define MEM2_ALLOC_TABLE_SIZE   MEM2_MAX_SIZE/MEM2_BLOCK_SIZE   /* 内存表大小 */
  9. /* mem3内存参数设定.mem3是外扩SRAM. */
  10. #define MEM3_BLOCK_SIZE    32           /* 内存块大小为32字节 */
  11. #define MEM3_MAX_SIZE      963 * 1024  /* 最大管理内存 100K */
  12. #define MEM3_ALLOC_TABLE_SIZE   MEM3_MAX_SIZE/MEM3_BLOCK_SIZE   /* 内存表大小 */
  13. /* 如果没有定义NULL,定义NULL */
  14. #ifndef NULL
  15. #define NULL 0
  16. #endif
  17. /* 内存管理控制器 */
  18. struct _m_mallco_dev
  19. {
  20.     void (*init)(uint8_t);           /* 初始化 */
  21.     uint16_t (*perused)(uint8_t);   /* 内存使用率 */
  22.     uint8_t *membase[SRAMBANK];     /* 内存池 管理SRAMBANK个区域的内存 */
  23.     MT_TYPE *memmap[SRAMBANK];       /* 内存管理状态表 */
  24.     uint8_t  memrdy[SRAMBANK];       /* 内存管理是否就绪 */
  25. };
复制代码
我们可以定义几个不同的内存管理表,再分配相应的指针给到管理控制器即可。程序中我们用宏定义MEM1_BLOCK_SIZE来定义malloc可以管理的内部内存池总大小,实际上我们定义为一个大小为MEM1_BLOCK_SIZE的数组,这样编译后就能获得一块实际的连续内存区域,这里是100K,MEM1_ALLOC_TABLE_SIZE代表内存池的内存管理表大小。我们可以定义多个内存管理表,这样就可以同时管理多块内存。

从这里可以看出,如果内存分块越小,那么内存管理表就越大,当分块为4字节1个块的时候,内存管理表就和内存池一样大了(管理表的每项都是uint16_t类型),显然是不合适。我们这里取64字节,比例为1:16,内存管理表相对就比较小了。

通过这个内存管理控制器_m_malloc_dev结构体,我们把分块式内存管理的相关信息,其初始化函数、获取使用率、内存池、内存管理表以及内存管理的状态保存下来,实现对内存池的管理控制。

下面介绍malloc.c文件,内存池、内存管理表、内存管理参数和内存管理控制器的定义如下:
  1. #if !(__ARMCC_VERSION >= 6010050)   /* 不是AC6编译器,即使用AC5编译器时 */
  2. /* 内存池(64字节对齐) */
  3. static __align(64) uint8_t mem1base[MEM1_MAX_SIZE];    /* 内部SRAM内存池 */
  4. static __align(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((at(0x10000000)));     /* 内部CCM内存池 */
  5. static __align(64) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((at(0x68000000)));     /* 外部SRAM内存池 */
  6. /* 内存管理表 */
  7. static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE];     /* 内部SRAM内存池MAP */
  8. static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0x10000000 + MEM2_MAX_SIZE)));  /* 内部CCM内存池MAP */
  9. static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0x68000000 + MEM3_MAX_SIZE)));  /* 外部SRAM内存池MAP */
  10. #else     /* 使用AC6编译器时 */
  11. /* 内存池(64字节对齐) */
  12. static __ALIGNED(64) uint8_t mem1base[MEM1_MAX_SIZE];  /* 内部SRAM内存池 */
  13. static __ALIGNED(64) uint8_t mem2base[MEM2_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0x10000000")));  /* 内部CCM内存池 */
  14. static __ALIGNED(64) uint8_t mem3base[MEM3_MAX_SIZE] __attribute__((section(".bss.ARM.__at_0x68000000"))); /* 外部SRAM内存池 */
  15. /* 内存管理表 */
  16. static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE];     /* 内部SRAM内存池MAP */
  17. static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((section(".bss.ARM.__at_0x1000F000"))); /* 内部CCM内存池MAP */
  18. static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((section(".bss.ARM.__at_0x680F0C00"))); /* 外部SRAM内存池MAP */
  19. #endif
  20. /* 内存管理参数 */
  21. const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE};         /* 内存表大小 */
  22. const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE, MEM3_BLOCK_SIZE};                                          /* 内存分块大小 */
  23. const uint32_t memsize[SRAMBANK] = {MEM1_MAX_SIZE, MEM2_MAX_SIZE, MEM3_MAX_SIZE};                                               /* 内存总大小 */
  24. /* 内存管理控制器 */
  25. struct _m_mallco_dev mallco_dev =
  26. {
  27.    my_mem_init,                                 /* 内存初始化 */
  28.    my_mem_perused,                             /* 内存使用率 */
  29.    mem1base, mem2base, mem3base,             /* 内存池 */
  30.    mem1mapbase, mem2mapbase, mem3mapbase, /* 内存管理状态表 */
  31.     0, 0, 0,                                     /* 内存管理未就绪 */
  32. };
复制代码
MDK支持用__attirbute__((at(地址)))的方法把变量定义到指定的区域,而且这个变量支持是算式,大家可以去MKD的帮助文件中查找__attribute__这个关键字查找相关信息,有比较详细的介绍。

我们通过判断编译器的版本,来执行不同方式的定义,对于AC5来说,使用的是__attirbute__((at(地址))),但是如果你想换成AC6编译器,指定变量位置的函数变成__attribute__((section(“.bss.ARM.__at_地址”)))的方式,其中的.bss表示初始化值为0,而且这个方式不支持算式,所以还用上面的方法直接用宏计算出SRAM的地址的方法不可行了,所以我们需要直接手动算出SRAM对应的内存地址,同样地__align(64)在AC6下的写法也变成了__ALIGNED(64),还有其它差异的部分,大家参考MDK官方提供的AC5到AC6的迁移方法的文档,我们主要介绍AC5编译器。

我们通过内存管理控制器mallco_dev结构体,实现对三个内存池的管理控制。
第一个是内部SRAM内存池,定义为:
  1. static __align(64) uint8_t  mem1base[MEM1_MAX_SIZE];        /* 内部SRAM内存池 */
复制代码
第二个是内部CCM内存池,定义为:
  1. static __align(64) uint8_t mem2base[MEM2_MAX_SIZE]
  2.                               __attribute__((at(0x10000000)));  /* 内部CCM内存池 */
复制代码
第三个是外部SRAM内存池,定义为:
  1. static __align(64) uint8_t mem3base[MEM3_MAX_SIZE]
  2.                               __attribute__((at(0x68000000)));  /* 外部SRAM内存池 */
复制代码
这里之所以要定义成3个,是因为这三个个内存区域的地址不一样,STM32F4内部内存分为两大块:1,普通内存(又分为主要内存和辅助内存,地址从:0x2000 0000开始,共128KB),这部分内存任何外设都可以访问。2,CCM内存(地址从:0x1000 0000开始,共64KB),这部分内存仅CPU可以访问,DMA之类的不可以直接访问,使用时得特别注意!最后就是外部SRAM(地址从0x6800 0000开始,共963KB),这部分内存任何外设都可以访问。

下面介绍其他的malloc代码,具体如下:
  1. /**
  2. *@brief        复制内存
  3. *@param        *des : 目的地址
  4. *@param        *src : 源地址
  5. *@param        n     : 需要复制的内存长度(字节为单位)
  6. *@retval       无
  7. */
  8. void my_mem_copy(void *des, void *src, uint32_t n)
  9. {
  10.     uint8_t  *xdes = des;
  11.     uint8_t  *xsrc = src;
  12.     while (n--)*xdes++ = *xsrc++;
  13. }
  14. /**
  15. *@brief        设置内存值
  16. *@param        *s     : 内存首地址
  17. *@param        c      : 要设置的值
  18. *@param        count : 需要设置的内存大小(字节为单位)
  19. *@retval       无
  20. */
  21. void my_mem_set(void *s, uint8_t c, uint32_t count)
  22. {
  23.     uint8_t *xs = s;
  24.     while (count--)*xs++ = c;
  25. }
  26. /**
  27. *@brief       内存管理初始化
  28. *@param       memx : 所属内存块
  29. *@retval      无
  30. */
  31. void my_mem_init(uint8_t memx)
  32. {
  33. /* 内存状态表数据清零 */
  34. mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx] * 2);
  35.    mymemset(mallco_dev.membase[memx], 0, memsize[memx]);  /* 内存池所有数据清零 */
  36.    mallco_dev.memrdy[memx] = 1;                                /* 内存管理初始化OK */
  37. }
  38. /**
  39. *@brief        获取内存使用率
  40. *@param        memx : 所属内存块
  41. *@retval       使用率(扩大了10倍,0~1000,代表0.0%~100.0%)
  42. */
  43. uint16_t my_mem_perused(uint8_t memx)
  44. {
  45.     uint32_t used = 0;
  46.     uint32_t i;
  47.     for (i = 0; i < memtblsize[memx]; i++)
  48.     {
  49.        if (mallco_dev.memmap[memx]) used++;
  50.     }
  51.     return (used * 1000) / (memtblsize[memx]);
  52. }
  53. /**
  54. *@brief        内存分配(内部调用)
  55. *@param        memx : 所属内存块
  56. *@param        size : 要分配的内存大小(字节)
  57. *@retval       内存偏移地址
  58. *  @arg        0 ~ 0xFFFFFFFE : 有效的内存偏移地址
  59. *  @arg        0xFFFFFFFF      : 无效的内存偏移地址
  60. */
  61. static uint32_t my_mem_malloc(uint8_t memx, uint32_t size)
  62. {
  63.     signed long offset = 0;
  64.     uint32_t nmemb;          /* 需要的内存块数 */
  65.     uint32_t cmemb = 0;     /* 连续空内存块数 */
  66.     uint32_t i;
  67.     if (!mallco_dev.memrdy[memx])
  68.     {
  69.        mallco_dev.init(memx);             /* 未初始化,先执行初始化 */
  70.     }
  71.     if (size == 0) return 0xFFFFFFFF;    /* 不需要分配 */
  72.    nmemb = size / memblksize[memx];     /* 获取需要分配的连续内存块数 */
  73.     if (size % memblksize[memx]) nmemb++;
  74. /* 搜索整个内存控制区 */
  75.     for (offset = memtblsize[memx] - 1; offset >= 0; offset--)
  76.     {
  77.        if (!mallco_dev.memmap[memx][offset])
  78.        {
  79.            cmemb++;            /* 连续空内存块数增加 */
  80.        }
  81.        else
  82.        {
  83.            cmemb = 0;         /* 连续内存块清零 */
  84.        }
  85.       
  86.        if (cmemb == nmemb)  /* 找到了连续nmemb个空内存块 */
  87.        {
  88.            for (i = 0; i < nmemb; i++) /* 标注内存块非空 */
  89.            {
  90.                 mallco_dev.memmap[memx][offset + i] = nmemb;
  91.            }
  92.            return (offset * memblksize[memx]); /* 返回偏移地址 */
  93.        }
  94.     }
  95.     return 0xFFFFFFFF;       /* 未找到符合分配条件的内存块 */
  96. }
  97. /**
  98. *@brief        释放内存(内部调用)
  99. *@param        memx   : 所属内存块
  100. *@param        offset : 内存地址偏移
  101. *@retval       释放结果
  102. *  @arg         0, 释放成功;
  103. *  @arg         1, 释放失败;
  104. *  @arg         2, 超区域了(失败);
  105. */
  106. static uint8_t my_mem_free(uint8_t memx, uint32_t offset)
  107. {
  108.     int i;
  109.     if (!mallco_dev.memrdy[memx])    /* 未初始化,先执行初始化 */
  110.     {
  111.        mallco_dev.init(memx);
  112.        return 1;                       /* 未初始化 */
  113.     }
  114.     if (offset < memsize[memx])      /* 偏移在内存池内. */
  115.     {
  116.        int index = offset / memblksize[memx];       /* 偏移所在内存块号码 */
  117.        int nmemb = mallco_dev.memmap[memx][index]; /* 内存块数量 */
  118.        for (i = 0; i < nmemb; i++)                    /* 内存块清零 */
  119.        {
  120.            mallco_dev.memmap[memx][index + i] = 0;
  121.        }
  122.        return 0;
  123.     }
  124.     else
  125.     {
  126.        return 2;    /* 偏移超区了. */
  127.     }
  128. }
  129. /**
  130. *@brief        释放内存(外部调用)
  131. *@param        memx : 所属内存块
  132. *@param        ptr  : 内存首地址
  133. *@retval       无
  134. */
  135. void myfree(uint8_t memx, void *ptr)
  136. {
  137.     uint32_t offset;
  138.     if (ptr == NULL)return;     /* 地址为0. */
  139.    offset = (uint32_t)ptr - (uint32_t)mallco_dev.membase[memx];
  140.    my_mem_free(memx, offset);  /* 释放内存 */
  141. }
  142. /**
  143. *@brief        分配内存(外部调用)
  144. *@param        memx : 所属内存块
  145. *@param        size : 要分配的内存大小(字节)
  146. *@retval       分配到的内存首地址.
  147. */
  148. void *mymalloc(uint8_t memx, uint32_t size)
  149. {
  150.     uint32_t offset;
  151.    offset =my_mem_malloc(memx, size);
  152.     if (offset == 0xFFFFFFFF)    /* 申请出错 */
  153.     {
  154.        return NULL;               /* 返回空(0) */
  155.     }
  156.     else    /* 申请没问题, 返回首地址 */
  157.     {
  158.        return (void *)((uint32_t)mallco_dev.membase[memx] + offset);
  159.     }
  160. }
  161. /**
  162. *@brief        重新分配内存(外部调用)
  163. *@param        memx : 所属内存块
  164. *@param        *ptr : 旧内存首地址
  165. *@param        size : 要分配的内存大小(字节)
  166. *@retval       新分配到的内存首地址.
  167. */
  168. void *myrealloc(uint8_t memx, void *ptr, uint32_t size)
  169. {
  170.     uint32_t offset;
  171.    offset =my_mem_malloc(memx, size);
  172.     if (offset == 0xFFFFFFFF)    /* 申请出错 */
  173.     {
  174.        return NULL;               /* 返回空(0) */
  175.     }
  176.     else    /* 申请没问题, 返回首地址 */
  177.     {
  178.        my_mem_copy((void *)((uint32_t)mallco_dev.membase[memx] + offset),
  179. ptr, size);           /* 拷贝旧内存内容到新内存 */
  180.        myfree(memx, ptr);                /* 释放旧内存 */
  181.        /* 返回新内存首地址 */
  182.        return (void *)((uint32_t)mallco_dev.membase[memx] + offset);
  183.     }
  184. }
复制代码
整个malloc代码的核心函数:my_mem_malloc和my_mem_free,分别用于内存申请和内存释放。思路就是前面35.1所介绍的分配内存和释放内存,不过在这里,这两个函数知识内部调用,外部调用我们另外定义了mymalloc和myfree两个函数,其他函数我们就不多介绍了。

2. main.c代码
main.c代码如下:
  1. const char *SRAM_NAME_BUF[SRAMBANK] = {" SRAMIN", "SRAMCCM ", " SRAMEX "};
  2. int main(void)
  3. {
  4.     uint8_t paddr[20];  /* 存放P Addr:+p地址的ASCII值 */
  5.     uint16_t memused = 0;
  6.     uint8_t key;
  7.     uint8_t i = 0;
  8.     uint8_t *p = 0;
  9.     uint8_t *tp = 0;
  10.     uint8_t sramx = 0;                  /* 默认为内部sram */
  11.     HAL_Init();                             /* 初始化HAL库 */
  12.    sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
  13.    delay_init(168);                       /* 延时初始化 */
  14.    usart_init(115200);                   /* 串口初始化为115200 */
  15.    usmart_dev.init(84);                  /* 初始化USMART */
  16.     led_init();                             /* 初始化LED */
  17.     lcd_init();                             /* 初始化LCD */
  18.     key_init();                             /* 初始化按键 */
  19.     sram_init();                            /* SRAM初始化 */
  20.     my_mem_init(SRAMIN);                  /* 初始化内部SRAM内存池 */
  21.     my_mem_init(SRAMEX);                  /* 初始化外部SRAM内存池 */
  22.     lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
  23.     lcd_show_string(30,  70, 200, 16, 16, "MALLOCTEST", RED);
  24.     lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  25.     lcd_show_string(30, 110, 200, 16, 16, "KEY0:Malloc & WR & Show", RED);
  26.     lcd_show_string(30, 130, 200, 16, 16, "KEY_UP:SRAMx KEY1:Free", RED);
  27.     lcd_show_string(60, 160, 200, 16, 16, " SRAMIN ", BLUE);
  28.     lcd_show_string(30, 176, 200, 16, 16, "SRAMIN  USED:", BLUE);
  29.     lcd_show_string(30, 192, 200, 16, 16, "SRAMCCM USED:", BLUE);
  30.     lcd_show_string(30, 208, 200, 16, 16, "SRAMEX  USED:", BLUE);
  31.     while (1)
  32.     {
  33.         key = key_scan(0);       /* 不支持连按 */
  34.         switch (key)
  35.         {
  36.             case KEY0_PRES:       /* KEY0按下 */
  37.                 /* 申请2K字节,并写入内容,显示在lcd屏幕上面 */
  38.                 p = mymalloc(sramx, 2048);
  39.                 if (p != NULL)
  40.                 {/* 向p写入一些内容 */
  41.                    sprintf((char *)p, "MemoryMalloc Test%03d", i);
  42. /* 显示P的内容 */
  43.                    lcd_show_string(30, 260, 209, 16, 16, (char *)p, BLUE);
  44.                 }
  45.                 break;
  46.             case KEY1_PRES:       /* KEY1按下 */
  47.                myfree(sramx, p);     /* 释放内存 */
  48.                 p = 0;                  /* 指向空地址 */
  49.                 break;
  50.             case WKUP_PRES:            /* KEY UP按下 */
  51.                sramx++;
  52.                 if (sramx > SRAMBANK)sramx = 0;
  53.                lcd_show_string(60, 160, 200, 16, 16,
  54. (char *)SRAM_NAME_BUF[sramx], BLUE);
  55.                 break;
  56.         }
  57.         if (tp != p)
  58.         {
  59.             tp = p;
  60.             sprintf((char *)paddr, "PAddr:0X%08X", (uint32_t)tp);
  61.             /* 显示p的地址 */
  62.            lcd_show_string(30, 240, 209, 16, 16, (char *)paddr, BLUE);
  63.             if (p)
  64.             {/* 显示P的内容 */
  65.                lcd_show_string(30, 260, 280, 16, 16, (char *)p, BLUE);
  66.             }
  67.             else
  68.             {
  69.                lcd_fill(30, 260, 209, 296, WHITE); /* p=0,清除显示 */
  70.             }
  71.         }
  72.         delay_ms(10);
  73.         i++;
  74.         if ((i % 20) == 0)   /* DS0闪烁. */
  75.         {
  76.             memused=my_mem_perused(SRAMIN);
  77.             sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);
  78. /* 显示内部内存使用率 */
  79.            lcd_show_string(30 + 112, 176, 200, 16, 16, (char *)paddr, BLUE);
  80.             memused=my_mem_perused(SRAMEX);
  81.             sprintf((char *)paddr, "%d.%01d%%", memused / 10, memused % 10);
  82.             /* 显示TCM内存使用率 */
  83.            lcd_show_string(30 + 112, 192, 200, 16, 16, (char *)paddr, BLUE);
  84.            LED0_TOGGLE();    /* LED0闪烁 */
  85.         }
  86.     }
  87. }
复制代码
该部分代码比较简单,主要是对mymalloc和myfree的应用。不过这里提醒大家,如果对一个指针进行多次内存申请,而之前的申请又没释放,那么将造成“内存泄露”,这是内存管理所不希望发生的,久而久之,可能导致无内存可用的情况!所以,在使用的时候,请大家一定记得,申请的内存在用完以后,一定要释放。

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

49.4 下载验证
将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如图49.4.1所示:     
image005.png
图49.4.1 内存管理实验测试图

可以看到,内存的使用率均为0%,说明还没有任何内存被使用。我们可以通过KEY_UP选择申请内存的位置:SRAMIN为内部,SRAMCCM为内部,SRAMEX为外部。此时我们选择从内部申请内存,按下KEY0,就可以看到申请了2%的一个内存块,同时看到下面提示了指针p所指向的地址(其实就是被分配到的内存地址)和内容。效果如图49.4.2所示。

KEY0键用来更新p的内容,更新后的内容将重新显示在LCD模块上。多按几次KEY0,可以看到内存使用率持续上升(注意比对p的值,可以发现是递减的,说明是从顶部开始分配内存!)。每次申请一个内存块后,可以通过按下KEY0释放本次申请的内存,如果我们每次申请完内存不再使用却不及时释放掉,再按KEY1将无法释放之前的内存了,当这样的情况重复了多次,就会造成“内存泄漏”。我们程序就是模拟这样一个情况,请大家在实际使用的时候去注意到这种做法的危险性,必须在编程时严格避免内存泄漏的情况发生。     
image007.png
图49.4.2 按下KEY0申请了部分内存

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

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


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-24 06:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表