当前位置:首页 > 单片机 > 单片机
[导读]一、内存分配:1.1 申请一块内存大小定义:#define MEM_0_SIZE (8) //8字节#define MEM_1_SIZE (16) //16字节#define MEM_2_SIZE (32)#define MEM_3_SIZE (64)#define MEM_4_SIZE (128)#define MEM_5_SIZE (256)1.2

一、内存分配:


1.1 申请一块内存大小定义:


#define MEM_0_SIZE (8) //8字节

#define MEM_1_SIZE (16) //16字节

#define MEM_2_SIZE (32)

#define MEM_3_SIZE (64)

#define MEM_4_SIZE (128)

#define MEM_5_SIZE (256)

1.2 设定SIZE大小内存可申请到的内存块最大个数定义:


#define MEM_0_COUNT (16) //最大16个内存块

#define MEM_1_COUNT (16)

#define MEM_2_COUNT (32)

#define MEM_3_COUNT (32)

#define MEM_4_COUNT (32)

#define MEM_5_COUNT (16)

1.3 内存数组定义


static u8 g_8bytesmem[MEM_0_COUNT*MEM_0_SIZE];

static u8 g_16bytesmem[MEM_1_COUNT*MEM_1_SIZE];

static u8 g_32bytesmem[MEM_2_COUNT*MEM_2_SIZE];

static u8 g_64bytesmem[MEM_3_COUNT*MEM_3_SIZE];

static u8 g_128bytesmem[MEM_4_COUNT*MEM_4_SIZE];

static u8 g_256bytesmem[MEM_5_COUNT*MEM_5_SIZE];

1.4 内存管理结构体定义



typedef struct _mem_t

{

//控制标记

u32 flag;//每种内存最多32块

/*每块内存的大小*/

u16 size;

/*内存块个数*/

u16 count;

/*内存开始指针*/

u8 *buf;

}mem_t;


1.5 内存分配设计思想:


  我们设置动态内存分配的初衷在于:有些单片机系统内存资源比较少,便显得特别珍贵,因此我们要实现内存的反复利用,好像就像一个池子一样,我们要循环利用池子里的水资源。比如说洗澡时,如果是喷头式的,这样如果不去回收水便会浪费;而如果是在澡池子里洗澡,每次利用完水后,水资源便会重新回到池子,可循环的利用起来。我们设置动态内存分配也是这个原理,使用之前先去申请,使用结束后便释放,下次便可继续申请该内存,循环利用内存池里的资源。


我们先定义6个数组,各个数组大小为XXX_SIZE * XXX_COUNT,XXX_SIZE是每个内存块大小,XXX_COUNT是内存块的个数。将各数组的首地址赋给g_mem_mngt[i].buf(i:0-5)m_mngt[i].buf便分别指向每个数组的首地址。我们申请某一长度len的内存时,通过计算选定匹配的内存块大小,然后从对应内存池首地址去查找空闲的内存块,找到即停止查找,将该内存块起始地址取出便为我们申请到的内存块,申请到后将该地址标记,表示已被占用,下次不能再申请到。


释放内存,首先根据内存节点所在的起始地址与各个内存池起始地址和结束地址,判断内存节点所有所在的内存池,然后从该内存池首地址开始查找,定位该内存落在的内存块控制区域,找到后则停止查找,并将该内存块标记位清零,表示该内存块已空闲,下次可申请使用。


1.6 各个内存块初始化,申请的起始地址、内存块个数、字节大小、标志位定义



void mem_init(void)

{

g_mem_mngt[0].buf = g_8bytesmem;

g_mem_mngt[0].count = MEM_0_COUNT;

g_mem_mngt[0].size = MEM_0_SIZE;

g_mem_mngt[0].flag = 0;


g_mem_mngt[1].buf = g_16bytesmem;

g_mem_mngt[1].count = MEM_1_COUNT;

g_mem_mngt[1].size = MEM_1_SIZE;

g_mem_mngt[1].flag = 0;


g_mem_mngt[2].buf = g_32bytesmem;

g_mem_mngt[2].count = MEM_2_COUNT;

g_mem_mngt[2].size = MEM_2_SIZE;

g_mem_mngt[2].flag = 0;


g_mem_mngt[3].buf = g_64bytesmem;

g_mem_mngt[3].count = MEM_3_COUNT;

g_mem_mngt[3].size = MEM_3_SIZE;

g_mem_mngt[3].flag = 0;


g_mem_mngt[4].buf = g_128bytesmem;

g_mem_mngt[4].count = MEM_4_COUNT;

g_mem_mngt[4].size = MEM_4_SIZE;

g_mem_mngt[4].flag = 0;


g_mem_mngt[5].buf = g_256bytesmem;

g_mem_mngt[5].count = MEM_5_COUNT;

g_mem_mngt[5].size = MEM_5_SIZE;

g_mem_mngt[5].flag = 0;

#ifdef MEM_DEBUG

memset(g_count, 0, sizeof(g_count));

#endif

#if CODE_REDUN

mem_fail = 0;

#endif

}


