datasheet

STM32学习笔记一一内存管理

2019-01-09来源: eefocus关键字:STM32  内存管理

1.简介


内存管理:指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。 内存管理的实现方法有很多种,最终都是要实现两个函数: malloc 和 free。


malloc :函数用于内存申请; 

free: 函数用于内存释放。


1.1 分块式内存管理原理


这里写图片描述

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


内存管理表的项值代表的意义:当该项值为 0 的时候,代表对应的内存块未被占用;当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。


比如:某项值为 10,那么说明包括本项对应的内存块在内,总共分配了 10 个内存块给外部的某个指针。内寸分配方向如图所示,是从顶—>底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存表全部清零,表示没有任何内存块被占用。


1.2 分配原理

当指针 p 调用 malloc 申请内存的时候,先判断 p 要分配的内存块数(m),然后从第 n 项开始,向下查找,直到找到 m 块连续的空内存块(即对应内存管理表项为 0),然后将这 m 个内存管理表项的值都设置为 m(标记被占用),最后,把最后的这个空内存块的地址返回指针 p,完成一次分配。


注:如果当内存不够的时候(找到最后也没找到连续的 m 块空闲内存),则返回 NULL 给 p,表示分配失败。


1.2 释放原理

当 p 申请的内存用完,需要释放的时候,调用 free 函数实现。 free 函数先判断 p 指向的内存地址所对应的内存块,然后找到对应的内存管理表项目,得到 p 所占用的内存块数目 m(内存管理表项目的值就是所分配内存块的数目),将这 m 个内存管理表项目的值都清零,标记释放,完成一次内存释放。


2.软件分析

头文件:


#ifndef __MALLOC_H

#define __MALLOC_H

#include "stm32f10x.h"


#ifndef NULL

#define NULL 0

#endif


//内存参数设定.

#define MEM_BLOCK_SIZE          32                              //内存块大小为32字节

#define MEM_MAX_SIZE            42*1024                         //最大管理内存 42K

#define MEM_ALLOC_TABLE_SIZE    MEM_MAX_SIZE/MEM_BLOCK_SIZE     //内存表大小



//内存管理控制器

struct _m_mallco_dev

{

    void (*init)(void);             //初始化

    u8 (*perused)(void);            //内存使用率

    u8  *membase;                   //内存池 

    u16 *memmap;                    //内存管理状态表

    u8  memrdy;                     //内存管理是否就绪

};

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 mem_init(void);                     //内存管理初始化函数(外/内部调用)

u32 mem_malloc(u32 size);               //内存分配(内部调用)

u8 mem_free(u32 offset);                //内存释放(内部调用)

u8 mem_perused(void);                   //得内存使用率(外/内部调用) 

////////////////////////////////////////////////////////////////////////////////

//用户调用函数

void myfree(void *ptr);                 //内存释放(外部调用)

void *mymalloc(u32 size);               //内存分配(外部调用)

void *myrealloc(void *ptr,u32 size);    //重新分配内存(外部调用)

#endif



参考例程:


#include "malloc.h"     


//内存池(4字节对齐)

__align(4) u8 membase[MEM_MAX_SIZE];            //SRAM内存池

//内存管理表

u16 memmapbase[MEM_ALLOC_TABLE_SIZE];           //SRAM内存池MAP

//内存管理参数       

const u32 memtblsize=MEM_ALLOC_TABLE_SIZE;      //内存表大小

const u32 memblksize=MEM_BLOCK_SIZE;            //内存分块大小

const u32 memsize=MEM_MAX_SIZE;                 //内存总大小



//内存管理控制器

struct _m_mallco_dev mallco_dev=

{

    mem_init,           //内存初始化

    mem_perused,        //内存使用率

    membase,            //内存池

    memmapbase,         //内存管理状态表

    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;  

}      

//内存管理初始化  

void mem_init(void)  

{  

    mymemset(mallco_dev.memmap, 0,memtblsize*2);//内存状态表数据清零  

    mymemset(mallco_dev.membase, 0,memsize);    //内存池所有数据清零  

    mallco_dev.memrdy=1;                        //内存管理初始化OK  

}  

//获取内存使用率

//返回值:使用率(0~100)

u8 mem_perused(void)  

