MQX--开篇之MQX简介、初始化、任务

        先简单介绍一下MQX:消息队列执行(Message Queue eXecutive,MQX)是Precise Software Technologies公司1989年开发的一款嵌入式实时操作系统。2009年飞思卡尔收购后,开放源代码。MQX由核心组件(必选)和可选性组件构成。如下图所示:

          

                下面直接上手开始使用MQX,这样学习起来更快。准备工作如下:

           硬件:Freescale FRDM-K64F开发板一块,MicroUSB线一根

           软件:IAR或者Keil,FSLMQXOS_4_1_0_FRDMK64F_GA.exe,驱动,软件安装很简单,就不再介绍了。

           

           在MQX安装目录中,进入<install_dir>\build\frdmk64f\iar,打开build_libs.eww,然后点击Project-Batch build,

                                       


     编译完之后,然后选择Project-Add Existing Project,找到<install_dir>\mqx\examples\hello\build\iar\hello_frdmk64f\hello_frdmk64f.ewp ,然后编译,之后将生成的bin文件下载到开发板中,使用串口调试助手,可以观察到如下结果:



          下面分析下这个例子。

           从main函数开始着手:

           

int main
   (
      void
   )
{ /* Body */

   extern const MQX_INITIALIZATION_STRUCT MQX_init_struct;

   /* Start MQX */
   _mqx( (MQX_INITIALIZATION_STRUCT_PTR) &MQX_init_struct );
   return 0;

} /* Endbody */

   main函数调用了_mqx() 函数,该函数以MQX初始化结构为参数。基于这一结构中的参数,MQX
完成如下任务:
       --------载入并初始化MQX经常使用的数据,包括默认内存池,就绪队列,中断堆栈和任务栈;
       --------初始化硬件(如芯片选择);
       --------开启定时器;
       --------设置默认的时间片值;
       --------生成空闲任务,当没有其它任务就绪时将被激活;
       --------生成由任务模板列表定义为自启动的任务;
       --------开始调度多任务。

   MQX初始化结构中定义应用程序和目标硬件的参数如下:

   

