【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卡內的文件)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM