单片机内存管理

这篇具有很好参考价值的文章主要介绍了单片机内存管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单片机内存管理

1、随机存储器

RAM是随机存储器,读写速度快,但掉电以后数据会丢失。它分为SRAM(静态RAM)和DRAM(动态RAM)。SRAM无需刷新就可以保存数据;DRAM需要不断刷新才可以保存数据。在CPU内部的RAM,就叫内部RAM,在CPU外部的RAM,就叫外部RAM。单片机应用中,一般很少扩展外部RAM。

2、单片机内存由哪几部分构成

单片机内存位于RAM中,它被分成四个区:静态存储区、栈区、堆区和未用区

静态存储区用来存放全局变量和static型变量,它在程序编译的时候就已经被分配好了。

用来保护现场和恢复现场的栈很多,主要有:函数栈和任务栈。在使用操作系统时,我们会申请一块数组用作任务栈,任务栈位于静态存储区,其实函数栈也位于静态存储区,只是我们硬要把他们分开叫而已。严格来讲,内存只有堆区静态存储区两种

栈区和堆区的大小由软件工程师设置。例如在STM32F103的启动文件中,有两个宏定义:Stack_Size和Heap_Size,其中Stack_Size用来设置栈区的大小,Heap_Size用来设置

堆区的大小,它们在startup_stm32f10x_hd.s文件中。格式如下:

Stack_Size   EQU     0x00000400

Heap_Size   EQU     0x00000200

注意:如果没有使用标准库的malloc,这里Heap_Size可以设置为0。例如:在使用其他操作系统或自定义的malloc.c文件时,都需要定义一个数组用作堆区,用作内存申请和释放。

3、堆区管理

下面主要介绍我的malloc.c文件,了解是如何进行堆管理。在单片机应用中,一般很少扩展外部RAM,通常会根据实际情况,选择合适的CPU来满足设计需求。堆的生长方向,本程序是向下的。

//定义两个内存池

#define InternalRAM          0    //内部堆区内存池

#define ExternalRAM       1    //外部堆区内存池

#define RAMBankNumber   2    //堆区种类为2

/内部堆定义开始/

#define InternalRAM_BlockSize    32

//定义内部堆区最小的数据块为32字节对齐

#define InternalRAM_TotalNumberOfBytes  30*1024  //定义内部堆区的大小为30K

#define InternalRAM_NumberOfBlock   InternalRAM_TotalNumberOfBytes/InternalRAM_BlockSize

//定义内部堆区中块的总数量为1280

__align(32) u8 InternalRAM_Array[InternalRAM_TotalNumberOfBytes];

//定义内部堆区内存池首地址为InternalRAM_Array,共分配InternalRAM_TotalNumberOfBytes个字节空间,并指定按照32位对齐。

u16 InternalRAM_MemoryStatusTableArray[InternalRAM_NumberOfBlock ];

//内部堆区的内存状态表

//InternalRAM_MemoryStatusTableArray[i]=0表示块i是空闲的,可以使用;

/内部堆定义结束/

/外部堆定义开始/

#define ExternalRAM_BlockSize  InternalRAM_BlockSize

//定义外部堆区最小的数据块为32字节对齐,保证内部堆和外部堆的大小相同

#define ExternalRAM_TotalNumberOfBytes  960 *1024 //定义外部堆区的大小为1M

#define ExternalRAM_NumberOfBlock   ExternalRAM_TotalNumberOfBytes/ExternalRAM_BlockSize

//定义外部SRAM中块的总数量为30720

__align(32) u8 ExternalRAM_Array[ExternalRAM_TotalNumberOfBytes]  __attribute__((at(0X68000000)));

//定义外部堆区的内存池首地址为ExternalRAM_Array,共分配ExternalRAM_TotalNumberOfBytes个字节空间,并指定按照32位对齐,进行数据访问

//定义外部内存内存池的物理首地址为0X68000000

u16 ExternalRAM_MemoryStatusTableArray[ExternalRAM_NumberOfBlock] __attribute__((at(0X68000000+ ExternalRAM_TotalNumberOfBytes)));

