席萌0209 发表于 2016-1-6 15:15:32

【RTX操作系统教程】第18章    内存管理

第18章    内存管理

    本章节为大家讲解RTX支持的内存管理。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。
18.1 内存管理介绍
18.2 内存管理API函数
18.3 实验例程说明(4字节对齐)
18.4 实验例程说明(8字节对齐)
18.5      总结

18.1内存管理介绍
    在ANSIC中,可以用malloc()和free()2个函数动态的分配内存和释放内存,但是,在嵌入式实时操作系统中,调用malloc()和free()却是危险的,因为多次调用这两个函数会把原来很大的一块连续内场区域逐渐地分割成许多非常小而且彼此又不相邻的内存块,也就是内存碎片。由于这些内存碎片的大量存在,使得程序到后来连一段非常小的连续内存也分配不到。另外,由于内存管理算法上的原因,malloc()和free()函数的执行时间是不确定的。
    在RTX中,操作系统把连续的大块内存按分区来管理。每个分区中包含整数个大小相同的内存块。如图18.1所示:
图18.1 内存分区
利用这种机制,就可以得到和释放固定大小的内存块。这样内存的申请和释放函数的执行时间就是确定的了。
    在一个系统中可以有多个内存分区,这样,应用程序就可以从不同的内存分区中得到不同大小的内存块。但是特定的内存块在释放时,必须重新放回到它以前所属于的内存分区。显然,采用这样的内存管理算法,上面的内存碎片文件就得到了解决。
    其实RTX的内存管理也非常好理解,可以理解成一个二维数组,比如我们定义一个二维数组为:uint8_t mpool。对应到RTX的内存管理上就是定义了10个内存块,每块大小是32字节。如果还需要其它大小的内存块,还可以多定义几个其它大小的。

席萌0209 发表于 2016-1-6 15:21:00

18.2 内存管理API函数


    使用如下7个函数可以实现RTX的内存管理:
u _declare_box
u _declare_box8
u _init_box
u _init_box8
u _alloc_box
u _calloc_box
u _free_box
关于这7个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件

这里我们重点的说一下函数_declare_box,_declare_box8,_init_box,_init_box8,_alloc_box和_free_box。

18.2.1 函数_declare_box


函数原型:
#define _declare_box( \\
    pool,      \\    /* 内存池变量名 */
    size,      \\    /* 内存块大小,单位字节 */
    cnt )      \\    /* 内存池中内存块的个数 */
    U32 pool[((size+3)/4)*(cnt) + 3]
函数描述:
函数_declare_box用于定义一块内存池。
u 第1个参数填写内存池的变量名。
u 第2个参数填写内存块的大小,单位字节。
u 第3个参数填写内存池中内存块的个数。
使用这个函数要注意以下问题:
1.   宏定义中通过操作(size+3)/4保证了每个内存块大小是4字节的倍数,从而也就保证了每个内存块的首地址是4字节对齐的(4字节对齐的含义是地址对4求余等于0)。这里初学者也要注意数据类型的字节对齐问题。
    基本数据类型的字节对齐问题    这个问题在MDK5安装目录里面的文档DUI0375G_02_mdk_armcc_user_guide.pdf里面有详细的说明,    简单的说明,数据类型有几个字节,那么这个数据类型的变量就是几字节对齐,比如32位的int型    有4个字节,那么此int型定义的变量就是4字节对齐,对于初学者要牢牢的记住这个知识点。                                                               
2.   内存池中额外定义的12个字节用于将内存块做指针链表,方便动态的申请和释放。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

18.2.2 函数_declare_box8


函数原型:
#define _declare_box8( \\
    pool,      \\    /* 内存池变量名 */
    size,      \\    /* 内存块大小,单位字节 */
    cnt )      \\    /* 内存池中内存块的个数 */
    U64 pool[((size+7)/8)*(cnt) + 2]
函数描述:
函数_declare_box用于定义一块内存池。
u 第1个参数填写内存池的变量名。
u 第2个参数填写内存块的大小,单位字节。
u 第3个参数填写内存池中内存块的个数。
使用这个函数要注意以下问题:
1.   宏定义中通过操作(size+7)/8保证了每个内存块大小是8字节的倍数,从而也就保证了每个内存块的首地址是8字节对齐的(8字节对齐的含义是地址对8求余等于0)。这里初学者也要注意数据类型的字节对齐问题。
2.   内存池中额外定义的16个字节用于将内存块做指针链表,方便动态的申请和释放。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box8(mpool, PoolPerBlockSize, PoolBlocks);

