(四)单片机系统动态内存分配,任务调度思想

发布者:真诚友谊最新更新时间:2017-02-04 来源: eefocus关键字:单片机  系统动态  内存分配  任务调度 手机看文章 扫描二维码
随时随地手机看文章

一、内存分配:


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:%d\r\n",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 = 0; i < TASK_MAX_COUNT; i++)

    {

        task = mem_alloc(sizeof(task_node_t));

#if CODE_REDUN

        if(NULL == task)

        {

            return;

        }

#endif

        list_add_tail(&g_idlelist, &task->next);

    }

}


2.3  生成任务函数,包括任务节点地址的申请,任务节点各个成员的赋值。从g_idlelist节点取出将要使用的任务节点,并将要它执挂载到g_runlist链表上,表示即将使用的任务节点。插入g_runlist链表时要根据任务优先级顺序插入节点。


/*任务进入队列:*/

static void task_in_queue(task_node_t *task)

{

    struct list_head * plist = NULL;

    task_node_t *p = NULL;


    if(list_isempty(&g_runlist))

    {

        list_add_tail(&g_runlist, &task->next);

        return;

    }


    /*队列不为空,根据优先级放到合适的地方*/

    for(plist = g_runlist.next; plist != &g_runlist; plist = plist->next)

    {

        p = container_of(plist, task_node_t, next);

        if(PRIO_GET(task->flag) < PRIO_GET(p->flag))

        {

            //找到位置了,终止循环

            list_add(&task->next, plist->prev, plist);

            return;

        }

    }


    //插入到最后

    list_add_tail(&g_runlist, &task->next);

}


/*任务控制块内存申请,参数定义:*/

u8 task_create(handle func,u8 *para, u8 prio)

{

    struct list_head * plist;

    task_node_t *task;


    __disable_irq();

    plist = list_fetch(&g_idlelist);

    __enable_irq();


    if(NULL == plist)

    {

        #if PRINTF_ON

        stmprintf("TASK full\r\n");

        #endif

        return 1;

    }


    task = container_of(plist, task_node_t, next);

    list_init_head(&task->next);

    task->callback = func;

    task->para = para;

    task->flag = prio;


    __disable_irq();

    task_in_queue(task);

    __enable_irq();


    return 0;

}


2.4 任务执行函数


/*执行任务函数,从任务链表g_runlist取出一个优先级最高的任务节点,执行任务。执行任务之前关闭总中断,执行结束后打开总中断*/



void task_run(void)

{

    task_node_t * task;

    handle cb;

    u8 * para;

    struct list_head * plist;

    __disable_irq();

    plist = list_fetch(&g_runlist);

    if(plist)

    {

        task = container_of(plist, task_node_t, next);

        cb = task->callback;

        para = task->para;

        list_add_tail(&g_idlelist, &task->next);

        __enable_irq();


        if(cb)

        {

#if CODE_REDUN

        current_tick = get_timer6_tick();

#endif

            cb(para);

#if CODE_REDUN

            current_tick = 0;

#endif

        }

    }

    else

    {

        __enable_irq();

    }

}


 


关键字:单片机  系统动态  内存分配  任务调度 引用地址:(四)单片机系统动态内存分配,任务调度思想

上一篇:(三)stm32之串口通信DMA传输完成中断
下一篇:(五)stm32工程代码HardFault异常查错调试方法

推荐阅读最新更新时间:2024-03-16 15:32

