【STM32】内存管理

news2024/11/29 4:30:23

 首先问个问题,你知道如何在LCD上显示SD卡文件浏览?-----需要读取所有文件名到内存,然后才能显示到LCD上。

一般的方法:是定义一个数组来存储文件名

1:需要知道最大文件名的长度

2:需要知道文件的个数。100?1000?10000?

倘若没有内存管理的话,我们得定义一个uint8_t fileNametBL【10000】【255】的数组!你想想会占用多少的内存呢?-----2550k字节内存(如果实际文件大小没有2550k字节,则会大大造成浪费!)

一、内存管理简介

内存管理,是指软件运行时对MCU内存资源的分配使用的技术。其最主要的目的是:如何高效、快速的分配,并且在适当的时候释放和回收内存资源**

内存使用三部曲:内存申请(分配)

内存使用

内存释放

内存管理的实现方式有很多种,最终都是实现2个函数:malloc[用于内存的申请]和free[用于内存的释放]

标准的C语言库也提供了mallow函数和free函数来实现动态申请和释放内存。那为啥我们不用C语言库的呢?

原因如下:

  • 占用大量的代码空间 不适合在资源紧缺的嵌入式系统当中

  • 没有线程安全的相关机制

  • 运行有不确定性,每次调用这些函数时花费的时间可能都不一样

  • 内存碎片化

所以我们接下来会学习一种内存管理叫----“分块式内存管理”

二、分块式内存管理

(一) 介绍

分块式内存管理由内存池和内存管理表两部分组成。内存池被等分为n块,对应的内存管理表,大小也为n,内存管理表的每一个项对应内存池的一个内存。

在这里插入图片描述

内存管理表的项值代表的意义:

当该项值为0时,代表对应的内存块未被占用。

当该项值为非零时,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。

当内存管理初始化时,内存管理表全部清零,表示没有任何内存块被占用

1.分配方向

从顶——>底的分配方向:即先从最末端开始找空内存。

2.分配原理:

当指针p调用malloc申请内存时:

1.先判断p要分配的内存块数(m)

2.从第n项开始,向下查找,直到找到有m块连续的空内存块(即对应内存管理表项为0)

3.将这m个内存管理表项的值都设置为m(标记为占用)

4.把最后的这个空内存块的地址返回指针p,完成一次分配

注:若当内存不够时(就是找到最后也没有找到连续空闲m块空闲内存),则返回MULL给p,表示分配失败!

3.释放原理

当指针p申请的内存用完,需要释放的时候,调用free函数实现。

free函数实现:

1.判断p指向的内存地址所对应的内存块

2.找到对应的内存管理项目,得到p所占用的内存块数目m

3.将这m个内存管理项目的值都清零,标记释放,完成一次内存释放

4.管理内存情况(基于STM32F429):

内存池大小:160*1024 B

内存块大小:64B

内存块数目 = 管理表项数目 =内存池大小/内存块大小= 160 * 1024 / 64

管理表项大小:2B(当申请内存后,内存管理表会做标记,最多是5120块,用2B就够了,,因为5120<65535)

占用内存大小=内存池+内存管理表

内存池占用的内存大小=内存块大小 * 内存块数

(二)内存管理使用

1.内存管理控制器结构体

这里有重要的内存管理控制器结构体和内存表参数宏定义

/* mem1 内存池1参数设定.mem1 完全处于F429内部 SRAM (最大192k)里面. */
#define MEM1_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
#define MEM1_MAX_SIZE 160 * 1024 /* 最大管理内存 160K */
#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE /* 内存池1的内存表大小 */
 
/* mem2 内存池2参数设定.mem2 处于 CCM,用于管理 CCM(特别注意,这部分 SRAM,仅 CPU 可以访问!!) */
#define MEM2_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
#define MEM2_MAX_SIZE 60 * 1024 /* 最大管理内存 60K */
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE /* 内存池2的内存表大小 */
 
