STM32_内存管理

发布者:烟雨江湖最新更新时间:2022-02-25 来源: eefocus关键字:STM32  内存管理 手机看文章 扫描二维码
随时随地手机看文章

1:内存管理简介

内存管理,是指软件运行时动态的对MCU内存资源的分配和使用的技术。最主要的目的是:如何高效,快速的分配,并且在适当的时候释放和回收内存资源。内存管理的实现方法有很多种。


2:分块式内存管理

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

当该项值为0的时候,代表对应的内存块未被占用,当该项值非零的时候,代表该项对应的内存块已经被占用,其数值则代表被连续占用的内存块数。比如某项值为10,那么说明包括本项对应的内存块在内,总共分配了10个内存块给外部的某个指针。


 内存分配方向:是从顶-》底的分配方向。即首先从最末端开始找空内存。当内存管理刚初始化的时候,内存管理表全部清零,表示没有任何内存块被占用。


3:分块式内存管理,分配,释放原理

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


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


4:代码实现

eg:正点原子STM32F407探索者开发板


探索者开发板外扩了SRAM,同时片内有SRAM和CCM之分,所以有3片内存区域,MEM1表示内部SRAM内存池(128K字节),MEM2表示外扩内存池(1024K字节)。MEM3表示内部CCM内存池(64K字节),内部SRAM内存池加CCM内存池一共有192KB的SRAM,注意下,64KBCCM(内部耦合存储器)数据RAM不属于总线矩阵,只能通过CPU对其进行访问

//内存管理控制器

struct _m_mallco_dev

{

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

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

u8 *membase[SRAMBANK]; //内存池 管理SRAMBANK个区域的内存

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

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

};

 

//内存管理控制器

struct _m_mallco_dev mallco_dev=

{

my_mem_init, //内存初始化

my_mem_perused, //内存使用率

mem1base,mem2base,mem3base, //内存池

mem1mapbase,mem2mapbase,mem3mapbase,//内存管理状态表

0,0,0,  //内存管理未就绪

};

 

//内存管理初始化  

//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  

}  

 

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

{  

    u8 *xs = s;  

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

}

这样码出来看着清楚些,mymemset这个函数有3个参数,第一个是你要申请内存的地址,第二个是要设置的值,第三个是申请内存个数,回到my_mem_initt这个函数中,调用mymemset,传入地址和个数,看下面就明白了


//定义三个内存池

#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 //内存表大小


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

__align(32) u8 mem1base[MEM1_MAX_SIZE];     //内部SRAM内存池   

__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000))); //外部SRAM内存池

__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000))); //内部CCM内存池

//内存管理表

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};


利用_attribute((at))命令在指定地址申请内存,这也就是mymemset函数要传入的第一个参数,


那么为什么这句,第三个参数还要*2,因为在mymeset中u8 *xs = s;传入地址后,是以字节为单位处理的,而memtblsize中记录的数据是内存表个数,而申明内存表所用的单位是U16,两个字节。这样,传入的内存表的个数就要*2。


上面的,只是原子哥写的内存管理的一点点,确实一大堆宏定义,结构体,指针啥的看起来头疼,但是原理并不难,拿个小本本边看边记,还是能弄懂的。

关键字:STM32  内存管理 引用地址:STM32_内存管理

上一篇:STM32_SDIO
下一篇:STM32_DCMI

推荐阅读最新更新时间:2024-11-11 16:04

