微控制器内存介绍
大部分微控制器内部中只有Flash和SRAM两种存储器,Flash中会存储代码以及const定义的常量(除了局部非静态常量,即函数内定义的非static常量,emmm我也很奇怪)。SRAM中则存储所有的变量以及局部非静态常量。通过代码可能更好的理解这一点:
const static int si_c ; //在Flash中存储
static int si; //在SRAM中存储
const int i_c; //在Flash中存储
int i; //在SRAM中存储
void num(void)
{
const static int sj_c ; //在Flash中存储
static int sj; //在SRAM中存储
const int j_c; //在SRAM中存储
int j; //在SRAM中存储
}
要验证上述类型的变量存储位置,可以定义后通过printf函数打印其地址。
除了以上方式会直接使用到微控制器内部存储器外,还可以通过malloc函数动态分配内存,通过malloc函数分配的内存,同样是位于微控制器内部Flash中,如下所示:
#include "stdlib.h"
void num(void)
{
int* num;
num = (int*)malloc(50);
}
一般来说,对微控制器的内存应用到此为止,通过malloc函数能基本运用到所有内存(SRAM一般较小,存储变量即可),但对更大的数据量则需要外部存储器进行存储,外部Flash、SRAM乃至SDRAM芯片可以存储大量数据,据此也引发了相应的问题:微控制器难以对外部存储器芯片进行有效的空间管理,即使使用EXMC、FSMC等接口,也只是对访问比较方便而已,因此需要通过编程,实现对各个存储器的管理。
内存管理原理
内存管理本质上是建立1张内存管理表(当然也可以有其它方法),类似NAND Flash管理中的FTL层,将表内的某一项对于实际内存的1小块,当通过设定的API函数申请内存时,内存管理表中对应项被赋值,同时实际内存的对应的块被分配,如下图所示。
在存储管理中,有以下几个主要变量、宏定义:
#define MemorySize 512*1024//外部存储块总大小
#define BlockNum 512 //将外部存储块分为512块,对应内存管理表第1项至第512项
#define BlockSize MemorySize/BlockNum //每个存储块大小
#define StartAddr 0 //外部存储块的访问首地址(通过EXMC接口的话可查阅手册)
int MemTable[100] = {0}; //下标为单数的元素为申请地址的首项,单数+1块为末项
int MemNum = 0; //已被分配的存储块数量
注意:每块存储块的大小决定了能申请的地址最小值。
内存分配原理
内存分配需要实现1个API函数,该函数的传入参数有两个:接收指针、申请地址大小。其中,接收指针用于存放分配的地址块首地址,申请地址大小应该以字节为单位。
在该函数中,需要完成以下事项:
- 根据申请地址的大小,计算分配存储块的数量。
- 从下标为0开始,根据MemTable,寻找足够、未被分配、连续的存储块以分配,若实在找不到,则返回0表示分配失败。
- 表示已被分配的块数量加1。
- 在MemTable中登记。
- 计算地址块首地址后,将其赋值给接收指针。
内存释放原理
内存释放同样需要1个API函数,该函数仅需要1个传入参数,即接收指针。接收指针用于计算首末地址块的位置。
在该函数中,需要完成以下事项:
- 根据接收指针及存储区域首地址,计算首末地址块的位置。
- 将MemTable中,被释放的内存块对应两个元素后开始,所有元素下标减2。
- 表示已被分配的块数量减1
参考代码
以下程序仅供参考!
宏定义及.c文件内部变量
#define MemorySize 512*1024//外部存储块总大小
#define BlockNum 512 //将外部存储块分为512块,对应内存管理表第1项至第512项
#define BlockSize MemorySize/BlockNum //每个存储块大小
#define StartAddr 0 //外部存储块的访问首地址(通过EXMC接口的话可查阅手册)
int MemTable[100] = {0}; //下标为单数的元素为申请地址的首项,单数+1块为末项
int MemNum = 0; //已被分配的存储条数量
内存分配
int MemDistribute(int size, char* p)
{
int num; //分配存储块的数量
int i = 0; //已确定块的数量
int j = 0;
int Startaddr = 1;
num = size / BlockSize;
if((size % BlockSize) != 0)num++; //整数存储块放不下
while(i < num) //通过循环寻找足够、未被分配、连续的存储块
{
if((Startaddr + size - 1) >= BlockNum)return 0; //剩余空间不足,返回
for(j = 0; j < MemNum; j++) //循环检查该块是否已被分配
{
if(MemTable[j*2] == (Startaddr+i)) //如果该块已被分配为其它存储块的首地址(只可能是首地址)
{
Startaddr = MemTable[j*2+1]+1; //跳过这一整段被分配的内存
i = 0;
break;
}
}
if(MemTable[j*2] == 0) //循环了所有被分配的存储条,该块未被分配
{
i = i+1; //进行下一块的判断
}
}
MemNum++;
MemTable[(MemNum-1)*2] = Startaddr;
MemTable[(MemNum-1)*2+1] = Startaddr + size -1;
p = (char*)(StartAddr + Startaddr * BlockNum);
return 1;
}
内存释放
int MemRelease(char* p)
{
int num;
int i = 0;
int j = 0;
int Startaddr;
Startaddr = (int)(p - StartAddr) / BlockNum;
if((Startaddr > BlockNum) || (MemNum == 0))return 0;
while(i < MemNum)
{
if(Startaddr == MemTable[i*2])
{
for(j = i; j < MemNum + 1; j++)
{
MemTable[j*2] = MemTable[j*2+2];
MemTable[i*2+3] = MemTable[i*2+3];
}
MemNum--;
break;
}
i++;
}
return 1;
}