/* mem3 内存池3参数设定.mem3的内存池处于外部 SDRAM 里面 */
#define MEM3_BLOCK_SIZE 64 /* 内存块大小为 64 字节 */
#define MEM3_MAX_SIZE 28912 * 1024 /* 最大管理内存 28912K */
#define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE /* 内存池3的内存表大小 */
​
​
/* 内存池(32 字节对齐) */
static __align(32) uint8_t mem1base[MEM1_MAX_SIZE]; /* 内部 SRAM 内存池 */ 
static __align(32) uint8_t mem2base[MEM2_MAX_SIZE]
__attribute__((at(0X10000000))); /* 内部 CCM 内存池 */
static __align(32) uint8_t mem3base[MEM3_MAX_SIZE]
__attribute__((at(0XC01F4000)));
/* 外部 SDRAM 内存池,前面 2M 给 LTDC 用了(1280*800*2) */
/* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部 SRAM 内存池 MAP */
static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE]
 __attribute__((at(0X10000000 + MEM2_MAX_SIZE))); /* 内部 CCM 内存池 MAP */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE]
 __attribute__((at(0XC01F4000+ MEM3_MAX_SIZE))); /* 外部 SRAM 内存池 MAP */
#else /* 使用 AC6 编译器时 */
/* 内存池(32 字节对齐) */
static __ALIGNED(32) uint8_t mem1base[MEM1_MAX_SIZE]; 
/* 内部 SRAM 内存池 */
static __ALIGNED(32) uint8_t mem2base[MEM2_MAX_SIZE]
__attribute__((section(".bss.ARM.__at_0X10000000"))); 
/* 内部 CCM 内存池 */
static __ALIGNED(32) uint8_t mem3base[MEM3_MAX_SIZE]
__attribute__((section(".bss.ARM.__at_0XC01F4000")));
/* 外部 SDRAM 内存池,前面 2M 给 LTDC 用了(1280*800*2) */
/* 内存管理表 */
static MT_TYPE mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; /* 内部 SRAM 内存池 MAP */
static MT_TYPE mem2mapbase[MEM2_ALLOC_TABLE_SIZE]
__attribute__((section(".bss.ARM.__at_0X1000F000"))); /* 内部 CCM 内存池 MAP */
static MT_TYPE mem3mapbase[MEM3_ALLOC_TABLE_SIZE]
__attribute__((section(".bss.ARM.__at_0XC1E30000"))); /* 外部 SRAM 内存池 MAP */
#endif
/* 内存管理参数 */
const uint32_t memtblsize[SRAMBANK] = {MEM1_ALLOC_TABLE_SIZE,
MEM2_ALLOC_TABLE_SIZE, MEM3_ALLOC_TABLE_SIZE}; /* 内存表大小 */
const uint32_t memblksize[SRAMBANK] = {MEM1_BLOCK_SIZE, MEM2_BLOCK_SIZE,
MEM3_BLOCK_SIZE}; /* 内存分块大小 */
/* 内存总大小 */
const uint32_t memsize[SRAMBANK]={MEM1_MAX_SIZE, MEM2_MAX_SIZE, MEM3_MAX_SIZE}; 
/* 内存管理控制器 */
struct _m_mallco_dev mallco_dev=
{
  my_mem_init, /* 内存初始化 */
  my_mem_perused, /* 内存使用率 */
  mem1base,mem2base,mem3base, /* 内存池 */
  mem1mapbase,mem2mapbase,mem3mapbase, /* 内存管理状态表 */
  0, 0, 0, /* 内存管理未就绪 */
};
2.初始化内存
void my_mem_init(uint8_t memx)  
{  
    my_mem_set(mallco_dev.memmap[memx], 0, memtblsize[memx] * 4);  /* 内存状态表数据清零 */
    mallco_dev.memrdy[memx] = 1;                                   /* 内存管理初始化OK */
}

其中

void my_mem_set(void *s, uint8_t c, uint32_t count)  
{  
    uint8_t *xs = s;  
    while (count--)
        *xs++ = c;  
}  

获取内存使用率

/**
 * @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 < memtblsize[memx]; i++)  
    {
        if (mallco_dev.memmap[memx][i])
        {
            used++;
        }
    }
​
    return (used * 1000) / (memtblsize[memx]);  
}
3.申请内存

/**
 * @brief       分配内存(外部调用)
 * @param       memx : 所属内存块
 * @param       size : 要分配的内存大小(字节)
 * @retval      分配到的内存首地址.
 */
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);//返回的是内存的首地址
    }
}