{  

    u32 used=0;  

    u32 i;  


    for(i=0;i

    {  

        if(mallco_dev.memmap[i])

            used++; 

    } 

    return (used*100)/(memtblsize);  

}  

//内存分配(内部调用)

//memx:所属内存块

//size:要分配的内存大小(字节)

//返回值:0XFFFFFFFF,代表错误;其他,内存偏移地址 

u32 mem_malloc(u32 size)  

{  

    signed long offset=0;  

    u16 nmemb;  //需要的内存块数  

    u16 cmemb=0;//连续空内存块数

    u32 i;  


    if(!mallco_dev.memrdy)

        mallco_dev.init();  //未初始化,先执行初始化 

    if(size==0)

        return 0XFFFFFFFF;              //不需要分配

    nmemb=size/memblksize;                      //获取需要分配的连续内存块数

    if(size%memblksize)

        nmemb++;  

    for(offset=memtblsize-1;offset>=0;offset--) //搜索整个内存控制区  

    {     

        if(!mallco_dev.memmap[offset])

            cmemb++;    //连续空内存块数增加

        else 

            cmemb=0;                            //连续内存块清零

        if(cmemb==nmemb)                        //找到了连续nmemb个空内存块

        {

            for(i=0;i

            {  

                mallco_dev.memmap[offset+i]=nmemb;  

            }  

            return (offset*memblksize);         //返回偏移地址  

        }

    }  

    return 0XFFFFFFFF;//未找到符合分配条件的内存块  

}  

//释放内存(内部调用) 

//offset:内存地址偏移

//返回值:0,释放成功;1,释放失败;  

u8 mem_free(u32 offset)  

{  

    int i;  

    if(!mallco_dev.memrdy)//未初始化,先执行初始化

    {

        mallco_dev.init();    

        return 1;//未初始化  

    }  

    if(offset

    {  

        int index=offset/memblksize;        //偏移所在内存块号码  

        int nmemb=mallco_dev.memmap[index]; //内存块数量

        for(i=0;i

        {  

            mallco_dev.memmap[index+i]=0;  

        }

        return 0;  

    }

    else

        return 2;//偏移超区了.  

}  

//释放内存(外部调用) 

//ptr:内存首地址 

void myfree(void *ptr)  

{  

    u32 offset;  

    if(ptr==NULL)

        return;//地址为0.  

    offset=(u32)ptr-(u32)mallco_dev.membase;  

    mem_free(offset);   //释放内存     

}  

//分配内存(外部调用)

//size:内存大小(字节)

//返回值:分配到的内存首地址.

void *mymalloc(u32 size)  

{  

    u32 offset;     


    offset=mem_malloc(size);                       

    if(offset==0XFFFFFFFF)

        return NULL;  

    else 

        return (void*)((u32)mallco_dev.membase+offset);  

}  

//重新分配内存(外部调用)

//*ptr:旧内存首地址

//size:要分配的内存大小(字节)

//返回值:新分配到的内存首地址.

void *myrealloc(void *ptr,u32 size)  

{  

    u32 offset;  


    offset=mem_malloc(size);  

    if(offset==0XFFFFFFFF)

        return NULL;     

    else  

    {                                      

        mymemcpy((void*)((u32)mallco_dev.membase+offset),ptr,size); //拷贝旧内存内容到新内存   

        myfree(ptr);                                                //释放旧内存

        return (void*)((u32)mallco_dev.membase+offset);             //返回新内存首地址

    }  

}


主函数测试:


#include "led.h"

#include "delay.h"

#include "sys.h"

#include "usart.h"

#include "lcd.h"

#include "key.h"

#include "malloc.h" 

#include "usmart.h" 


 int main(void)

 { 

    u8 key;      

    u8 i=0;     

    u8 *p=0;

    u8 *tp=0;

    u8 paddr[18];           //存放P Addr:+p地址的ASCII值 


    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2

    delay_init();            //延时函数初始化    

    uart_init(9600);        //串口初始化为9600

    LED_Init();             //初始化与LED连接的硬件接口

    LCD_Init();             //初始化LCD    

    usmart_dev.init(72);    //初始化USMART 

    KEY_Init();             //按键初始化   

    mem_init();             //初始化内存池


    POINT_COLOR=RED;//设置字体为红色 

    LCD_ShowString(60,50,200,16,16,"Mini STM32");   

    LCD_ShowString(60,70,200,16,16,"MALLOC TEST");  

    LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");

    LCD_ShowString(60,110,200,16,16,"2014/3/12");  

    LCD_ShowString(60,130,200,16,16,"KEY0:Malloc");

    LCD_ShowString(60,150,200,16,16,"KEY1:Write Data");

    LCD_ShowString(60,170,200,16,16,"WK_UP:Free");


    POINT_COLOR=BLUE;//设置字体为蓝色  

    LCD_ShowString(60,190,200,16,16,"SRAM USED:   %");  

    while(1)

    {   

        key=KEY_Scan(0);//不支持连按 

        switch(key)

        {

            case 0:     //没有按键按下    

                break;

            case 1:     //KEY0按下

                p=mymalloc(2048);   //申请2K字节

                if(p!=NULL)

                    sprintf((char*)p,"Memory Malloc Test%03d",i);//向p写入一些内容

                break;

            case 2:     //KEY1按下       

                if(p!=NULL)

                {

                    sprintf((char*)p,"Memory Malloc Test%03d",i);//更新显示内容    

                    LCD_ShowString(60,250,200,16,16,p);          //显示P的内容

                }

                break;

            case 3:     //WK_UP按下     

                myfree(p);  //释放内存

                p=0;        //指向空地址

                break; 

        }

        if(tp!=p)

        {

            tp=p;

            sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);

            LCD_ShowString(60,230,200,16,16,paddr); //显示p的地址

            if(p)LCD_ShowString(60,250,200,16,16,p);//显示P的内容

            else LCD_Fill(60,250,239,266,WHITE);    //p=0,清除显示

        }

        delay_ms(10);   

        i++;

        if((i%20)==0)//DS0闪烁.

        {

            LCD_ShowNum(60+80,190,mem_perused(),3,16);//显示内存使用率

            LED0=!LED0;

        }

    }          

}


参考:


1.原子库函数教程


2.STM32-内存管理



关键字:STM32  内存管理

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/2019/ic-news010942852.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32学习笔记一一UCOSII(1)
下一篇:STM32学习笔记一一DMA传输

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32堆栈设置

1.堆和栈大小 定义大小在startup_stm32f2xx.sStack_Size      EQU     0x00000400                AREA    STACK, NOINIT, READWRITE, ALIGN=3Stack_Mem      
发表于 2019-04-16
STM32堆栈设置

STM32堆和栈(Heap & Stack)的资料理解

源起:在移植cjson的过程中,解析json包的时候发现动态内存分配不足而导致解析失败,为解决这一问题,而深入了解stm32的堆和栈。stm32的存储器结构。Flash,SRAM寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。可访问的存储器空间被分成8个主要块,每个块为512MB。FLASH存储下载的程序。SRAM是存储运行程序中的数据。而SRAM一般分这几个部分:静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率
发表于 2019-04-16
STM32堆和栈(Heap & Stack)的资料理解

STM32定义堆栈地址到ram区顶部

本设置针对stm32f103rbt6的设置,该芯片RAM大小为20kB,故RAM区地址范围为0x20000000—0x20005000,芯片信息如下图所示;第一步:设置.sct文件;;*************************************************************; *** Scatter-Loading Description Filegenerated by uVision ***; *************************************************************LR_IROM1 0x08000000 0x00020000  
发表于 2019-04-16
STM32定义堆栈地址到ram区顶部

STM32之程序如何防止堆栈溢出

近日为某个项目写了个草稿程序,即非正式程序,后来发现老是进入hardfaulthandler,原来是堆栈溢出,后仔细查看发现函数调用纵深太深,最多的时候可保持7个函数在堆栈中调用。因此有心得如下:一、函数调用不要纵深太深,即以下模式:main(){   fun1();}fun1(){  fun2();}fun2(){   fun3();}fun3(){  fun4();}fun4(){  fun5();}fun5(){  fun6();}fun6(){   fun7();}这样子main函数要调用fun1函数完成某个功能,则要一直调到
发表于 2019-04-16

stm32之堆栈

stm32中的堆栈设置keil编译完成时存储情况当编译成功时,会出现: BUILD://Program Size: Code=340 RO-data=252 RW-data=0 ZI-data=1632Code:程序代码部分RO-data: 程序定义的常量const tempRW-data:已初始化的全局变量ZI-data:未初始化的全局变量片中的:flash=Code+RO-data+RW-dataRAM=RW-data+ZI-data通过上面的BUILD可以看出,这个程序已经用了1600多的RAM,为什么会出用到这么多的RAM呢?在startup_stm32f10x_md.s文件中存在:St
发表于 2019-04-16

说说STM32的堆栈与内存

1.概念这里所说的堆栈,是针对单片机所说的“堆”与“栈”,指的是内存中一片特殊用途的区域。而不是数据结构中的堆栈(虽然其实规则一样)。这里所说的内存,是指RAM,RAM包括SRAM,DRAM等。而不是什么手机内存卡之类。这里所说的flash,指的是用作为ROM的存储器,保存代码与常量数据。而不是动画制作。。。栈的生长方向:指的是入栈方向,从高地址向低地址生长叫做向下生长,或逆向生长;反过来就叫向上生长,或正向生长。STM32的栈是向下生长。2.内存中的堆栈安排确切地说,是keil mdk根据STM32的特性,对stm32的RAM甚至flash进行部署。编译工程后,在生成的.map文件里可以看到具体的安排。双击工程界面的工程根目录
发表于 2019-04-16
说说STM32的堆栈与内存

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved