打印
[STM32F1]

STM32基础篇 ——SRAM 内存管理实验

[复制链接]
4162|21
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
实验目标:
1. 了解内存管理的原理
2. 学会使用内存分配和使用
沙发
aizaixiyuanqian|  楼主 | 2018-3-7 20:49 | 只看该作者
学习了使用 STM32 驱动外部 SRAM,以拓展 STM32 的内存。
如果使用外部内存像上一节讲的那样,定义一个数组来使用,显然不是一个好方
法。

使用特权

评论回复
板凳
aizaixiyuanqian|  楼主 | 2018-3-7 20:49 | 只看该作者
在我们学习 C 语言的时候,我们曾经学习过使用 mallac 和 free 函数来申请
内存和内存释放,今天我们就来介绍一种比较简单的内存管理:分块内存管理。

使用特权

评论回复
地板
aizaixiyuanqian|  楼主 | 2018-3-7 20:52 | 只看该作者
内存管理的实现原理


使用特权

评论回复
5
aizaixiyuanqian|  楼主 | 2018-3-7 20:54 | 只看该作者
从图上我们可以看出,内存管理由内存池和内存管理表两部分组成,内存池
被分成 n 个内存块,然后对应内管管理表,内存管理表就是用来标识相应的内存
块是否已经使用。我们可以设置为,当相应的内存管理表设置为 0 的时候,表示
该内存块没有被使用;而当内存管理表设置为非零的时候,表示该内存已被使用,
而管理表里面的数字可以用当前申请内存块的数量。

使用特权

评论回复
6
aizaixiyuanqian|  楼主 | 2018-3-7 20:55 | 只看该作者
定义内存池大小。
要实现内存池,肯定要先设定好内存池的大小,所以第一步我们先来设
置内存池的大小。在我们 malloc.h 里面我们定义了:
#define MEMORY_MAX_SIZE 200*1024 //先设置为 200KB
在这里我们定义了内存大小为 200KB,大家在这里注意的是,我们的
IS62WV512 只有 1MB 字节,所以定义内存池的大小的时候,不要超过 1MB,
甚至要远小于 1MB,因为我们还需要一部内存来保存内存管理表。

使用特权

评论回复
7
aizaixiyuanqian|  楼主 | 2018-3-7 20:56 | 只看该作者
如果大家是使用 MDK 来编译的话,不一样的 MDK 版本,它的编译内
存大小是不一样的,所以,有时候,虽然没有超过内存空间,但是编译的时
候还是会出错。就像上一节讲的 SRAM 实验,其实大家可以直接定义一个
1MB 长度数组,囊括外部 SRAM 所有的内存来测试,而有些版本的 MDK
可以编译,而有些版本的会编译出错。

使用特权

评论回复
8
aizaixiyuanqian|  楼主 | 2018-3-7 20:57 | 只看该作者
内存块的大小。
在内存池中,内存是一块一块的来保存的,所以我们还要设定内存块的
大小。内存块越大,内存管理表也就越小,但是内存分配造成的内存浪费也
就更多;而内存块越小,内存管理表也就越大。例程里面我们定义的内存块
大小在 malloc.h 里面
#define MEMORY_BLOCK_SIZE 32 //内存块长度

使用特权

评论回复
9
aizaixiyuanqian|  楼主 | 2018-3-7 21:02 | 只看该作者
内存管理表的长度
定义了内存池,还要定义相应的内存管理表,在 malloc.h 中例程还定义
了内存管理表的大小:
#define MAP_TABLE_SIZE (MEMORY_MAX_SIZE / 32)
内存管理表的大小其实也就是内存池的总内存除以内存块的大小,毕竟
只要一个内存块对应内存管理表中的一个标识就可以。

使用特权

评论回复
10
aizaixiyuanqian|  楼主 | 2018-3-7 21:13 | 只看该作者
内存管理信息
在程序中,我们需要定义变量来保存我们建立的内存管理信息,在例程
中,定义了一个结构体,用来保存相应的内存管理信息。结构体定义在
malloc.h 里面:
typedef struct
{
uint8_t managementReady;  //内存管理状态
uint8_t *memoryBaseAddr; //内存池首地址
uint16_t *managementMap; //内存管理表
uint16_t memoryFree;  //空闲内存块(没有用到的内存)
} MemoryTypeDef;

使用特权

评论回复
11
aizaixiyuanqian|  楼主 | 2018-3-7 21:13 | 只看该作者
在例程中我们定义了这样一个结构体全局变量,用来保存信息。
/* 定义一个内存状态结构体 ,同时初始化 */
MemoryTypeDef Memory =
{
0, //内存管理状态不可用
MemoryBase,  //内存池首地址
MemoryManagementMap,  //内存管理表
MAP_TABLE_SIZE, //空闲内存块
};

使用特权

评论回复
12
aizaixiyuanqian|  楼主 | 2018-3-7 21:14 | 只看该作者
内存池程序实现
/* 将所有空间定义位一个数据,首地址从 0x68000000 开始,并且 4
字节对齐 */
__align(4)  uint8_t  MemoryBase[MEMORY_MAX_SIZE]
__attribute__((at(0X68000000)));
这里我们定义了一个 8 位长度的内存池 MemoryBase,后面的
__attribute__((at(0X68000000)))的意义是该内存池数组从 0x68000000 开
始。

使用特权

评论回复
13
aizaixiyuanqian|  楼主 | 2018-3-7 21:14 | 只看该作者
内存管理表程序实现
/* 定义一个内存管理表 */
uint16_t  MemoryManagementMap[MAP_TABLE_SIZE]
__attribute__((at(0X68000000 + MEMORY_MAX_SIZE)));
定义的内存管理表放置在内存池之后。