18.2.3 函数_init_box


函数原型:
int _init_box(
    void* box_mem,       /* 内存池首地址 */
    U32   box_size,      /* 内存池大小,单位字节 */
    U32   blk_size );    /* 内存块大小,单位字节 */
函数描述:
函数_init_box用于内存池的初始化,初始化时用到的参数都是源自于_declare_box。
u 第1个参数填写内存池的首地址。
u 第2个参数填写内存池的大小,单位字节。
u 第3个参数填写内存块大小,单位字节。
使用这个函数要注意以下问题:
1.   强烈建议跟函数_declare_box一起使用。用户不要自己去初始化这个函数,用_declare_box声明的才是最保险的。
2.   如果用户没有使用函数_declare_box进行定义,那么要保证内存池首地址是4字节对齐的。
3.   如果用户没有使用函数_declare_box进行定义,那么要保证内存池的大小box_size至少有12个字节,因为这个12个字节是用于将内存块做成指针链表,方便动态申请和释放。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*         函 数 名: AppObjCreate
*         功能说明: 创建任务通信机制
*         形    参: 无
*         返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{      
/* 初始化内存池,4字节对齐,*/
_init_box (mpool, sizeof (mpool), PoolPerBlockSize);
}

18.2.4 函数_init_box8


函数原型:
int _init_box(
    void* box_mem,       /* 内存池首地址 */
    U32   box_size,      /* 内存池大小,单位字节 */
    U32   blk_size );    /* 内存块大小,单位字节 */
函数描述:
函数_init_box8用于内存池的初始化,初始化时用到的参数都是源自于_declare_box8。
u 第1个参数填写内存池的首地址。
u 第2个参数填写内存池的大小,单位字节。
u 第3个参数填写内存块大小,单位字节。
使用这个函数要注意以下问题:
1.   强烈建议跟函数_declare_box8一起使用。用户不要自己去初始化这个函数,用_declare_box声明的才是最保险的。
2.   如果用户没有使用函数_declare_box8进行定义,那么要保证内存池首地址是8字节对齐的。
3.   如果用户没有使用函数_declare_box8进行定义,那么要保证内存池的大小box_size至少有16个字节,因为这个16个字节是用于将内存块做成指针链表,方便动态申请和释放。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box8(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*         函 数 名: AppObjCreate
*         功能说明: 创建任务通信机制
*         形    参: 无
*         返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{      
/* 初始化内存池,4字节对齐,*/
_init_box8 (mpool, sizeof (mpool), PoolPerBlockSize);
}

18.2.5 函数_alloc_box


函数原型:
void *_alloc_box (
    void* box_mem );    /* 内存池的首地址 */
函数描述:
函数_alloc_box用于从首地址是box_mem的内存池中申请一个内存块。
u 第1个参数填写内存池的首地址。
使用这个函数要注意以下问题:
1.   调用此函数前,一定要使用函数_init_box或者_init_box8进行初始化。
2.   函数_alloc_box支持重入,而且是线程安全的,也即是说用户可以没有限制的在主函数和中断中调用此函数。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*         函 数 名: AppObjCreate
*         功能说明: 创建任务通信机制
*         形    参: 无
*         返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{      
/* 初始化内存池,4字节对齐,*/
_init_box (mpool, sizeof (mpool), PoolPerBlockSize);
}


/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

18.2.6 函数_free_box


函数原型:
int _free_box (
    void* box_mem,    /* 内存池首地址 */
    void* box );      /* 要释放的内存块首地址 */
函数描述:
函数_free_box用于释放使用函数_alloc_box申请的内存块。
u 第1个参数填写内存池的首地址。
u 第2个参数填写要释放的内存块首地址。
使用这个函数要注意以下问题:
1.   此函数的第二个参数必须要填写正确,也就是用户使用的时候最好配套_alloc_box一起使用。
2.   函数_alloc_box支持重入,而且是线程安全的,也即是说用户可以没有限制的在主函数和中断中调用此函数。
使用举例:
#include <rtl.h>

#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*         函 数 名: AppObjCreate
*         功能说明: 创建任务通信机制
*         形    参: 无
*         返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{      
/* 初始化内存池,4字节对齐,*/
_init_box (mpool, sizeof (mpool), PoolPerBlockSize);
}


/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

席萌0209 发表于 2016-1-6 15:32:42

18.3      实验例程说明(4字节对齐)

18.3.1   STM32F103开发板实验

配套例子:
    V4-417_RTX实验_内存管理(4字节对齐)
实验目的:
    1.   学习RTX的内存管理(4字节对齐)。
    2.   函数_declare_box和_init_box用于4字节对齐的内存池初始化。这里4字节对齐的含义
      a.内存池的首地址是4字节对齐(4字节对齐就是地址对4求余等于0)。
      b.每个内存块大小也得是4字节的倍数,这样才能保证每申请到的一个内存块的首地址也是4字节对齐。
      c.4字节对齐是由RTX系统完成的。
实验内容:
    1. K1按键按下,串口打印。
    2. K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据。
    3. K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据。
    4. 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据。
    5. 各个任务实现的功能如下:
      AppTaskUserIF任务   :按键消息处理。
      AppTaskLED任务   :LED闪烁。
      AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
      AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
    RTX配置向导详情如下:

uTask Configuration
l Number of concurrent running tasks
    允许创建4个任务,实际创建了如下四个任务:
                  AppTaskUserIF任务   :按键消息处理。
                  AppTaskLED任务   :LED闪烁。
                  AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                  AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

程序设计:
u任务栈大小分配:
    staticuint64_t AppTaskUserIFStk;   /* 任务栈 */
    staticuint64_t AppTaskLEDStk;      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk;/* 任务栈 */
    staticuint64_t AppTaskStartStk;   /* 任务栈 */
      将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u系统栈大小分配:

uRTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
   /* 初始化外设 */
   bsp_Init();
   
   /* 创建启动任务 */
   os_sys_init_user (AppTaskStart,             /* 任务函数 */
                     4,                        /* 任务优先级 */
                     &AppTaskStartStk,         /* 任务栈 */
                     sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
   while(1);
}
uRTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
   HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
   HandleTaskLED = os_tsk_create_user(AppTaskLED,            /* 任务函数 */
                                        2,                     /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));/* 任务栈大小,单位字节数 */
   
   HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}
