• 【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吧?

    再加上内部SRAMCCM,总共就有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卡内的文件)

  • 相关阅读:
    Oracle建立表空间和用户
    fscanf()函数具体解释
    三层架构(我的理解及具体分析)
    ListView嵌套ListView优化
    Android xml 解析
    玩转Web之servlet(三)---一张图看懂B/S架构
    jquery.scrollTo-min.js
    C#中MessageBox使用方法大全(附效果图)
    hdu 1882 Strange Billboard(位运算+枚举)
    MySQL 通配符学习小结
  • 原文地址:https://www.cnblogs.com/PureHeart/p/14183355.html
Copyright © 2020-2023  润新知