stm32系统时钟详解&&移植
写作原由:今日接手用stm32f100xx芯片开发的项目,以前用的是stm8s 和stm32f103xx芯片;因为在别人的项目代码的基础上做2次开发,但是发现那个代码main函数中没有对系统时钟的设置的相关函数,一直纳闷,但也没有深究,直至昨日 调试时出现串口收发数据出错,源代码在原项目的板子上串口发送、接收数据正常,同样程序在项目板子上收发的数据不正确, 两块板子芯片一样,串口收发管脚一样,最后发现原来板子外部晶振是8MHZ ,新板子外部晶振是12MHZ; 而在STM32固件库中,默认的外部晶振是8MHZ,由于时钟源不正确,导致波特率不正确,当然收发的数据也不正确了.....我勒个去!都怪自己平时看问题“不求甚解”。 (波特率与
[单片机]
<font color='red'>stm32</font>系统时钟详解&&移植
STM32 软件触发 DAC
/* DAC.c 用于DAC的初始化和配置 */ #include includes.h void DAC_Config(void); void DAC_Task(void* prg); void DAC_Task(void* prg) { DAC_SetChannel1Data(DAC_Align_12b_R,1028); //设置数据右对齐,转换值设置为1028 DAC_SoftwareTriggerCmd(DAC_Channel_1,ENABLE); //软件触发通道1 开始转换 DAC_SetChannel2Data(DAC_Align_12b_R,2047)
[单片机]
STM32-RCC的相关知识
STM32的内部RC时钟HSI RC、LSI RC可以被关闭,外部晶振时钟HSE Osc、LSE Osc可以被关闭或旁路,外部晶体时钟更精确。 一、为所有外设提供时钟 SYSCLK有三种时钟源:HSI(8MHz)、HSE(8MHz)、PLLCLK。其中开启CSS(时钟监视系统)可以在HSE失能时自动切换到HSI; PLLCLK-》USB Prescaler; SYSCLK-》AHB Prescaler-》(HLCK,APB1 Prescaler,APB2 Prescaler):HLCK为核心总线、DMA、AHB总线提供时钟; APB1 Prescaler-》(PLCK1 up to 36MHz,TIM2、
[单片机]
STM32-RCC的相关知识
STM32再学习——启动流程分析
我们写嵌入式程序,基本上采用C语言来编写,以main( )作为程序的入口。但实际上,mian()并不是最先要执行的,在这之前需要做一些基本的工作,如堆、栈的定义;main函数的复位连接等,这些工作就需要一个专门的启动程序来完成,由于需要做的工作内容不多,并且需要更直接的管理内存,一般采用汇编编写。 无论是STM32、ARM系列的单片机,还是简单的如51,PIC等,都以为上述原因,需要启动程序,只不过51,PIC等单片机的启动程序已经在相应的IDE编译、链接的时候隐含的编译了,故在写单片机程序的时候无需考虑。而STM32的启动有相应的启动文件,本文将采用KEIL MDK自带的启动文件STM32F10x.s进行分析。 1 启动模式的选
[单片机]
stm32设置MAC地址设置建议
虽然可以直接使用stm32的唯一ID做mac地址,但是不排除接入系统的设备中有mac地址相同的可能。 MAC地址一共6个字节,前3字节称为OUI,是由IEEE组织注册给网络设备生产商的;每个厂商拥有一个或多个OUI,彼此不同。后三字节则是由网络设备生产商分配给自己生产的每一个拥有MAC地址的设备,互不重复。 原理就是根据MAC地址前3字节来判断的,前3个字节、对应制造商的名称。 比如说,我们这个网络大家都不用苹果的设备,那么我所有stm32的mac地址的前三位都可以设置成苹果的MAC地址范围,这样就保证了后续无论什么设备接入进来,都不用担心mac地址冲突的问题了。 https://mac.51240.com/ 上面这个网站
[单片机]
<font color='red'>stm32</font>设置MAC地址设置建议
STM32 IO 问题
今天调试一块板子出现如下问题: 某口 PB5,用某个芯片的复位工作。 配置如下: #define GPIO_PIN_REST GPIO_Pin_5 GPIO_InitStructure.GPIO_Pin = GPIO_PIN_REST; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIOB- BSRR = GPIO_Pin_5 ; jlink调试,观看GPIOB,keil显示是Pin 为1 但是芯片实际测量管脚的电压却是0V。搞定不懂 后面改为:GPIO_Ini
[单片机]
STM32串口的发送和接收
USART是STM32内部集成的硬件外设,可以根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可以自动接收RX引脚的数据帧时序,拼接成一个字节数据,存放在数据寄存器里。 当配置好USART的电路之后,直接读取数据寄存器,就可以自动发送数据和接收数据了。在发送和接收的模块有4个重要的寄存器 发送数据寄存器TDR 发送移位寄存器,把一个字节的数据一位一位的移出去 接收数据寄存器RDR 接收移位寄存器,把一个字节的数据 下方为串口的发送和接收图解: 串口发送 在配置串口的各个参数时,可以选择发送数据帧的数据位的大小,可选8位或9位。 串口发送数据实际上就是对发送数据寄存器TDR进行写操作。 当串口发送
[单片机]
<font color='red'>STM32</font>串口的发送和接收
STM32学习笔记之 DS18B20 SEARCH ROM
使用说明,根据MCU不同 用户只需修改4函数 //单总线复位函数 int OWReset(); 单线总线的复位函数,注意这个要做相应修改,如果期间存在要返回1,期间不存在返回0, 直接从总线上读取的是期间存在返回0,不存在返回1 //向总线发送一个字节 void OWWriteByte(unsigned char dat); //向总线发送一位 void OWWriteBit(unsigned char bit_value); //读取总线一位 unsigned char OWReadBit(); 使用时用 int OWFirst();发现第一个单线器件 如果期间存在返回1,并且把ID存在 unsigned char
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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