u内存管理的初始化和消息邮箱的创建:
#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个支持10个消息的消息邮箱 */
os_mbx_declare (mailbox, 10);

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
   /* 创建消息邮箱 */
      os_mbx_init (&mailbox, sizeof(mailbox));
   
   /* 初始化内存池,4字节对齐,*/
   _init_box (mpool, sizeof (mpool), PoolPerBlockSize);
}
u四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint16_t *pusMsg;
   uint32_t *pulMsg;
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                     printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\\r\\n");
                     break;

                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K3:
                     /* 申请一个内存块用于16位整形变量 */
                     pusMsg= _alloc_box (mpool);
                     *pusMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pusMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K3键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pusMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K3键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pusMsg);
                     }
                     break;
                  
                   /* 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_OK:
                     /* 申请一个内存块用于32位整形变量 */
                     pulMsg= _alloc_box (mpool);
                     *pulMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pulMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pulMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
   const uint16_t usFrequency = 200; /* 延迟周期 */
   
   /* 设置延迟周期 */
   os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的消息邮箱数据
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
   uint8_t *pMsg;
   OS_RESULT xResult;
   const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_mbx_wait(&mailbox, (void *)&pMsg, usMaxBlockTime);
      
         switch (xResult)
         {
            /* 无需等待接受到消息邮箱数据 */
            case OS_R_OK:
                   printf("无需等待接受到消息邮箱数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据 */
            case OS_R_MBX:
                   printf("因为消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 超时 */
            case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
            /* 其他值不处理 */
            default:                  
                   break;
         }   
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskStart
*    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 4
*********************************************************************************************************
*/
__task void AppTaskStart(void)
{
   /* 创建任务 */
   AppTaskCreate();
   
   /* 创建任务通信机制 */
   AppObjCreate();
   
    while(1)
    {
         /* 按键扫描 */
         bsp_KeyScan();
      os_dly_wait(10);
    }
}

18.3.2    STM32F407开发板实验

配套例子:
V5-417_RTX实验_内存管理(4字节对齐)
实验目的:
    1.   学习RTX的内存管理(4字节对齐)。
    2.   函数_declare_box和_init_box用于4字节对齐的内存池初始化。这里4字节对齐的含义
      a.内存池的首地址是4字节对齐(4字节对齐就是地址对4求余等于0)。
      b.每个内存块大小也得是4字节的倍数,这样才能保证每申请到的一个内存块的首地址也是4字节对齐。
      c.4字节对齐是由RTX系统完成的。
实验内容:
    1. K1按键按下,串口打印。
    2. K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据。
    3. K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据。
    4. 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据。
    5. 各个任务实现的功能如下:
      AppTaskUserIF任务   :按键消息处理。
      AppTaskLED任务   :LED闪烁。
      AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
      AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
    RTX配置向导详情如下:
uTask Configuration
l Number of concurrent running tasks
   允许创建4个任务,实际创建了如下四个任务:
                  AppTaskUserIF任务   :按键消息处理。
                  AppTaskLED任务   :LED闪烁。
                  AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                  AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

程序设计:
u任务栈大小分配:
    staticuint64_t AppTaskUserIFStk;   /* 任务栈 */
    staticuint64_t AppTaskLEDStk;      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk;/* 任务栈 */
    staticuint64_t AppTaskStartStk;   /* 任务栈 */
      将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u系统栈大小分配:

uRTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
   /* 初始化外设 */
   bsp_Init();
   
   /* 创建启动任务 */
   os_sys_init_user (AppTaskStart,             /* 任务函数 */
                     4,                        /* 任务优先级 */
                     &AppTaskStartStk,         /* 任务栈 */
                     sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
   while(1);
}
uRTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
   HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
   HandleTaskLED = os_tsk_create_user(AppTaskLED,            /* 任务函数 */
                                        2,                     /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));/* 任务栈大小,单位字节数 */
   
   HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}