typedef  struct mqx_initialization_struct
{
   /*!
    * \brief Application-unique processor number of the processor.
    *
    * Minimum is 1, maximum is 255. (Processor number 0 is reserved and is used
    * by tasks to indicate their local processor.)
    */
   _mqx_uint       PROCESSOR_NUMBER;

   /*! \brief Lowest address from which MQX allocates dynamic memory and task stacks. */
   void          *START_OF_KERNEL_MEMORY;
   /*!
    * \brief Highest address from which MQX allocates dynamic memory and task stacks.
    *
    * It is the application's responsibility to allocate enough memory for all
    * tasks.
    */
   void          *END_OF_KERNEL_MEMORY;

   /*!
    * \brief The size of the interrupt stack.
    *
    * This is the maximum amount of stack space used by all interrupt handlers.
    *
    */
   _mqx_uint       INTERRUPT_STACK_SIZE;

   /*! \brief Pointer to the task template list for the processor.
    *
    * The default name for the list is MQX_template_list[].
    */
   TASK_TEMPLATE_STRUCT_PTR TASK_TEMPLATE_LIST;

   /*!
    * \brief The maximum hardware interrupt priority level of MQX.
    *
    * All tasks and interrupts run at lower priority (Applicable to CPUs with
    * multiple interrupt levels only).
    */
   _mqx_uint       MQX_HARDWARE_INTERRUPT_LEVEL_MAX;

   /*! \brief Maximum number of message pools. */
   _mqx_uint       MAX_MSGPOOLS;

   /*!
    * \brief Maximum number of message queues. Minimum is MSGQ_FIRST_USER_QUEUE,
    * maximum is 255.
    */
   _mqx_uint       MAX_MSGQS;

   /*!
    * \brief A string indicating which device is to be used as
    * the default (console) I/O for the processor.
    */
   char          *IO_CHANNEL;

   /*! \brief The open flags to pass to the default I/O device */
   char          *IO_OPEN_MODE;

   /* reserved fields for later use */
/*   _mqx_uint       RESERVED[2];*/

   /*! \brief The location of the interrupt stack (if not NULL). */
   void          *INTERRUPT_STACK_LOCATION;
   /*! \brief Reserved field.
    *
    * Reserved for future enhancements to MQX; each element of the array must be
    * initialized to 0.
    */
   _mqx_uint       RESERVED[1];

#if MQX_ENABLE_USER_MODE
   /*! \brief Lowest adress of kernel area. */
   void            *START_OF_KERNEL_AREA;
   /*! \brief Highest adress of kernel area. */
   void            *END_OF_KERNEL_AREA;

   /*! \brief Lowest adress of default data section (.data .bss). */
   void            *START_OF_USER_DEFAULT_MEMORY;
   /*! \brief Highest adress of default data section (.data .bss). */
   void            *END_OF_USER_DEFAULT_MEMORY;

   /*! \brief Lowest adress of user heap - mem alloc. */
   void            *START_OF_USER_HEAP;
   /*! \brief Highest adress of user heap - mem alloc. */
   void            *END_OF_USER_HEAP;

   /*! \brief Lowest adress of user read/write memory. */
   void            *START_OF_USER_RW_MEMORY;
   /*! \brief Highest adress of user read/write memory. */
   void            *END_OF_USER_RW_MEMORY;

   /*! \brief Lowest adress of user read-only memory. */
   void            *START_OF_USER_RO_MEMORY;
   /*! \brief Highest adress of user read-only memory. */
   void            *END_OF_USER_RO_MEMORY;

   /*! \brief Lowest adress of user no access memory. */
   void            *START_OF_USER_NO_MEMORY;
   /*! \brief Highest adress of user no access memory. */
   void            *END_OF_USER_NO_MEMORY;
   /*! \brief Maximal priority of user's tasks. */
   _mqx_uint        MAX_USER_TASK_PRIORITY;
   /*! \brief Maximal number of user's tasks. */
   _mqx_uint        MAX_USER_TASK_COUNT;
#endif

} MQX_INITIALIZATION_STRUCT, * MQX_INITIALIZATION_STRUCT_PTR;

   main()函数的实参MQX_init_struct的定义如下:

const MQX_INITIALIZATION_STRUCT  MQX_init_struct =
{
   /* PROCESSOR_NUMBER                */  BSP_DEFAULT_PROCESSOR_NUMBER,
   /* START_OF_KERNEL_MEMORY          */  BSP_DEFAULT_START_OF_KERNEL_MEMORY,
   /* END_OF_KERNEL_MEMORY            */  BSP_DEFAULT_END_OF_KERNEL_MEMORY,
   /* INTERRUPT_STACK_SIZE            */  BSP_DEFAULT_INTERRUPT_STACK_SIZE,
   /* TASK_TEMPLATE_LIST              */  MQX_template_list,
   /* MQX_HARDWARE_INTERRUPT_LEVEL_MAX*/  BSP_DEFAULT_MQX_HARDWARE_INTERRUPT_LEVEL_MAX,
   /* MAX_MSGPOOLS                    */  BSP_DEFAULT_MAX_MSGPOOLS,
   /* MAX_MSGQS                       */  BSP_DEFAULT_MAX_MSGQS,
   /* IO_CHANNEL                      */  BSP_DEFAULT_IO_CHANNEL,
   /* IO_OPEN_MODE                    */  BSP_DEFAULT_IO_OPEN_MODE,
   0,
   0,
#if MQX_ENABLE_USER_MODE   
   BSP_DEFAULT_START_OF_KERNEL_AREA,
   BSP_DEFAULT_END_OF_KERNEL_AREA,
   
   BSP_DEFAULT_START_OF_USER_DEFAULT_MEMORY,
   BSP_DEFAULT_END_OF_USER_DEFAULT_MEMORY,
   
   BSP_DEFAULT_START_OF_USER_HEAP,
   BSP_DEFAULT_END_OF_USER_HEAP,
   
   BSP_DEFAULT_START_OF_USER_RW_MEMORY,
   BSP_DEFAULT_END_OF_USER_RW_MEMORY,
   
   BSP_DEFAULT_START_OF_USER_RO_MEMORY,
   BSP_DEFAULT_END_OF_USER_RO_MEMORY,
   
   BSP_DEFAULT_START_OF_USER_NO_MEMORY,
   BSP_DEFAULT_END_OF_USER_NO_MEMORY,

   BSP_DEFAULT_MAX_USER_TASK_PRIORITY,
   BSP_DEFAULT_MAX_USER_TASK_COUNT,
#endif
};

   重点看一下上述结构的第五个参数任务模板列表,

