【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

  1. #include "malloc.h"
  2.  
  3. //内存池(32字节对齐)
  4. __align(32) u8 mem1base[MEM1_MAX_SIZE]; //内部SRAM内存池
  5. __align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000))); //外部SRAM内存池,attribute修饰,指向0x68000000首地址
  6. __align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000))); //内部CCM内存池,attribute修饰,指向0x10000000首地址
  7. //内存管理表
  8. u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //内部SRAM内存池MAP
  9. u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE))); //外部SRAM内存池MAP
  10. u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000+MEM3_MAX_SIZE))); //内部CCM内存池MAP
  11. //内存管理参数
  12. const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //内存表大小
  13. const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,MEM3_BLOCK_SIZE}; //内存分块大小
  14. const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,MEM3_MAX_SIZE}; //内存总大小
  15.  
  16. //内存管理控制器
  17. struct _m_mallco_dev mallco_dev=
  18. {
  19. my_mem_init, //内存初始化
  20. my_mem_perused, //内存使用率
  21. mem1base,mem2base,mem3base, //内存池
  22. mem1mapbase,mem2mapbase,mem3mapbase,//内存管理状态表
  23. 0,0,0, //内存管理未就绪
  24. };
  25.  
  26. //复制内存
  27. //*des:目的地址
  28. //*src:源地址
  29. //n:需要复制的内存长度(字节为单位)
  30. void mymemcpy(void *des,void *src,u32 n)
  31. {
  32. u8 *xdes=des;
  33. u8 *xsrc=src;
  34. while(n--)*xdes++=*xsrc++;
  35. }
  36. //设置内存
  37. //*s:内存首地址
  38. //c :要设置的值
  39. //count:需要设置的内存大小(字节为单位)
  40. void mymemset(void *s,u8 c,u32 count)
  41. {
  42. u8 *xs = s;
  43. while(count--)*xs++=c;
  44. }
  45. //内存管理初始化
  46. //memx:所属内存块
  47. void my_mem_init(u8 memx)
  48. {
  49. mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//内存状态表数据清零
  50. mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //内存池所有数据清零
  51. mallco_dev.memrdy[memx]=1; //内存管理初始化OK
  52. }
  53. //获取内存使用率
  54. //memx:所属内存块
  55. //返回值:使用率(0~100)
  56. u8 my_mem_perused(u8 memx)
  57. {
  58. u32 used=0;
  59. u32 i;
  60. for(i=0;i<memtblsize[memx];i++)
  61. {
  62. if(mallco_dev.memmap[memx][i])used++;
  63. }
  64. return (used*100)/(memtblsize[memx]);
  65. }
  66. //内存分配(内部调用)
  67. //memx:所属内存块
  68. //size:要分配的内存大小(字节)
  69. //返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址
  70. u32 my_mem_malloc(u8 memx,u32 size)
  71. {
  72. signed long offset=0;
  73. u32 nmemb; //需要的内存块数
  74. u32 cmemb=0;//连续空内存块数
  75. u32 i;
  76. if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先执行初始化
  77. if(size==0)return 0XFFFFFFFF;//不需要分配
  78. nmemb=size/memblksize[memx]; //获取需要分配的连续内存块数
  79. if(size%memblksize[memx])nmemb++;
  80. for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整个内存控制区
  81. {
  82. if(!mallco_dev.memmap[memx][offset])cmemb++;//连续空内存块数增加
  83. else cmemb=0; //连续内存块清零
  84. if(cmemb==nmemb) //找到了连续nmemb个空内存块
  85. {
  86. for(i=0;i<nmemb;i++) //标注内存块非空
  87. {
  88. mallco_dev.memmap[memx][offset+i]=nmemb;
  89. }
  90. return (offset*memblksize[memx]);//返回偏移地址
  91. }
  92. }
  93. return 0XFFFFFFFF;//未找到符合分配条件的内存块
  94. }
  95. //释放内存(内部调用)
  96. //memx:所属内存块
  97. //offset:内存地址偏移
  98. //返回值:0,释放成功;1,释放失败;
  99. u8 my_mem_free(u8 memx,u32 offset)
  100. {
  101. int i;
  102. if(!mallco_dev.memrdy[memx])//未初始化,先执行初始化
  103. {
  104. mallco_dev.init(memx);
  105. return 1;//未初始化
  106. }
  107. if(offset<memsize[memx])//偏移在内存池内.
  108. {
  109. int index=offset/memblksize[memx]; //偏移所在内存块号码
  110. int nmemb=mallco_dev.memmap[memx][index]; //内存块数量
  111. for(i=0;i<nmemb;i++) //内存块清零
  112. {
  113. mallco_dev.memmap[memx][index+i]=0;
  114. }
  115. return 0;
  116. }else return 2;//偏移超区了.
  117. }
  118. //释放内存(外部调用)
  119. //memx:所属内存块
  120. //ptr:内存首地址
  121. void myfree(u8 memx,void *ptr)
  122. {
  123. u32 offset;
  124. if(ptr==NULL)return;//地址为0.
  125. offset=(u32)ptr-(u32)mallco_dev.membase[memx];
  126. my_mem_free(memx,offset); //释放内存
  127. }
  128. //分配内存(外部调用)
  129. //memx:所属内存块
  130. //size:内存大小(字节)
  131. //返回值:分配到的内存首地址.
  132. void *mymalloc(u8 memx,u32 size)
  133. {
  134. u32 offset;
  135. offset=my_mem_malloc(memx,size);
  136. if(offset==0XFFFFFFFF)return NULL;
  137. else return (void*)((u32)mallco_dev.membase[memx]+offset);
  138. }
  139. //重新分配内存(外部调用)
  140. //memx:所属内存块
  141. //*ptr:旧内存首地址
  142. //size:要分配的内存大小(字节)
  143. //返回值:新分配到的内存首地址.
  144. void *myrealloc(u8 memx,void *ptr,u32 size)
  145. {
  146. u32 offset;
  147. offset=my_mem_malloc(memx,size);
  148. if(offset==0XFFFFFFFF)return NULL;
  149. else
  150. {
  151. mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size); //拷贝旧内存内容到新内存
  152. myfree(memx,ptr); //释放旧内存
  153. return (void*)((u32)mallco_dev.membase[memx]+offset); //返回新内存首地址
  154. }
  155. }

  