u内存管理的初始化和消息邮箱的创建:
#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个支持10个消息的消息邮箱 */
os_mbx_declare (mailbox, 10);

/* 声明一个内存池,10块,每块大小8字节 */
_declare_box(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
   /* 创建消息邮箱 */
      os_mbx_init (&mailbox, sizeof(mailbox));
   
   /* 初始化内存池,4字节对齐,*/
   _init_box (mpool, sizeof (mpool), PoolPerBlockSize);
}
u四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint16_t *pusMsg;
   uint32_t *pulMsg;
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                     printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\\r\\n");
                     break;

                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                  case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K3:
                     /* 申请一个内存块用于16位整形变量 */
                     pusMsg= _alloc_box (mpool);
                     *pusMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pusMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K3键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pusMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K3键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pusMsg);
                     }
                     break;
                  
                   /* 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_OK:
                     /* 申请一个内存块用于32位整形变量 */
                     pulMsg= _alloc_box (mpool);
                     *pulMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                        {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pulMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pulMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
   const uint16_t usFrequency = 200; /* 延迟周期 */
   
   /* 设置延迟周期 */
   os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的消息邮箱数据
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
   uint8_t *pMsg;
   OS_RESULT xResult;
   const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_mbx_wait(&mailbox, (void *)&pMsg, usMaxBlockTime);
      
         switch (xResult)
         {
            /* 无需等待接受到消息邮箱数据 */
            case OS_R_OK:
                   printf("无需等待接受到消息邮箱数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据 */
            case OS_R_MBX:
                   printf("因为消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 超时 */
            case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
            /* 其他值不处理 */
            default:                  
                   break;
         }   
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskStart
*    功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 4
*********************************************************************************************************
*/
__task void AppTaskStart(void)
{
   /* 创建任务 */
   AppTaskCreate();
   
   /* 创建任务通信机制 */
   AppObjCreate();
   
    while(1)
    {
         /* 按键扫描 */
         bsp_KeyScan();
      os_dly_wait(10);
    }

席萌0209 发表于 2016-1-6 15:39:24

18.4    实验例程说明(8字节对齐)

18.4    实验例程说明(8字节对齐)

18.4.1   STM32F103开发板实验

配套例子:
V4-418_RTX实验_内存管理(8字节对齐)
实验目的:
    1.   学习RTX的内存管理(8字节对齐)。
    2.   函数_declare_box8和_init_box8用于8字节对齐的内存池初始化。这里8字节对齐的含义:
      a.   内存池的首地址是8字节对齐(8字节对齐就是地址对8求余等于0)。
      b.    每个内存块大小也得是8字节的倍数,这样才能保证每申请到的一个内存块的首地址也是8字节对齐。
      c.   8字节对齐是由RTX系统完成的。
实验内容:
    1.K1按键按下,串口打印。
    2.K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据。
    3.K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据。
    4.摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据。
    5.摇杆UP键按下,申请一个内存块用于64位整形变量,然后向消息邮箱发送数据。
    6.各个任务实现的功能如下:
      AppTaskUserIF任务   :按键消息处理。
      AppTaskLED任务   :LED闪烁。
      AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
      AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
    RTX配置向导详情如下:

uTask Configuration
l Number of concurrent running tasks
    允许创建4个任务,实际创建了如下四个任务:
                  AppTaskUserIF任务   :按键消息处理。
                  AppTaskLED任务   :LED闪烁。
                  AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                  AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

程序设计:
u任务栈大小分配:
   staticuint64_t AppTaskUserIFStk;   /* 任务栈 */
   staticuint64_t AppTaskLEDStk;      /* 任务栈 */
   staticuint64_t AppTaskMsgProStk;/* 任务栈 */
   staticuint64_t AppTaskStartStk;   /* 任务栈 */
      将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u系统栈大小分配:

uRTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
   /* 初始化外设 */
   bsp_Init();
   
   /* 创建启动任务 */
   os_sys_init_user (AppTaskStart,             /* 任务函数 */
                     4,                        /* 任务优先级 */
                     &AppTaskStartStk,         /* 任务栈 */
                     sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
   while(1);
}
uRTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
   HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
   HandleTaskLED = os_tsk_create_user(AppTaskLED,            /* 任务函数 */
                                        2,                     /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));/* 任务栈大小,单位字节数 */
   
   HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}
u内存管理的初始化和消息邮箱的创建:
#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个支持10个消息的消息邮箱 */
os_mbx_declare (mailbox, 10);

/* 声明一个内存池,10个块,每块大小8字节 */
_declare_box8(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
   /* 创建消息邮箱 */
      os_mbx_init (&mailbox, sizeof(mailbox));
   
   /* 初始化内存池,8字节对齐,*/
   _init_box8 (mpool, sizeof (mpool), PoolPerBlockSize);
}
u四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint16_t *pusMsg;
   uint32_t *pulMsg;
   uint64_t *pullMsg;
   
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                     printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\\r\\n");
                     break;

                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K3:
                     /* 申请一个内存块用于16位整形变量 */
                     pusMsg= _alloc_box (mpool);
                     *pusMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pusMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K3键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pusMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K3键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pusMsg);
                     }
                     break;
                  
                  /* 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_OK:
                     /* 申请一个内存块用于32位整形变量 */
                     pulMsg= _alloc_box (mpool);
                     *pulMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pulMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pulMsg);
                     }
                     break;

                   /* 摇杆UP键按下,申请一个内存块用于64位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_U:
                     /* 申请一个内存块用于32位整形变量 */
                     pullMsg= _alloc_box (mpool);
                     *pullMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pullMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pullMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
   const uint16_t usFrequency = 200; /* 延迟周期 */
   
   /* 设置延迟周期 */
   os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的消息邮箱数据
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
   uint8_t *pMsg;
   OS_RESULT xResult;
   const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_mbx_wait(&mailbox, (void *)&pMsg, usMaxBlockTime);
      
         switch (xResult)
         {
            /* 无需等待接受到消息邮箱数据 */
            case OS_R_OK:
                   printf("无需等待接受到消息邮箱数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据 */
            case OS_R_MBX:
                   printf("因为消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 超时 */
            case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
            /* 其他值不处理 */
            default:                  
                   break;
         }   
    }
}