typedef struct task_template_struct 
{ 
_mqx_uint TASK_TEMPLATE_INDEX; 
void _CODE_PTR_ TASK_ADDRESS)(uint_32); 
_mem_size TASK_STACKSIZE; 
_mqx_uint TASK_PRIORITY; 
char _PTR_ TASK_NAME; 
_mqx_uint TASK_ATTRIBUTES; 
uint_32 CREATION_PARAMETER; 
_mqx_uint DEFAULT_TIME_SLICE; 
} TASK_TEMPLATE_STRUCT, _PTR_ TASK_TEMPLATE_STRUCT_PTR;
  对于本例子,任务模板列表如下:

const TASK_TEMPLATE_STRUCT  MQX_template_list[] = 
{ 
    /* Task Index,   Function,   Stack,  Priority, Name,     Attributes,          Param, Time Slice */
    { HELLO_TASK,   hello_task, 1500,   8,        "hello",  MQX_AUTO_START_TASK, 0,     0 },
    { 0 }
};
   
    对于本例子,打印Hello World是一个自启动任务,该任务优先级为8,hell_task()函数是任务的入口,栈的大小为1500.




      上面的例子是在MQX初始化时自启动任务,MQX同样可以在应用程序运行时生成、管理或终止任务。

      找到<install_dir>\mqx\examples\hello2\build\iar\hello2_frdmk64f\hello_frdmk64f.ewp ,这个例子的相关代码如下:

const TASK_TEMPLATE_STRUCT  MQX_template_list[] = 
{  
   /* Task Index,   Function,   Stack,  Priority, Name,     Attributes,          Param, Time Slice */
    { WORLD_TASK,   world_task, 1000,   9,        "world",  MQX_AUTO_START_TASK, 0,     0 },
    { HELLO_TASK,   hello_task, 1000,   8,        "hello",  0,                   0,     0 },
    { 0 }
};

/*TASK*-----------------------------------------------------
* 
* Task Name    : world_task
* Comments     :
*    This task creates hello_task and then prints " World ".
*
*END*-----------------------------------------------------*/

void world_task
   (
      uint32_t initial_data
   )
{
   _task_id hello_task_id;

   hello_task_id = _task_create(0, HELLO_TASK, 0);
   if (hello_task_id == MQX_NULL_TASK_ID) {
      printf ("\n Could not create hello_task\n");
   } else {
      printf(" World \n");
   }

   _task_block();

}


/*TASK*-----------------------------------------------------
* 
* Task Name    : hello_task
* Comments     :
*    This task prints " Hello".
*
*END*-----------------------------------------------------*/

void hello_task
   (
      uint32_t initial_data
   )
{

   printf("\n Hello\n");
   _task_block();

}

/* EOF */

编译之后的运行结构如下:

Hello

World

如果修改hello 任务的优先级为9或者更大,那么运行结果是:

World

Hello


当优先级不同时,打印的先后顺序不同,原因是什么呢?

对于第一种情况,即Hello的优先级高于World时,当调用_task_create()时,Hello任务变成Ready状态,由于它的优先级高于World,所以它变为Active(这时Hello任务变为Ready状态),这样它就会打印输出Hello,之后它变为Blocked状态,然后World_task任务将由Ready变为Active状态并检查hello_task任务是否成功创建。如果是,world_task将输出World;否则,world_task将输出一个错
误信息。最后world_task 也变为阻塞状态。