//外部堆区的内存状态表,其首地址为0X68000000+ ExternalRAM_TotalNumberOfBytes

// ExternalRAM_MemoryStatusTableArray[i]=0表示块i是空闲的,可以使用;

/外部堆定义结束/

struct  _m_mallco_dev

{//内存管理控制器

  void (*init)(u8);             //初始化函数

  u8   (*perused)(u8);        //内存使用率统计函数

  u8   *membase[RAMBankNumber];

//指针数组用来存放内存池数组首地址,内存池数量为RAMBankNumber

  u16  *memmap[RAMBankNumber];

//指针数组用来存放内存管理状态表, 内存管理状态表数量为RAMBankNumber

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

};

//extern struct  _m_mallco_dev   mallco_dev;

//内存管理参数

const u32 NumberOfMyBlock[RAMBankNumber]={ InternalRAM_NumberOfBlock, ExternalRAM_NumberOfBlock };

//记录内部堆和外部堆中分别含有块的总数量

const u32 MyBlockSize[RAMBankNumber]={ InternalRAM_BlockSize, ExternalRAM_BlockSize };

//记录内部堆和外部堆中的块的大小

const u32 MyTotalNumberOfBytes[RAMBankNumber]={ InternalRAM_TotalNumberOfBytes, ExternalRAM_TotalNumberOfBytes };

//记录内部堆和外部堆的内存池大小

//函数功能:src为首地址的存储块,复制前n个字节到des为首地址的存储块中

//*des:目的地址

//*src:源地址

//n:需要复制的内存长度(字节为单位)

void MyMemoryCopy(void *des,void *src,u32 n)

{

  u8 *xdes=des;

  u8 *xsrc=src;

  while(n--) *xdes++=*xsrc++;

}

//函数功能:s为首地址的存储块的前count个字节全部设置为c的值

//*s:内存首地址

//c :要设置的值

//count:需要设置的内存大小(字节为单位)

void MyMemorySet(void *s,u8 c,u32 count)

{

  u8 *xs = s;

  while(count--) *xs++=c;

}

//函数功能: 清除内存状态表和堆区的内存池

//memx= InternalRAM,初始化内部堆区;memx= ExternalRAM,初始化外部堆区

void MyMemoryInit(u8 memx)

{

  MyMemorySet( mallco_dev.memmap[memx], 0, NumberOfMyBlock[memx]*2 );

         //将堆区的内存状态表数组清零,NumberOfMyBlock[]表示块的总数量

//InternalRAM_MemoryStatusTableArray[]ExternalRAM_MemoryStatusTableArray[]存储的是双字节

//NumberOfMyBlock[memx]也就是双字节,所以这里乘以2;

  MyMemorySet( mallco_dev.membase[memx], 0, MyTotalNumberOfBytes[memx] );

         //将堆区的内存池所有数据清零

// InternalRAM_Array[]ExternalRAM_Array[]存储的是单字节,

//mallco_dev.membase[memx]也就是单字节存储区

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

}

//函数功能:

//memx= InternalRAM,计算内部堆区的使用率;

//memx= ExternalRAM,计算外部堆区的使用率

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

u8 Get_MyMemoryUsed(u8 memx)

{

  u32 used=0;

  u32 i;

  for(i=0;i<NumberOfMyBlock[memx];i++)

  {

if( mallco_dev.memmap[memx][i] ) used++;

//计算堆区内存池中空闲块的数量,

//堆区内存池中块的总数量保存在NumberOfMyBlock[memx]

//mallco_dev.memmap[0][y]=0表示内部堆的块y是空闲的

//mallco_dev.memmap[1][y]=0表示外部堆的块y是空闲的

  }

  return (used*100)/(NumberOfMyBlock[memx]);

}

//内存管理控制器,创建结构变量时,就开始初始化一次

struct _m_mallco_dev   mallco_dev=

{

         MyMemoryInit,       //调用MyMemoryInit(),内存初始化