malloc.h

  1. #ifndef __MALLOC_H
  2. #define __MALLOC_H
  3. #include "stm32f4xx.h"
  4.  
  5. #ifndef NULL
  6. #define NULL 0
  7. #endif
  8.  
  9. //定义三个内存池
  10. #define SRAMIN 0 //内部内存池
  11. #define SRAMEX 1 //外部内存池
  12. #define SRAMCCM 2 //CCM内存池(此部分SRAM仅仅CPU可以访问!!!)
  13.  
  14. #define SRAMBANK 3 //定义支持的SRAM块数.
  15.  
  16. //mem1内存参数设定.mem1完全处于内部SRAM里面.
  17. #define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
  18. #define MEM1_MAX_SIZE 100*1024 //最大管理内存 100K
  19. #define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //内存管理表大小
  20.  
  21. //mem2内存参数设定.mem2的内存池处于外部SRAM里面
  22. #define MEM2_BLOCK_SIZE 32 //内存块大小为32字节
  23. #define MEM2_MAX_SIZE 960 *1024 //最大管理内存960K
  24. #define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE //内存管理表大小
  25.  
  26. //mem3内存参数设定.mem3处于CCM,用于管理CCM(特别注意,这部分SRAM,仅CPU可以访问!!)
  27. #define MEM3_BLOCK_SIZE 32 //内存块大小为32字节
  28. #define MEM3_MAX_SIZE 60 *1024 //最大管理内存60K
  29. #define MEM3_ALLOC_TABLE_SIZE MEM3_MAX_SIZE/MEM3_BLOCK_SIZE //内存管理表大小
  30.  
  31. //内存管理控制器
  32. struct _m_mallco_dev
  33. {
  34. void (*init)(u8); //初始化
  35. u8 (*perused)(u8); //内存使用率
  36. u8 *membase[SRAMBANK]; //内存池 管理SRAMBANK个区域的内存
  37. u16 *memmap[SRAMBANK]; //内存管理状态表
  38. u8 memrdy[SRAMBANK]; //内存管理是否就绪
  39. };
  40. extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定义
  41.  
  42. void mymemset(void *s,u8 c,u32 count); //设置内存
  43. void mymemcpy(void *des,void *src,u32 n);//复制内存
  44. void my_mem_init(u8 memx); //内存管理初始化函数(外/内部调用)
  45. u32 my_mem_malloc(u8 memx,u32 size); //内存分配(内部调用)
  46. u8 my_mem_free(u8 memx,u32 offset); //内存释放(内部调用)
  47. u8 my_mem_perused(u8 memx); //获得内存使用率(外/内部调用)
  48. ////////////////////////////////////////////////////////////////////////////////
  49. //用户调用函数
  50. void myfree(u8 memx,void *ptr); //内存释放(外部调用)
  51. void *mymalloc(u8 memx,u32 size); //内存分配(外部调用)
  52. void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配内存(外部调用)
  53. #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(八)-认识内存管理的更多相关文章

  1. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(终)-配合内存管理来遍历SD卡

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  2. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(七)-准备移植FatFs

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  3. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(六)-FatFs使用的思路介绍

    [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 [STM3 ...

  4. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(五)-文件管理初步介绍

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  5. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡

    由于一张SD卡要能读写,涉及到的技术有些多,我打算分以下几篇博客 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含 ...

  6. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  7. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(三)-SD卡的操作流程

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  8. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(四)-介绍库函数,获取一些SD卡的信息

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  9. 第36章 SDIO—SD卡读写测试

    第36章     SDIO—SD卡读写测试 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...

随机推荐

  1. vue中element-ui table列名lable换行问题 ---亲测

    1.lable操作 :label = "'xxxxx \n xxxxx'" // 注意 lable 的: 注:双引号内有单引号,这样才可以解析文本.需要换行的文本处添加 \n 2. ...

  2. LOTO虚拟示波器软件功能演示之——FIR数字滤波

    本文章介绍一下LOTO示波器新出的功能--FIR数字滤波的功能. 在此之前我们先来了解一下带通滤波和带阻滤波.我们都知道每个信号是不同频率不同幅值正弦波的线性叠加,为了方便直接得观察到这种现象,就有了 ...

  3. CSS学习笔记:定位属性position

    目录 一.定位属性简介 二.各属性值的具体功能 1. relative 2. absolute 3. fixed 三.三种定位属性的效果总结 参考资料:https://www.bilibili.com ...

  4. Jmeter 运行结果的csv文件生成报告

    把运行结果保存到本地,下次可以直接用结果生成测试报告. 一.首先保证脚本能正常运行 二.本地创建csv文件,用来保存运行结果 三.察看结果树,选择本地文件(上一步创建好的csv文件),保存运行结果,如 ...

  5. LeetCode刷题 字符串详解

    一.字符串常用的操作 1. string类 1.1 string的定义与初始化 1.1.1 string的定义 1.1.2 string的初始化 1.2 string的赋值与swap.大小操作.关系运 ...

  6. 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 ...

  7. Python进阶(多线程)

    多线程结构 import threading def worker():#子线程要执行的具体逻辑代码函数 print('threading') t1 = threading.current_threa ...

  8. 理解ASP.NET Core - 错误处理(Handle Errors)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或[点击此处查看全文目录](https://www.cnblogs.com/xiaoxiaotank/p/151852 ...

  9. [atARC064F]Rotated Palindromes

    (长度为$n$的序列$a_{i}$,下标范围为$[0,n)$,且用字符串的方式即$a_{[l,r]}$来表示子区间) 定义一个长为$n$的序列$a_{i}$的周期为的$l$满足$l|n$且$\fora ...

  10. [bzoj5415]归程

    首先肯定要预处理出每一个点到1的最短路(别写spfa) 然后以海拔为边权,建一棵kruskal重构树 用倍增找到vi最后一个小于pi的祖先,然后在子树中取min(预处理) 1 #include< ...