对于第二种情况,即Hello的优先级等于或低于World时,当调用_task_create()时,Hello任务变成Ready状态,由于它的优先级等于或低于World,所以它这时无法变为Active。World_task任务这时依然是Active状态,它检查hello_task任务是否成功创建。如果是,world_task将输出World;否则,world_task将输出一个错
误信息,world_task 最后变为阻塞状态。这时处于Ready状态的Hello task变为Active状态,打印出Hello。


MQX中task的四种状态如下:



     做完这个实验之后,我突然想试一下,在第一种情况下输出Hello World之后,让Hello 任务再执行一次,于是我考虑使用_task_ready()函数,本意是让处于blocked状态的Hello task 重新变为Ready 状态并Active。我做的改进是:

/*TASK*-----------------------------------------------------
* 
* Task Name    : world_task
* Comments     :
*    This task creates hello_task and then prints " World ".
*
*END*-----------------------------------------------------*/

void world_task
   (
      uint32_t initial_data
   )
{
   _task_id hello_task_id;

   hello_task_id = _task_create(0, HELLO_TASK, 0);
   if (hello_task_id == MQX_NULL_TASK_ID) {
      printf ("\n Could not create hello_task\n");
   } else {
      printf(" World \n");
   }

   // added by wenxue ,want to use _task_ready()
   _task_ready(_task_get_td(hello_task_id));  //  添加了这么一条语句</span>
   _task_block();

}


/*TASK*-----------------------------------------------------
* 
* Task Name    : hello_task
* Comments     :
*    This task prints " Hello".
*
*END*-----------------------------------------------------*/

void hello_task
   (
      uint32_t initial_data
   )
{

   printf("\n Hello\n");
   _task_block();

}

/* EOF */

但是结果还是只输出Hello World。而不是我原本期望的Hello World Hello. 我反复的尝试与思考,花了几个小时的时间,最后终于找出原因了。

原因是Hello 这个任务只有两条语句,即先打印,然后就让任务block。所以就算让它处于Ready状态,它也什么事情都不做了。

将hello 任务的代码改为:

void hello_task
   (
      uint32_t initial_data
   )
{
  // modified by wenxue,add while(1)
  while(1)
  {
   printf("\n Hello\n");
   _task_block();
  }
}

就可以打印出Hello World Hello 了。



对于第二种情况,代码改动如下:

const TASK_TEMPLATE_STRUCT  MQX_template_list[] = 
{  
   /* Task Index,   Function,   Stack,  Priority, Name,     Attributes,          Param, Time Slice */
    { WORLD_TASK,   world_task, 1000,   9,        "world",  MQX_AUTO_START_TASK, 0,     0 },
    { HELLO_TASK,   hello_task, 1000,   10,        "hello",  0,                   0,     0 },
    { 0 }
};

/*TASK*-----------------------------------------------------
* 
* Task Name    : world_task
* Comments     :
*    This task creates hello_task and then prints " World ".
*
*END*-----------------------------------------------------*/

void world_task
   (
      uint32_t initial_data
   )
{
   _task_id hello_task_id;

   hello_task_id = _task_create(0, HELLO_TASK, 0);
   
   while(1)
   {
   if (hello_task_id == MQX_NULL_TASK_ID) {
      printf ("\n Could not create hello_task\n");
   } else {
      printf(" World \n");
   }


   _task_block();
   }
}


/*TASK*-----------------------------------------------------
* 
* Task Name    : hello_task
* Comments     :
*    This task prints " Hello".
*
*END*-----------------------------------------------------*/

void hello_task
   (
      uint32_t initial_data
   )
{
  // modified by wenxue,add while(1)
   printf("\n Hello\n");
   // added by wenxue ,want to use _task_ready()
   _task_ready(_task_get_td(_task_get_id_from_name("world")));
   _task_block();
  
}

/* EOF */

这样打印的结果是World Hello World


上面的例子其实使用的是MQX默认的调度策略:FIFO,还有另外两种调度策略,分别为轮询和任务队列。这两种策略还需要通过例子学习一下。






       

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值