18.4.2 STM32F407开发板实验

配套例子:
    V5-418_RTX实验_内存管理(8字节对齐)
实验目的:
    1.   学习RTX的内存管理(8字节对齐)。
    2.   函数_declare_box8和_init_box8用于8字节对齐的内存池初始化。这里8字节对齐的含义:
      a.   内存池的首地址是8字节对齐(8字节对齐就是地址对8求余等于0)。
      b.    每个内存块大小也得是8字节的倍数,这样才能保证每申请到的一个内存块的首地址也是8字节对齐。
      c.   8字节对齐是由RTX系统完成的。
实验内容:
    1.K1按键按下,串口打印。
    2.K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据。
    3.K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据。
    4.摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据。
    5.摇杆UP键按下,申请一个内存块用于64位整形变量,然后向消息邮箱发送数据。
    6.各个任务实现的功能如下:
      AppTaskUserIF任务   :按键消息处理。
      AppTaskLED任务   :LED闪烁。
      AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
      AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
    RTX配置向导详情如下:
uTask Configuration
l Number of concurrent running tasks
    允许创建4个任务,实际创建了如下四个任务:
                  AppTaskUserIF任务   :按键消息处理。
                     AppTaskLED任务   :LED闪烁。
                  AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的消息邮箱数据。
                  AppTaskStart任务    :启动任务,也是最高优先级任务,这里实现按键扫描。