         Get_MyMemoryUsed,   //调用Get_MyMemoryUsed(),统计内存使用率

         InternalRAM_Array,  //传入InternalRAM_Array[]数组首地址

ExternalRAM_Array,  //传入ExternalRAM_Array[]数组首地址

         InternalRAM_MemoryStatusTableArray,

//传入InternalRAM_MemoryStatusTableArray[]数组首地址

ExternalRAM_MemoryStatusTableArray,    

//传入ExternalRAM_MemoryStatusTableArray[]数组首地址

         0,                //内部堆区管理未就绪

0                //外部堆区管理未就绪

};

//函数功能:

//memx= InternalRAM,从内部堆区内存池中分配size个字节;

//memx= ExternalRAM,从外部堆区内存池中分配size个字节;

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

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

u32 MyMemoryMalloc(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/MyBlockSize[memx];

//计算需要多少个整块, MyBlockSize[memx]为块的大小

  if( size%MyBlockSize[memx] ) nmemb++;

//剩余的字节空间不满一个块的,则按一个整块操作

///MyBlockSize[memx]为块的大小,NumberOfMyBlock[memx]为块的总数量

  for( offset=NumberOfMyBlock[memx]-1;offset>=0;offset-- )//搜索内存池,offset为块的号码

  {//堆区按照向下生成方式分配空间, NumberOfMyBlock[]表示块的总数量

if( !mallco_dev.memmap[memx][offset] ) cmemb++;//若块空间空闲,cmemb1

         else cmemb=0; //若连续空闲块数量小于nmemb,则cmemb=0

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

    {

      for(i=0;i<nmemb;i++) //标注内存块非空

      {

        mallco_dev.memmap[memx][offset+i]=nmemb;

//”nmemb个连续空闲块对应的内存状态表设置为nmemb

//连续的内存状态表的值相同,表示为同批次分配

      }

      return ( offset*MyBlockSize[memx] );//返回分配到的内存池偏移地址

    }

  }

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

}

//函数功能:释放内存(内部调用)

//memx= InternalRAM,从内部堆区内存池中释放偏移地址为offset的数据块;

//memx= ExternalRAM,从外部堆区内存池中释放偏移地址为offset的数据块;

//offset:内存地址偏移

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

u8 MyMemoryFree(u8 memx,u32 offset)

{

  int i;

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

  {

    mallco_dev.init(memx);//初始化内存池

    return 1;//未初始化

  }

  if(offset< MyTotalNumberOfBytes[memx])//偏移在内存池内

  {

    int index=offset/MyBlockSize[memx]; //计算偏移地址offset在内存块中的号码

    int nmemb=mallco_dev.memmap[memx][index]; //读取要释放的块数量

    for(i=0;i<nmemb;i++)

    {

      mallco_dev.memmap[memx][index+i]=0;

//”nmemb个连续空闲块对应的内存状态表设置为0

    }

    return 0;//释放内存成功

  }

  else return 2;//偏移超区了

}

//函数功能:分配内存(外部调用)

//memx= InternalRAM,从内部堆区内存池中分配size个字节;

//memx= ExternalRAM,从外部堆区内存池中分配size个字节;

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

void * MyMalloc(u8 memx,u32 size)

{

  u32 offset;

  offset= MyMemoryMalloc(memx,size);//读分配size个字节空间的偏移地址

  if(offset==0XFFFFFFFF)//没有分配到数据块

 return NULL;

  else //分配到数据

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

//返回分配到的数据块首地址

}

//函数功能:释放内存(外部调用)

//memx= InternalRAM,从内部堆区内存池中释放首移地址为ptr的字节空间;

//memx= ExternalRAM,从外部堆区内存池中释放首移地址为ptr的字节空间;

//释放的块数为在内存状态表中

//ptr:内存首地址

void MyFree(u8 memx,void *ptr)

{

  u32 offset;

  if(ptr==NULL)return;//地址为0

  offset=(u32)ptr-(u32)mallco_dev.membase[memx];

  //计算偏移地址

  MyMemoryFree(memx,offset);//释放内存

}