1.7 内存块申请


  查找可申请内存起始地址,返回值为内存块起始地址。该类型函数有void * mem_alloc(u8 size)和void *mem_isr_alloc(u8 size)两种函数定义,文章中只附加在非中断模式下代码。在非中断模式下,申请内存块之前要先关闭中断,申请结束后再打开中断通知将申请到的内存地址标志位置1,表示已申请,这样做比较安全。在中断模式下,不必做此操作,其他写法都一致。



void * mem_alloc(u8 size)

{

u8 i, j;

mem_t * memptr = NULL;

u8 * ptr = NULL;

/*先找到内存适合的控制块所在控制头*/

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

{

if(size <= g_mem_mngt[i].size)

{

memptr = &g_mem_mngt[i];

//找到空闲的控制块

ptr = memptr->buf;

for(j = 0; j < memptr->count; j++, ptr += memptr->size)

{

__disable_irq();

if(!(memptr->flag & (1<

{

//标记占用

memptr->flag |= (1<

__enable_irq();

return ptr;

}

__enable_irq();

}

#ifdef MEM_DEBUG

//内存不足,记录一下

MEM_STATIC_INC(i);

#endif

}

}

#if CODE_REDUN

mem_fail++;

#endif

return NULL;

}


1.8 内存的释放


  释放内存,即将表示该内存的占有标志位清零,释放后下次便可申请该内存。释放内存函数分为void mem_free(void * ptr)和void mem_isr_free(void * ptr)两种,一种是在非中断模式下,一种是在中断模式下。在非中断模式下释放之前应先关闭总中断,防止被打断,释放结束后再打开总中断。在中断模式下则不必处理该操作。


void mem_free(void * ptr)

{

u8 i;

mem_t * memp = NULL;

u8 * optr = ptr;

u8 j;

u8 * p;


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

{

memp = &g_mem_mngt[i];

//定位该内存指针落在哪个控制区域

if(optr >= memp->buf && optr < memp->buf + memp->size*memp->count)

{

for(p = memp->buf, j=0; j < memp->count; p += memp->size, j++)

{

if((optr >= p) && (optr < p + memp->size))

{

__disable_irq();

memp->flag &= ~(1<


#if PRINTF_ON

stmprintf("free size:%d,j:%drn",memp->size, j);

#endif

__enable_irq();

return;

}

}

}

}

}


二、任务调度


/*链表的定义,list_head g_idlelist表示空闲可用任务节点链表,list_head g_runlist表示即将使用的任务节点链表。*/

static struct list_head g_runlist;

static struct list_head g_idlelist;


/*任务节点*/

typedef struct node

{

struct list_head next; //双向链表定义

handle callback; //任务操作函数指针

u8 *para; //任务操作函数参数

u8 flag; //,标志字段,当前用来表示任务优先级

}task_node_t; //任务节点


/*任务优先级*/

#define PRIO_HIGH (0x1) //优先级最高

#define PRIO_NORMAL (0x2) //次优先级

#define PRIO_LOW (0x4) //最低优先级


2.1 任务调度,该算法思想为:


分别建立g_idlelist和g_runlist两个双向链表,在任务初始化时,为各个任务控制块节点申请内存,将各个任务节点挂载到g_idlelist链表上,表示目前空闲可用的任务节点,当有我们要申请任务时,要从链表g_idlelist上取下任务节点,同时将节点挂载到g_runlist链表上,表示即将使用的任务节点,挂载时是有优先级的,当g_runlist为空链表时,我们直接挂载上去,当g_runlist不为空链表时,便要考虑优先级的问题,任务优先级高的任务节点挂载在最前面。然后按照优先级顺序执行对应的任务,等任务执行结束后将任务节点又挂载到g_idlelist链表最后面。等待下次的调用。


2.2 任务节点初始化,为任务节点申请内存,并将任务节点挂载到g_idlelist链表上,表示未使用的任务节点。



void task_queue_init(void)

{

u8 i;

task_node_t * task;

list_init_head(&g_runlist);

list_init_head(&g_idlelist);


for(i =

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