【PIC单片机】-- IIC相关知识
00 写在前面 该系列的文章,源自于担任过PIC单片机课程的助教,主要向学弟们讲解了几节实验课的内容。在此记录上课的一些知识。 本系列文章主要介绍的内容: 实验1–介绍和基本I/O–按钮和LED(学习嵌入式的第一步就是点一个灯,就像学习编程语言的第一步都是写一个“hello world”代码) 实验2–MPLAB+PICkit 3+LCD+I/O(这次主要是介绍液晶显示屏的使用,很多时候我们系统的调试都需要用到,比如做一个测温系统,那我们就可以通过液晶显示屏,显示传感器采集的数值,然后再通过软件来做进一步的处理) 实验3–ADC(这个是模数转化实验,就是之前模数电学习那些知识的一个综合运用) 实验4–串行通信–UA
[单片机]
【PIC<font color='red'>单片机</font>】-- IIC相关知识
[单片机] md5签名算法
#include string.h #include md5.h #define F(x, y, z) ((x & y) | (~x & z)) #define G(x, y, z) ((x & z) | (y & ~z)) #define H(x, y, z) (x ^ y ^ z) #define I(x, y, z) (y ^ (x | ~z)) #define ROTATE_LEFT(x, n) ((x n) | (x (32 - n))) #define FF(a, b, c, d, x, s, ac) { a += F(b, c, d) + x + ac;
[单片机]
基于MSP430F1611单片机实现多功能电路保护装置的设计
电力系统的飞速发展对继电保护不断提出新的要求,电子技术、计算机技术与通信技术的飞速发展又为继电保护技术的发展不断注入了新的活力。随着微机保护装置的研究,在微机保护软件、算法等方面也取得了很多理论成果。从20世纪90年代开始我国继电保护技术已进入了微机保护的时代。 电力系统对微机保护的要求不断提高,除了保护的基本功能外,还应具有大容量故障信息和数据的长期存放空间,快速的数据处理功能,强大的通信能力,与其他保护、控制装置和调度联网,以共享全系统数据、信息和网络资源的能力、高级语言编程等。这就要求微机保护装置具有相当于一台PC机的功能。 计算机网络可从网上获取电力系统运行和故障的任何信息和数据,也可将它所获得的被保护元件的任何信
[单片机]
基于MSP430F1611<font color='red'>单片机</font>实现多功能电路保护装置的设计
基于PIC单片机与TC787芯片的三相半控整流电路设计
  整流 电路 广泛应用在直流电机调速,直流稳压 电压 等场合。而三相半控整流桥电路结构是一种常见的整流电路,其容易控制,成本较低。本文中介绍了一种基于 PIC690单片机与专用集成触发芯片TC787的三相半控整流电路,它结合专用集成触发芯片和数字触发器的优点 ,获得了高性能和高度对称的触发脉冲。它充分利用单片机内部资源 ,集相序自适应、系统参数在线调节和各种保护功能于一体,可用于对负载的恒电压控制。主电路采用了三相半控桥结构,直流侧采用LC滤波结构来提高输出的电压质量。    系统总体设计   本系统通过PIC690单片机作为主控制芯片,用晶闸管作为主要开关器件。设计的目标是保持输出的直流电压稳定,输出电压纹波小,交流输出
[单片机]
基于PIC<font color='red'>单片机</font>与TC787芯片的三相半控整流电路设计
单片机最小系统的适配驱动器FB120-AC220V
单片机在当今的电子设备中已得到大量使用,原因是其功能强大、价格低廉、应用灵活。一般来说,单片机都是数字型集成电路,而要保证这种类型集成电路的正常工作,一般需要直流电源;当要驱动交流220V负载时,还应选择合适的执行部件产设计相应的驱动电路。直流电源的设计有多种方案,常用的有交流220V/直流电源模块、专门设计的开关电源电路以及由整流电路和三端稳压器件组成的电路模块等三种。相比来说,第一种电路的优点是简单、方便、可靠,但价格稍高;第二种电路虽然性能较好,但调试比较费事,且成本稍高;而方案三虽然比较成熟而且简单、方便,但抗EMI能力稍弹,特别是这种方案必须采用电源变压器,因此也比较笨得且成本偏高。 1 FB120的主要功能 本文
[电源管理]
单片机开发技巧
一、 如何提高C语言编程代码的效率 邓宏杰指出,用C语言进行单片机程序设计是单片机开发与应用的必然趋势。他强调:“ 如果使用C编程时,要达到最高的效率,最好熟悉所使用的C编译器。先试验一下每条C语 言编译以后对应的汇编语言的语句行数,这样就可以很明确的知道效率。在今后编程的 时候,使用编译效率最高的语句。” 他指出,各家的C编译器都会有一定的差异,故编译效率也会有所不同,优秀的嵌入式系 统C编译器代码长度和执行时间仅比以汇编语言编写的同样功能程度长5-20%。他说:“ 对于复杂而开发时间紧的项目时,可以采用C语言,但前提是要求你对该MCU系统的C语言 和C编译器非常熟悉,特别要注意该C编译系统所能支持的数据类型和算法。虽然C语言是
[单片机]
用电位器通过STC12C2052AD单片机片内AD和PWM控制电机无极变速
/******************************************************** 功能 :用电位器通过STC12C2052AD片内AD和PWM控制电机转速 单片机;STC12C2052AD 晶振 :12M ********************************************************/ #include STC12C2052AD.H //头文件 #include intrins.h //51基本运算(包括_nop_空函数) #define uchar unsigned char #define uin
[单片机]
MSP430单片机实践篇---内部FALSH的操作
//****************************************************************************** // 测试MSP430单片机向FLASH中写入操作 // // 描述; 每次掉电或复位后都能看到数码管上显示的数值增1,说明对flash的写入与读取成功 // // 注意:修改flash中的内容必须首先执行擦除操作, // 因为对FLASH的操作只能将1写成0,而不能将0写成1只有擦除才能将0写回1 // 擦除操作至少将擦除1个段 // // 如果使用其它型号的单片机,只需将头文件改为相应的 msp430xx.h ; // 并在Option的Target中的device改
[单片机]
热门资源推荐
热门放大器推荐
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

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

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