//函数功能:

//ptr为首地址的前size个字节拷贝到新分配的内存中,再释放ptr为首地址的内存

//memx= InternalRAM,从内部堆区内存池中分配size个字节;

//memx= ExternalRAM,从外部堆区内存池中分配size个字节;

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

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

void *MyMalloc_CopyOldData_And_FreeOldDataBlock(u8 memx,void *ptr,u32 size)

{

  u32 offset;

  offset=MyMemoryMalloc(memx,size);//读分配size个字节空间的偏移地址

  if(offset==0XFFFFFFFF)

return NULL;

  else

  {

MyMemoryCopy( (void*)((u32)mallco_dev.membase[memx]+offset),ptr,size );

//拷贝旧内存内容到新内存

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

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

  }

}

//函数功能:动态分配内存,

//注意:为了和FreeRTOS兼容定义为pvPortMalloc(u32 size)

/*

在使用FreeRTOS系统时,最好用它自带的内存管理heap_4.c,系统所有总的堆大小,体现在ZI-DATA

FreeRTOSConfig.h中,有个宏定义:#define configTOTAL_HEAP_SIZE   ((size_t)(33*1024))

*/

void *pvPortMalloc(u32 size)                     

{

         return (void*)MyMalloc(InternalRAM,size);

}

//函数功能:释放内部RAM的内存

//注意:为了和FreeRTOS兼容定义为vPortFree()

//在使用FreeRTOS系统时,最好用它自带的内存管理heap_4.c

/*

在使用FreeRTOS系统时,最好用它自带的内存管理heap_4.c,系统所有总的堆大小,体现在ZI-DATA

FreeRTOSConfig.h中,有个宏定义:#define configTOTAL_HEAP_SIZE   ((size_t)(33*1024))

*/

void vPortFree(void* mf)

{

         myfree(InternalRAM,mf);

}

u8 Myused;

int main(void)

{

         u8 *p;

         MyMemoryInit(InternalRAM); //初始化内部内存池

         Myused=Get_MyMemoryUsed(InternalRAM);//读内存使用率

         p=pvPortMalloc(15*1024);

         Myused=Get_MyMemoryUsed(InternalRAM);//读内存使用率

         vPortFree(p);

         Myused=Get_MyMemoryUsed(InternalRAM);//读内存使用率

         while(1)

         {

         }

}

4、函数栈的管理

函数栈也中断栈,或叫硬件栈,书本上没有严格的定义。栈区用来存放局部变量和一些寄存器数据。局部变量在函数内部,其存储空间位于栈中。当进入函数时,会对根据局部变量需求,在栈上申请一段内存空间,供局部变量使用。当局部变量生命周期结束后,在栈上释放。在C语言程序中,是由编译器系统完成申请和释放的。CPU栈的增长方向,通常是向下的。

在C语言程序中,“函数A”调用“函数B”时,需要将一些寄存器数据和“函数A”中的局部变量压入到“指定的RAM”中入栈代码是由编译器系统完成的,这叫现场保护;接着再执行“函数B”;“函数B”被执行完毕后再回到“函数A”,此时需要将从“指定的RAM”中把“以前压入栈中的数据”返回给寄存器和“函数A”中的局部变量出栈代码是由编译器系统完成的,叫恢复现场。这个“指定的RAM”,叫“函数栈”或“中断栈”。在C语言中,入栈和出栈是由编译器系统自动完成的,但在汇编语言中,入栈和出栈代码就需要人工完成。见下图:

单片机内存管理,产品研发,单片机,stm32,嵌入式硬件,Malloc,内存管理

单片机内存管理,产品研发,单片机,stm32,嵌入式硬件,Malloc,内存管理

5、任务栈的管理

任务栈需要结合具体的操作系统,说明会更有效果。文章来源地址https://www.toymoban.com/news/detail-637164.html