使用特权

评论回复
14
aizaixiyuanqian|  楼主 | 2018-3-7 22:14 | 只看该作者
内存管理初始化程序实现
void MEMORY_Init(void)
{
/* 将管理复位清零 */
MEMORY_Set(MemoryManagementMap, MAP_TABLE_SIZE, 0);
Memory.managementReady = 1;
}
这个初始化程序,也就是将管理表所有的状态都设置为未使用状态,并
将内存池状态设置为准备状态。而在这里还使用了一个 MEMORY_Set()函
数,它的程序如下:
static void MEMORY_Set(uint16_t *s, uint32_t length, uint8_t dat)
{
while(length--)
{
*s = dat;
s++;
}
}
这个程序其实也就是将内存管理表设置为相应的状态。

使用特权

评论回复
15
aizaixiyuanqian|  楼主 | 2018-3-7 22:14 | 只看该作者
内存分配原理
1. 内存分配的实现原理
内存的分配其实也就是 malloc 函数的实现,malloc 函数就是输入你想要
申请的内存大小,然后返回一个内存空间的首地址。如何实现呢?它的实现
步骤如下:
1) 首先判断申请内存需要的内存块数量(比如说 n 个)。
2) 然后从内存管理表最后开始寻找 n(n 等于所要申请的内存块数量)
个连续的、空的、内存块。当寻找到的时候,将这段内存的内存管
理表设置为使用,并返回该内存空间的首地址。而如果不够那么返
回空指针。

使用特权

评论回复
16
aizaixiyuanqian|  楼主 | 2018-3-7 22:15 | 只看该作者
内存分配的程序。
static uint8_t MEMORY_Free(uint32_t addrOffset)
{
uint16_t block;
/* 若未初始化,先初始化 */
if(!Memory.managementReady)
{
MEMORY_Init();
return 0xFF;
}
if(addrOffset < MEMORY_MAX_SIZE) //在内存池内
{
/* 先将内存池地址偏移量转换为内存管理表偏移量 */
addrOffset = addrOffset / MEMORY_BLOCK_SIZE;
/* 读取开辟的内存空间大小 */
block = Memory.managementMap[addrOffset];
/* 将释放内存的内存管理表释放 */
MEMORY_Set(&Memory.managementMap[addrOffset], block, 0);
Memory.memoryFree += block; //空闲内存块增加
return 0;
}
else
{
return 0xFF;
}
}

使用特权

评论回复
17
aizaixiyuanqian|  楼主 | 2018-3-7 22:16 | 只看该作者
这个函数作用就是寻找相应的地址空间,并返回一个地址偏移量,它寻
找的方式是从内存池最后一块内存块开始寻找的。而 malloc 函数的具体实现
如下:
void *malloc(uint32_t size)
{
uint32_t addrOffset;
addrOffset = MEMORY_Maloc(size); //查找一个合适的内存空间
if(addrOffset == 0xFFFFFFFF) //分配失败
{
return 0; //返回 NULL(即 0)
}
/* 将地址偏移量转换成指针地址返回 */
return (void *)((uint32_t)Memory.memoryBaseAddr + addrOffset);
}

使用特权

评论回复
18
aizaixiyuanqian|  楼主 | 2018-3-7 22:37 | 只看该作者
内存释放的原理实现
内存释放其实也就是 free 函数的实现,这个函数的作用就是将我们申请
的内存释放,以便能够重新使用。它有一个输入参数,也就是要释放内存的
首地址。这个函数的实现方式是,根据内存的首地址,读取该内存空间的长
度(前面我们说过内存管理表里面的数字也就是申请内存空间的大小,所以
读取该首地址对应的内存管理表,就可以得到该内存空间的大小),然后根
据内存空间的长度,清零内存管理表里对应的内存使用标志。

使用特权

评论回复
19
aizaixiyuanqian|  楼主 | 2018-3-7 22:38 | 只看该作者
内存释放的程序实现
void free(void *pro)
{
uint32_t addrOffset;
if(pro == 0) //若指针为 NULL 的时候
{
return;
}
/* 读取该内存空间在内存池上面的地址偏移量 */
addrOffset = (uint32_t)pro - (uint32_t)Memory.memoryBaseAddr;
/* 释放内存 */
MEMORY_Free(addrOffset);
/* 释放 pro 指针 */
pro = 0;
}

使用特权

评论回复
20
aizaixiyuanqian|  楼主 | 2018-3-7 22:38 | 只看该作者
这个函数的作用是将要释放的内存空间首地址转换为内存池的地址偏移
量,然后在调用 MEMORY_Free()函数释放内存。MEMORY_Free()函数程序
如下:
static uint8_t MEMORY_Free(uint32_t addrOffset)
{
uint16_t block;
/* 若未初始化,先初始化 */
if(!Memory.managementReady)
{
MEMORY_Init();
return 0xFF;
}
if(addrOffset < MEMORY_MAX_SIZE) //在内存池内
{
/* 先将内存池地址偏移量转换为内存管理表偏移量 */
addrOffset = addrOffset / MEMORY_BLOCK_SIZE;
/* 读取开辟的内存空间大小 */
block = Memory.managementMap[addrOffset];
/* 将释放内存的内存管理表释放 */
MEMORY_Set(&Memory.managementMap[addrOffset], block, 0);
Memory.memoryFree += block; //空闲内存块增加
return 0;
}
else
{
return 0xFF;
}
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

62

主题

1353

帖子

6

粉丝