其中

/**
 * @brief       内存分配(内部调用)
 * @param       memx : 所属内存块
 * @param       size : 要分配的内存大小(字节)
 * @retval      内存偏移地址
 *   @arg       0 ~ 0xFFFFFFFE : 有效的内存偏移地址
 *   @arg       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 (!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;                                          /* 未找到符合分配条件的内存块 */
}

4.操作内存
/**
 * @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;  
}  
​

5.释放内存
/**
 * @brief       释放内存(内部调用)
 * @param       memx   : 所属内存块
 * @param       offset : 内存地址偏移
 * @retval      释放结果
 *   @arg       0, 释放成功;
 *   @arg       1, 释放失败;
 *   @arg       2, 超区域了(失败);
 */
uint8_t my_mem_free(uint8_t memx, uint32_t offset)
{
    int i;
​
    if (!mallco_dev.memrdy[memx])                   /* 未初始化,先执行初始化 */
    {
        mallco_dev.init(memx);
        return 1;                                   /* 未初始化 */
    }
    //通过offset确定偏移在第几个内存块以及获取内存块数
    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;                                  /* 偏移超区了. */
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2227345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV图像处理方法:腐蚀操作

腐蚀操作 前提 图像数据为二值的&#xff08;黑/白&#xff09; 作用 去掉图片中字上的毛刺 显示图片 读取一个图像文件&#xff0c;并在一个窗口中显示它。用户可以查看这个图像&#xff0c;直到按下任意键&#xff0c;然后程序会关闭显示图像的窗口 # cv2是OpenCV库的P…

使用Python中的jieba库进行简单情感分析

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情感分析是一项重要的任务&#xff0c;它可以帮助我们理解文本背后的情感倾向。本文将通过一个简单的例子来介绍如何使用Python的jieba库对中文文本进行基本的情感分析。 1. 环境准备 首先&#xff0c;确保已经安装…

图像分割从基础到进阶:阈值化、K-means和Mean-Shift算法的应用

图像分割是计算机视觉中的一项关键技术&#xff0c;用来将图像划分为若干个 有意义 的区域&#xff0c;以便后续的图像处理和分析工作。根据任务的不同&#xff0c;图像分割可以进一步细分为语义分割、实例分割和全景分割&#xff1a; 语义分割 (Semantic Segmentation) 对图像…

SQL进阶技巧:如何求组内排除当前行的移动平均值?

目录 0 需求描述 2 数据准备 3 问题分析 4 小结 0 需求描述 -- 按照 日期,省份,等级 分组 求分数的平均值;但是需要剔除当前行的数据 2 数据准备 create table avgtest as (select 2024-10-24 as cdate, 广东 as province,深圳 as city, 2 as level, 200 as scoreunio…

后台管理系统的通用权限解决方案(六)SpringBoot整合Logback实现日志记录

1 Logback介绍 1.1 Logback的组件 由上图可知&#xff0c;Logback和Log4j都是slf4j规范的具体实现。在程序中直接调用Logback的API其实都是slf4j规范定义好的API&#xff0c;其底层则是真正的日志实现组件—Logback或者Log4j。 Logback构建在三个主要的类上&#xff1a;Logge…

厨艺爱好者的在线互动平台:Spring Boot实现

摘 要 使用旧方法对厨艺交流信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在厨艺交流信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次开发的厨艺交流平台功能…

面试域——技术面试准备

摘要 来到技术面试这环节有两种情况&#xff0c;其一&#xff1a;这场技术面试可能就是一个面试官KPI面试&#xff08;就是面试工作量&#xff0c;这个面试你是不可能过。&#xff09;如今的就业环境下&#xff0c;人力资源部门也是有考核指标。如果遇到这样的面试你就放平心态…

2024年【焊工(中级)】最新解析及焊工(中级)考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 焊工&#xff08;中级&#xff09;最新解析参考答案及焊工&#xff08;中级&#xff09;考试试题解析是安全生产模拟考试一点通题库老师及焊工&#xff08;中级&#xff09;操作证已考过的学员汇总&#xff0c;相对有…

VsCode | 修改内置字体为JetBrains Mono NL

文章目录 一、下载JetBrains Mono NL字体二、VsCode进行字体的设置 一、下载JetBrains Mono NL字体 字体下载 下载完成以后解压找到JetBrainsMono-2.304\fonts\ttf文件夹下&#xff0c;全选鼠标右键点安装即可。 注意&#xff1a;一定要全部安装&#xff0c;否则字体样式可…

【微服务】Nacos 注册中心

<!-- nacos 依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${nacos.version}</version><type>pom</type><scope>import&l…

Wasserstein Generative Adversarial Networks

Abstract 我们引入了一种名为 WGAN 的新算法&#xff0c;它是传统 GAN 训练的替代方案。 在这个新模型中&#xff0c;我们表明我们可以提高学习的稳定性&#xff0c;摆脱模式崩溃等问题&#xff0c;并提供对调试和超参数搜索有用的有意义的学习曲线。 此外&#xff0c;我们表明…

pip命令行安装pytest 一直报错

其实就是切换不同镜像安装 我最终成功的是阿里云镜像 pip install --trusted-host mirrors.aliyun.com pytest 也可以用其他的 pip install -i https://pypi.org/simple pytest # 或者使用其他的镜像源 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest

v4.7版本使用线下付款方式不给管理员发送新订单通知问题修复

在app/api/controller/v1/order/StoreOrderController.php文件中&#xff0c;将红框内的代码注释&#xff0c;加上绿框的代码即可修复 if ($this->services->setOrderTypePayOffline($order[order_id])) {event(NoticeListener, [$order, admin_pay_success_code]);retur…

DIY可视化-uniapp悬浮菜单支持拖动、吸附-代码生成器

在Uniapp中&#xff0c;悬浮菜单支持拖动和吸附功能&#xff0c;可以为用户带来更加灵活和便捷的操作体验。以下是对这两个功能的详细解释&#xff1a; 悬浮菜单支持拖动 提高用户体验&#xff1a;用户可以根据自己的需要&#xff0c;将悬浮菜单拖动到屏幕上的任意位置&#x…

MySQL企业常见架构与调优经验分享

文章目录 一、选择 PerconaServer、MariaDB 还是 MYSQL二、常用的 MYSQL 调优策略三、MYSOL 常见的应用架构分享四、MYSOL 经典应用架构 观看学习课程的笔记&#xff0c;分享于此~ 课程&#xff1a;MySQL企业常见架构与调优经验分享 mysql官方优化文档 调优MySQL参数 一、选择 …

Stable Diffusion 3.5发布:图像生成新纪元,多模态AI的突破!

在人工智能的图像生成领域&#xff0c;我们刚刚迎来了一位新的明星——Stable Diffusion 3.5。这是一款由多模态扩散Transformer&#xff08;MMDiT&#xff09;驱动的文本到图像模型&#xff0c;它在图像质量、字体处理、复杂提示理解以及资源效率方面都实现了显著提升。今天&a…

华为ensp静态路由,浮动路由,缺省路由讲解及配置

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;网络通信基础TCP/IP专栏&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年10月24日0点15分 祝大家程序员节快乐~ 路由的选择与管理至关重要。静态路由…

少儿编程进入义务教育课程:培养信息科技素养的新政策解读

近年来&#xff0c;随着数字化进程的推进和人工智能技术的普及&#xff0c;编程教育逐渐走入中小学课堂。教育部在《义务教育课程方案和课程标准&#xff08;2022年版&#xff09;》中正式将编程与信息科技教育纳入小学和初中的课程体系中&#xff0c;强调培养学生的计算思维、…

js监听div尺寸,ResizeObserver

示例&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><style>.observedDiv {width: 40vw;height: 50vh;background-color: lightblue;}</style></head><body><div id"…

NVR批量管理软件/平台EasyNVR多个NVR同时管理:H.265与H.264编码优势和差异深度剖析

在数字化安防领域&#xff0c;视频监控系统正逐步成为各行各业不可或缺的一部分。随着技术的不断进步&#xff0c;传统的视频监控系统已经难以满足日益复杂和多变的监控需求。下面我们谈及NVR批量管理软件/平台EasyNVR平台H.265与H.264编码优势及差异。 一、EasyNVR视频汇聚平台…