【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(八)-认识内存管理
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(三)-SD卡的操作流程
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(五)-文件管理初步介绍
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(八)-认识内存管理
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡
首先说明一下,为何要介绍内存管理
在SD卡的读取中,你并不知道对方到底存了多少文件?文件名的长度又是多少?
文件个数先暂定100个额度吧,文件名长度先默认长文件名255字节
那么你要申请数组来记录,u8 file_name[100][255];
这样你就已经花掉25.5K的内存了,但你又保证,100个额度,绝对够用吗?
1000个额度?那就需要255K的内存了。。。
先看一下我使用的这颗芯片(本篇中提到芯片这个字样,并且没有明确说是哪个型号时,指的是我使用的这颗,也就是下图STM32F405RGT6)
RAM只有192K,还比255K小,这时候,就体现内存管理的作用了,当然,这里先不谈外部扩展的事情
芯片内部有三个内存,分别是SRAM1、SRAM2、CCM,如下图2显示
下图1红框:64K的CCM,是包含在192+4K里面的
CCM(core coupled memory)(核心耦合存储器):理论上是最快的,但是它只能被CPU访问,像其他外设(DMA、以太、USB),都无法访问
接着来讲一下【分块式内存管理】的原理
它是由内存池和内存管理表两个部分组成的
里面有n个内存块
每个内存块,对应内存管理表的一项
项值为0,代表对应的内存块未被使用
而项值为非0,里面的数值代表被连续占用的内存块数
分配内存时,先给一个参数m(此参数表示需要多少内存块)
然后从第n项开始,向下查找,直到发现m块连续的空内存
把这些连续的空内存,项值都设置成m
最终在把这些空内存块的地址返回(谁申请内存的就返回给谁)
由于我使用的是正点原子的探索者开发板,这里先提取两个文件《malloc.c》《malloc.h》
里面有封装好的函数,只要了解一些宏定义,和一些其他的设置即可
malloc.c
- #include "malloc.h"
- //内存池(32字节对齐)
- __align(32) u8 mem1base[MEM1_MAX_SIZE]; //内部SRAM内存池
- __align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000))); //外部SRAM内存池,attribute修饰,指向0x68000000首地址
- __align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000))); //内部CCM内存池,attribute修饰,指向0x10000000首地址
- //内存管理表
- u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
- u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE))); //外部SRAM内存池MAP
- u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE))); //内部CCM内存池MAP
- //内存管理参数
- const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //内存表大小
- const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE}; //内存分块大小
- const u32 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, //内存管理未就绪
- };
- //复制内存
- //*des:目的地址
- //*src:源地址
- //n:需要复制的内存长度(字节为单位)
- void mymemcpy(void *des,void *src,u32 n)
- {
- u8 *xdes=des;
- u8 *xsrc=src;
- while(n--)*xdes++=*xsrc++;
- }
- //设置内存
- //*s:内存首地址
- //c :要设置的值
- //count:需要设置的内存大小(字节为单位)
- void mymemset(void *s,u8 c,u32 count)
- {
- u8 *xs = s;
- while(count--)*xs++=c;
- }
- //内存管理初始化
- //memx:所属内存块
- void my_mem_init(u8 memx)
- {
- mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//内存状态表数据清零
- mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零
- mallco_dev.memrdy[memx]=1; //内存管理初始化OK
- }
- //获取内存使用率
- //memx:所属内存块
- //返回值:使用率(0~100)
- u8 my_mem_perused(u8 memx)
- {
- u32 used=0;
- u32 i;
- for(i=0;i<memtblsize[memx];i++)
- {
- if(mallco_dev.memmap[memx][i])used++;
- }
- return (used*100)/(memtblsize[memx]);
- }
- //内存分配(内部调用)
- //memx:所属内存块
- //size:要分配的内存大小(字节)
- //返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
- u32 my_mem_malloc(u8 memx,u32 size)
- {
- signed long offset=0;
- u32 nmemb; //需要的内存块数
- u32 cmemb=0;//连续空内存块数
- u32 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;//未找到符合分配条件的内存块
- }
- //释放内存(内部调用)
- //memx:所属内存块
- //offset:内存地址偏移
- //返回值:0,释放成功;1,释放失败;
- u8 my_mem_free(u8 memx,u32 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;//偏移超区了.
- }
- //释放内存(外部调用)
- //memx:所属内存块
- //ptr:内存首地址
- void myfree(u8 memx,void *ptr)
- {
- u32 offset;
- if(ptr==NULL)return;//地址为0.
- offset=(u32)ptr-(u32)mallco_dev.membase[memx];
- my_mem_free(memx,offset); //释放内存
- }
- //分配内存(外部调用)
- //memx:所属内存块
- //size:内存大小(字节)
- //返回值:分配到的内存首地址.
- void *mymalloc(u8 memx,u32 size)
- {
- u32 offset;
- offset=my_mem_malloc(memx,size);
- if(offset==0XFFFFFFFF)return NULL;
- else return (void*)((u32)mallco_dev.membase[memx]+offset);
- }
- //重新分配内存(外部调用)
- //memx:所属内存块
- //*ptr:旧内存首地址
- //size:要分配的内存大小(字节)
- //返回值:新分配到的内存首地址.
- void *myrealloc(u8 memx,void *ptr,u32 size)
- {
- u32 offset;
- offset=my_mem_malloc(memx,size);
- if(offset==0XFFFFFFFF)return NULL;
- else
- {
- mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size); //拷贝旧内存内容到新内存
- myfree(memx,ptr); //释放旧内存
- return (void*)((u32)mallco_dev.membase[memx]+offset); //返回新内存首地址
- }
- }
malloc.h
- #ifndef __MALLOC_H
- #define __MALLOC_H
- #include "stm32f4xx.h"
- #ifndef NULL
- #define NULL 0
- #endif
- //定义三个内存池
- #define SRAMIN 0 //内部内存池
- #define SRAMEX 1 //外部内存池
- #define SRAMCCM 2 //CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
- #define SRAMBANK 3 //定义支持的SRAM块数.
- //mem1内存参数设定.mem1完全处于内部SRAM里面.
- #define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
- #define MEM1_MAX_SIZE 100*1024 //最大管理内存 100K
- #define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存管理表大小
- //mem2内存参数设定.mem2的内存池处于外部SRAM里面
- #define MEM2_BLOCK_SIZE 32 //内存块大小为32字节
- #define MEM2_MAX_SIZE 960 *1024 //最大管理内存960K
- #define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //内存管理表大小
- //mem3内存参数设定.mem3处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!)
- #define MEM3_BLOCK_SIZE 32 //内存块大小为32字节
- #define MEM3_MAX_SIZE 60 *1024 //最大管理内存60K
- #define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE //内存管理表大小
- //内存管理控制器
- struct _m_mallco_dev
- {
- void (*init)(u8); //初始化
- u8 (*perused)(u8); //内存使用率
- u8 *membase[SRAMBANK]; //内存池 管理SRAMBANK个区域的内存
- u16 *memmap[SRAMBANK]; //内存管理状态表
- u8 memrdy[SRAMBANK]; //内存管理是否就绪
- };
- extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义
- void mymemset(void *s,u8 c,u32 count); //设置内存
- void mymemcpy(void *des,void *src,u32 n);//复制内存
- void my_mem_init(u8 memx); //内存管理初始化函数(外/内部调用)
- u32 my_mem_malloc(u8 memx,u32 size); //内存分配(内部调用)
- u8 my_mem_free(u8 memx,u32 offset); //内存释放(内部调用)
- u8 my_mem_perused(u8 memx); //获得内存使用率(外/内部调用)
- ////////////////////////////////////////////////////////////////////////////////
- //用户调用函数
- void myfree(u8 memx,void *ptr); //内存释放(外部调用)
- void *mymalloc(u8 memx,u32 size); //内存分配(外部调用)
- void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配内存(外部调用)
- #endif
在头文件《malloc.h》里
首先看到三个宏定义《SRAMIN》《SRAMEX》《SRAMCCM》
这几个的说明,在注释里已经写的很清楚了
探索者开发板还扩充外部(extern)的SRAM,所以宏定义命名为SRAMEX吧?
再加上内部SRAM和CCM,总共就有3个内存池了
下方一点的宏定义《SRAMBANK》,因为有3个内存池,这里就设置为3
紧接着的是三个区域的宏定义,《mem1》《mem2》《mem3》
这三个也就是对应上方的《内部SRAM》《外部SRAM》《CCM》
里面除了内存管理表大小的宏定义不要改以外,其余两个看各位的需求
额外提一点,假设像宏定义《MEM1_MAX_SIZE》一样,设置了100K的内存
这里可是实打实的100K,虽然内存管理表也会占用到内存,但并不在这100K内
它还会额外占 MEM1_MAX_SIZE * 2 个字节的空间
乘2是因为,它是u16类型的
然后来看源文件《malloc.c》
最上方定义了《内存池》《内存管理表》《内存管理参数》
首先是内存池,直接看【__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000)));】
__align是字节对齐,这作法能让CPU更快的访问变量
attribute是修饰,指向的是CCM的首地址0x10000000,下面会放图
这里说一下,因为我还没研究正点原子开发板上,外部扩展的是什么元件,导致我不知道0x68000000这首地址是如何来的
先看下面这图,了解一下CCM的首地址吧,毕竟它是STM32F4的内存,datasheet里面都说的很清楚了
源文件《malloc.c》剩下两个定义《内存管理表》《内存管理参数》,也没什么好说的,设置好这些,等着下方的封装函数来调用吧
而这些封装好的函数,我们只要看四个函数即可
1.《my_mem_init》初始化指定的内存池
2.《mymalloc》申请内存
3.《myfree》释放内存
4.《my_mem_perused》查看内存使用率
本章就先到这里了,下一章要回头处理FatFs文件管理的后续(读取SD卡内的文件)
【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(八)-认识内存管理的更多相关文章
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡
[STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs
[STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍
[STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(五)-文件管理初步介绍
其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡
由于一张SD卡要能读写,涉及到的技术有些多,我打算分以下几篇博客 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍
其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(三)-SD卡的操作流程
其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...
- 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息
其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...
- 第36章 SDIO—SD卡读写测试
第36章 SDIO—SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...
随机推荐
- vue中element-ui table列名lable换行问题 ---亲测
1.lable操作 :label = "'xxxxx \n xxxxx'" // 注意 lable 的: 注:双引号内有单引号,这样才可以解析文本.需要换行的文本处添加 \n 2. ...
- LOTO虚拟示波器软件功能演示之——FIR数字滤波
本文章介绍一下LOTO示波器新出的功能--FIR数字滤波的功能. 在此之前我们先来了解一下带通滤波和带阻滤波.我们都知道每个信号是不同频率不同幅值正弦波的线性叠加,为了方便直接得观察到这种现象,就有了 ...
- CSS学习笔记:定位属性position
目录 一.定位属性简介 二.各属性值的具体功能 1. relative 2. absolute 3. fixed 三.三种定位属性的效果总结 参考资料:https://www.bilibili.com ...
- Jmeter 运行结果的csv文件生成报告
把运行结果保存到本地,下次可以直接用结果生成测试报告. 一.首先保证脚本能正常运行 二.本地创建csv文件,用来保存运行结果 三.察看结果树,选择本地文件(上一步创建好的csv文件),保存运行结果,如 ...
- LeetCode刷题 字符串详解
一.字符串常用的操作 1. string类 1.1 string的定义与初始化 1.1.1 string的定义 1.1.2 string的初始化 1.2 string的赋值与swap.大小操作.关系运 ...
- The 'stream().forEach()' chain can be replaced with 'forEach()' (may change semantics)
对集合操作时,因不同的写法Idea经常会提示:The 'stream().forEach()' chain can be replaced with 'forEach()' (may change s ...
- Python进阶(多线程)
多线程结构 import threading def worker():#子线程要执行的具体逻辑代码函数 print('threading') t1 = threading.current_threa ...
- 理解ASP.NET Core - 错误处理(Handle Errors)
注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或[点击此处查看全文目录](https://www.cnblogs.com/xiaoxiaotank/p/151852 ...
- [atARC064F]Rotated Palindromes
(长度为$n$的序列$a_{i}$,下标范围为$[0,n)$,且用字符串的方式即$a_{[l,r]}$来表示子区间) 定义一个长为$n$的序列$a_{i}$的周期为的$l$满足$l|n$且$\fora ...
- [bzoj5415]归程
首先肯定要预处理出每一个点到1的最短路(别写spfa) 然后以海拔为边权,建一棵kruskal重构树 用倍增找到vi最后一个小于pi的祖先,然后在子树中取min(预处理) 1 #include< ...