到了这里,关于单片机内存管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32单片机开发-01 STM32介绍

    通过野火开发板学习单片机 从内核上分有Cortex-M0、M3、M4 和M7 F1 代表了基础型,基于Cortex-M3 内核,主频为72MHZ F4 代表了高性能,基于Cortex-M4 内核,主频180M。 数据手册:用于芯片选型和设计原理图 参考手册:用于编程时查阅 Icode总线 – 该总线讲M3内核的指令总线与闪存指令

    2024年01月21日
    浏览(63)
  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库,STM32F103

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月07日
    浏览(58)
  • 【STM32】STM32单片机结构及部件原理

    STM32是目前比较常见并且多功能的单片机,要想学习STM32,首先要去了解它的基本构成部分以及各部分的原理。 单片机型号:正点原子STM32F103ZET6 目录 STM32内部结构总览图: 2.内部结构解析         1.内核 :STM32F103ZET6采用的是 ARM Cortex-M3 处理器,内核可以理解为单片机 处

    2023年04月08日
    浏览(51)
  • STM32单片机学习3--STM32控制键盘

    单片机型号:STM32F103C8T6 开发环境:Keil5 4种输入模式 上拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为1,高电平(与Vdd相连的为上拉电阻); 下拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为0,低电平(与Vss相连的为下拉电

    2024年02月10日
    浏览(60)
  • 【单片机】STM32单片机,定时器的输入捕获,基于捕获的频率计,STM32F103

    下面的定时器都具有输入捕获能力: 查看另一篇文章:https://qq742971636.blog.csdn.net/article/details/131471539 外部计数频率计的缺点:需要两个定时器配合,最高能测量的频率是否有限制我没具体尝试。 基于捕获的频率计的缺点:最高能测量的频率有限制。 TIM3_CH1 PWM PA6 10KHZ。 输入

    2024年02月14日
    浏览(58)
  • stm32系列单片机介绍

        stm32是基于ARM® Cortex®  内核的 32位微控制器和微处理器。常见的内核有:     Cortex-M0,代表型号STM32F0、STM32L0;     Cortex-M0+,代表型号STM32C0(23年新推出,主要针对低成本);     Cortex-M3,代表型号STM32F1、STM32F2、STM32L1;     Cortex-M4,代表型号STM32F3(混合信号)、

    2024年02月06日
    浏览(56)
  • 单片机简介(STM32介绍)

        单片机是 单片微型计算机 的简称,Mcu是Microcontroller的简称,也就是嵌入式微控制器。采用集成电路技术将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、定时器/计时器、多种I/O口和中断系统等功能集成到一块硅片上。可以说单片机就是一个小而完善

    2024年02月16日
    浏览(41)
  • 单片机stm32智能鱼缸

    随着我国经济的快速发展而给人们带来了富足的生活,也有越来越多的人们开始养鱼,通过养各种鱼类来美化居住环境和缓解压力。但是在鱼类饲养过程中,常常由于鱼类对水质、水位及光照强度有着很高的要求,而人们也由于工作的方面而无法贴心的照料,因此经常因为水

    2024年02月22日
    浏览(45)
  • STM32-01-认识单片机

    单片机是什么? 单片机:Single-Chip Microcomputer,单片微型计算机,是一种集成电路芯片。 下面是电脑与单片机的对应关系: 单片机的特点? 体积小:5mmx5mm 功耗低:mA级 集成度高:IO、TIM、AD、DA 使用方便:C、Debug 拓展灵活:IIC、SPI、FSMC 单片机有什么用? 仪器仪表:电源、

    2024年02月03日
    浏览(47)
  • STM32单片机学习4--STM32控制八段码

    数码管:实际上是多个LED按照一定顺序排列,并加上遮罩所构成的元件。 八段码一般会引出9个引脚,其中7个引脚显示数字(或某些字母),1个显示小数点,1个作为片选端。 根据连接方式的不同,数码管分为 共阳 和 共阴 。 共阳在这端输出低电平时点亮,高电平时会熄灭

    2024年01月23日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包