l Number of tasks with user-provided stack
    创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:

程序设计:
u任务栈大小分配:
    staticuint64_t AppTaskUserIFStk;   /* 任务栈 */
    staticuint64_t AppTaskLEDStk;      /* 任务栈 */
    staticuint64_t AppTaskMsgProStk;/* 任务栈 */
    staticuint64_t AppTaskStartStk;   /* 任务栈 */
      将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。
u系统栈大小分配:

uRTX初始化:
/*
*********************************************************************************************************
*    函 数 名: main
*    功能说明: 标准c程序入口。
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
int main (void)
{   
   /* 初始化外设 */
   bsp_Init();
   
   /* 创建启动任务 */
   os_sys_init_user (AppTaskStart,             /* 任务函数 */
                     4,                        /* 任务优先级 */
                     &AppTaskStartStk,         /* 任务栈 */
                     sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */
   while(1);
}
uRTX任务创建:
/*
*********************************************************************************************************
*    函 数 名: AppTaskCreate
*    功能说明: 创建应用任务
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
   HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF,             /* 任务函数 */
                                           1,                         /* 任务优先级 */
                                           &AppTaskUserIFStk,         /* 任务栈 */
                                           sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */
   
   HandleTaskLED = os_tsk_create_user(AppTaskLED,            /* 任务函数 */
                                        2,                     /* 任务优先级 */
                                        &AppTaskLEDStk,          /* 任务栈 */
                                        sizeof(AppTaskLEDStk));/* 任务栈大小,单位字节数 */
   
   HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro,             /* 任务函数 */
                                           3,                         /* 任务优先级 */
                                           &AppTaskMsgProStk,         /* 任务栈 */
                                           sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */
}
u内存管理的初始化和消息邮箱的创建:
#define PoolBlocks         10
#define PoolPerBlockSize   8

/* 声明一个支持10个消息的消息邮箱 */
os_mbx_declare (mailbox, 10);

/* 声明一个内存池,10个块,每块大小8字节 */
_declare_box8(mpool, PoolPerBlockSize, PoolBlocks);

/*
*********************************************************************************************************
*    函 数 名: AppObjCreate
*    功能说明: 创建任务通信机制
*    形    参: 无
*    返 回 值: 无
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
   /* 创建消息邮箱 */
      os_mbx_init (&mailbox, sizeof(mailbox));
   
   /* 初始化内存池,8字节对齐,*/
   _init_box8 (mpool, sizeof (mpool), PoolPerBlockSize);
}
u四个RTX任务的实现:
/*
*********************************************************************************************************
*    函 数 名: AppTaskUserIF
*    功能说明: 按键消息处理   
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 1(数值越小优先级越低,这个跟uCOS相反)
*********************************************************************************************************
*/
__task void AppTaskUserIF(void)
{
   uint8_t*pucMsg;
   uint16_t *pusMsg;
   uint32_t *pulMsg;
   uint64_t *pullMsg;
   
   uint8_t ucKeyCode, ucCount = 0;

    while(1)
    {
         ucKeyCode = bsp_GetKey();
      
         if (ucKeyCode != KEY_NONE)
         {
            switch (ucKeyCode)
            {
                   /* K1键按下,打印调试说明 */
                   case KEY_DOWN_K1:
                     printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\\r\\n");
                     break;

                   /* K2键按下,申请一个内存块用于8位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K2:
                     /* 申请一个内存块用于8位整形变量 */
                     pucMsg= _alloc_box (mpool);
                     *pucMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pucMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K2键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pucMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K2键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pucMsg);
                     }
                     break;

                   /* K3键按下,申请一个内存块用于16位整形变量,然后向消息邮箱发送数据 */
                   case KEY_DOWN_K3:
                     /* 申请一个内存块用于16位整形变量 */
                     pusMsg= _alloc_box (mpool);
                     *pusMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pusMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("K3键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pusMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("K3键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pusMsg);
                     }
                     break;
                  
                  /* 摇杆OK键按下,申请一个内存块用于32位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_OK:
                     /* 申请一个内存块用于32位整形变量 */
                     pulMsg= _alloc_box (mpool);
                     *pulMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pulMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pulMsg);
                     }
                     break;

                   /* 摇杆UP键按下,申请一个内存块用于64位整形变量,然后向消息邮箱发送数据 */
                   case JOY_DOWN_U:
                     /* 申请一个内存块用于32位整形变量 */
                     pullMsg= _alloc_box (mpool);
                     *pullMsg = ucCount++;
                  
                     /* 向消息邮箱发数据,如果消息邮箱满了,等待100个时钟节拍 */
                     if(os_mbx_send (&mailbox, pulMsg, 100) != OS_R_OK)
                     {
                            /* 发送失败,即使等待了100个时钟节拍 */
                            printf("摇杆OK键按下,向消息邮箱发送数据失败,即使等待了100个时钟节拍\\r\\n");
                            _free_box (mpool, pullMsg);
                     }
                     else
                     {
                            /* 发送成功 */
                            printf("摇杆OK键按下,向消息邮箱发送数据成功\\r\\n");   
                            _free_box (mpool, pullMsg);
                     }
                     break;

                   /* 其他的键值不处理 */
                   default:                  
                     break;
            }
         }
      
         os_dly_wait(20);
   }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskLED
*    功能说明: LED闪烁。
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 2
*********************************************************************************************************
*/
__task void AppTaskLED(void)
{
   const uint16_t usFrequency = 200; /* 延迟周期 */
   
   /* 设置延迟周期 */
   os_itv_set(usFrequency);
   
    while(1)
    {
         bsp_LedToggle(2);
         bsp_LedToggle(3);

         /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
         os_itv_wait();
    }
}

/*
*********************************************************************************************************
*    函 数 名: AppTaskMsgPro
*    功能说明: 消息处理,等待任务AppTaskUserIF发来的消息邮箱数据
*    形    参: 无
*    返 回 值: 无
*   优 先 级: 3
*********************************************************************************************************
*/
__task void AppTaskMsgPro(void)
{
   uint8_t *pMsg;
   OS_RESULT xResult;
   const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
   
    while(1)
    {
         xResult = os_mbx_wait(&mailbox, (void *)&pMsg, usMaxBlockTime);
      
         switch (xResult)
         {
            /* 无需等待接受到消息邮箱数据 */
            case OS_R_OK:
                   printf("无需等待接受到消息邮箱数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据 */
            case OS_R_MBX:
                   printf("因为消息邮箱空,usMaxBlockTime等待时间从消息邮箱内获得数据,pMsg = %d\\r\\n", *pMsg);
                   break;

            /* 超时 */
            case OS_R_TMO:
                   bsp_LedToggle(1);
                   bsp_LedToggle(4);
                   break;
            
            /* 其他值不处理 */
            default:                  
                   break;
         }   
    }
}

席萌0209 发表于 2016-1-6 15:41:10

18.5 总结

    本章节主要为大家讲解了RTX内存管理的实现方法,关于嵌入式内存管理的方案还有很多,有兴趣的同学可以查阅相关资料了解一下。

xhrain 发表于 2021-12-23 12:38:57

席萌0209 发表于 2016-1-6 15:41
18.5 总结

    本章节主要为大家讲解了RTX内存管理的实现方法,关于嵌入式内存管理的方案还有很多,有兴趣 ...

楼主好,有几个问题问一下。
怎么确定RTX是4字节对齐还是8字节对齐?
4或者8字节对齐是通过配置文件什么配置的吗?
RTX5是不是8字节对齐的?

eric2013 发表于 2021-12-26 10:18:04

xhrain 发表于 2021-12-23 12:38
楼主好,有几个问题问一下。
怎么确定RTX是4字节对齐还是8字节对齐?
4或者8字节对齐是通过配置文件什 ...

本帖的动态内存分配是类似数组方式的,需要8字节对齐,有专门的API
页: [1]
查看完整版本: 【RTX操作系统教程】第18章    内存管理