MQX 实时操作系统用户手册 MC9RS08KA1数据手册:技术数据MC9RS08KA2MC9RS08KA1数据手册 MQXUGZHS第0版2010年7月 飞思卡尔半导体 目录 第一章前言......................................................................................................................................
1 1.1关于MQX.................................................................................................................................
11.2关于本手册..............................................................................................................................
11.3版本3.0和2.50的新特点......................................................................................................
11.4约定..........................................................................................................................................
3 1.4.1提示..................................................................................................................................
31.4.2注释..................................................................................................................................
31.4.3注意..................................................................................................................................
3
第二章MQX概述.............................................................................................................................
4 2.1MQX的组织结构.....................................................................................................................
42.2初始化......................................................................................................................................
52.3任务管理..................................................................................................................................
52.4调度..........................................................................................................................................
62.5存储管理..................................................................................................................................
6 2.5.1可变大小存储块管理.......................................................................................................
62.5.2固定大小存储块管理(区块).......................................................................................
62.5.3高速缓存控制...................................................................................................................
62.5.4存储器管理单元(MMU)控制.....................................................................................62.5.5轻量级存储管理...............................................................................................................
62.6任务同步..................................................................................................................................
72.6.1轻量级事件.......................................................................................................................
72.6.2事件..................................................................................................................................
72.6.3轻量级信号量...................................................................................................................
72.6.4信号量...............................................................................................................................
72.6.5互斥..................................................................................................................................
72.6.6消息..................................................................................................................................
72.6.7任务队列...........................................................................................................................
82.7处理器间通信..........................................................................................................................
82.8定时..........................................................................................................................................
82.8.1时间组件...........................................................................................................................
82.8.2轻量级定时器...................................................................................................................
82.8.3定时器...............................................................................................................................
82.8.4看门狗...............................................................................................................................
92.9中断和异常处理......................................................................................................................
92.10输入/输出(I/O)驱动..........................................................................................................
92.10.1格式化输入/输出...........................................................................................................
9
II
飞思卡尔半导体
2.10.2输入/输出子系统...........................................................................................................
92.11检测工具................................................................................................................................
9 2.11.1日志.................................................................................................................................
92.11.2轻量级日志.....................................................................................................................
92.11.3内核日志.......................................................................................................................
102.11.4栈的运用.......................................................................................................................
102.12出错处理..............................................................................................................................
102.12.1任务出错代码...............................................................................................................
102.12.2异常处理.......................................................................................................................
102.12.3实时测试.......................................................................................................................
102.13队列操纵..............................................................................................................................
112.14命名组件..............................................................................................................................
112.15嵌入式调试..........................................................................................................................
11 第三章使用MQX...........................................................................................................................
12 3.1前言........................................................................................................................................
123.2初始化并开始运行MQX.......................................................................................................
12 3.2.1MQX初始化结构.............................................................................................................
123.2.2任务模板列表.................................................................................................................
133.3使用FREESCALECODEWARRIORDEVELOPMENTSTUDIO......................................................153.4管理任务................................................................................................................................
173.4.1创建任务.........................................................................................................................
183.4.2获取任务IDs...................................................................................................................
183.4.3获取和设置一个任务环境.............................................................................................
183.4.4管理任务错误.................................................................................................................
193.4.5重启任务.........................................................................................................................
193.4.6终止任务.........................................................................................................................
193.4.7实例:创建任务.............................................................................................................
203.5调度任务................................................................................................................................
223.5.1FIFO调度........................................................................................................................
223.5.2轮循调度.........................................................................................................................
223.6内存管理................................................................................................................................
233.6.1使用可变块管理内存.....................................................................................................
233.6.2利用可变大小块管理轻量级内存.................................................................................
253.6.3使用固定大小块管理内存(区块).............................................................................
253.6.4操纵缓冲寄存器.............................................................................................................
273.6.5控制MMU(虚拟存储器)...........................................................................................
283.7任务同步................................................................................................................................
313.7.1事件................................................................................................................................
31 飞思卡尔半导体 III 3.7.2轻量级事件.....................................................................................................................
363.7.3关于信号量类型的对象.................................................................................................
373.7.4轻量级信号量.................................................................................................................
393.7.5信号量.............................................................................................................................
443.7.6互斥................................................................................................................................
513.7.7消息................................................................................................................................
563.7.8任务队列.........................................................................................................................
633.8处理器间的通信....................................................................................................................
663.8.1发送消息到远程处理器.................................................................................................
663.8.2创建和结束远程处理器上的任务.................................................................................
673.8.3访问远程处理器上的事件组.........................................................................................
673.8.4创建和初始化IPC..........................................................................................................
673.8.5消息头的端模式转换.....................................................................................................
753.9定时........................................................................................................................................
763.9.1MQX定时翻转法.............................................................................................................
763.9.2MQX定时精度.................................................................................................................
763.9.3定时器组件.....................................................................................................................
763.9.4定时器.............................................................................................................................
803.9.5轻量级定时器...............................................................................................................
833.9.6看门狗...........................................................................................................................
843.10中断和异常处理................................................................................................................
873.10.1中断处理初始化.........................................................................................................
883.10.2装载应用程序定义的ISR...........................................................................................
883.10.3针对ISR的限制...........................................................................................................
893.10.4修改默认ISR...............................................................................................................
913.10.5异常处理.....................................................................................................................
913.10.6ISR异常处理...............................................................................................................
913.10.7任务异常处理.............................................................................................................
923.10.8举例:装载ISR...........................................................................................................
923.11工具....................................................................................................................................
943.11.1日志.............................................................................................................................
943.11.2轻量级日志.................................................................................................................
983.11.3内核日志.....................................................................................................................
1013.11.4堆栈使用工具..............................................................................................................
1043.12工具....................................................................................................................................
1043.12.1队列.............................................................................................................................
1043.12.2命名组件.....................................................................................................................
1053.12.3实时测试.....................................................................................................................
1063.12.4其它工具.....................................................................................................................
110 IV 飞思卡尔半导体 3.13嵌入式调试........................................................................................................................
1113.14实时编译配置MQX...........................................................................................................
111 3.14.1MQX编译时配置选项.................................................................................................
1123.14.2推荐设置.....................................................................................................................
114 第四章重建MQX.........................................................................................................................
116 4.1为什么要重建MQX?.........................................................................................................
1164.2开始之前..............................................................................................................................
1164.3FREESCALEMQX的目录结构...............................................................................................
116 4.3.1MQXRTOS目录结构....................................................................................................
1174.3.2PSP子目录.....................................................................................................................
1184.3.3BSP子目录.....................................................................................................................
1184.3.4输入/输出(I/O)子目录............................................................................................
1194.3.5其它源子目录...............................................................................................................
1194.4FREESCALEMQX构建工程...................................................................................................
1194.4.1PSP构建工程.................................................................................................................
1194.4.2BSP构建工程.................................................................................................................
1194.4.3构建后处理...................................................................................................................
1194.4.4构建目标.......................................................................................................................
1204.5重建FREESCALEMQXRTOS...............................................................................................
1204.6创建客户MQX配置并构建工程.........................................................................................1204.6.1为什么创建一个新的配置?.......................................................................................
1204.6.2克隆现有配置...............................................................................................................
120 第五章开发一个新的BSP...........................................................................................................
122 5.1什么是BSP?.......................................................................................................................
1225.2开发一个新的BSP...............................................................................................................
1225.3选择一个基准BSP然后开始工作.......................................................................................1225.4和目标板通信......................................................................................................................
1225.5修改BSP特定的包含文件...................................................................................................
122 5.5.1bsp_prv.h........................................................................................................................
1235.5.2bsp.h...............................................................................................................................
1235.5.3target.h...........................................................................................................................
1235.6修改启动代码......................................................................................................................
123p.c............................................................................................................................
1235.6.2p.......................................................................................................................
1245.7修改源代码..........................................................................................................................
1245.7.1init_bsp.c........................................................................................................................
1245.7.2get_usec.c.......................................................................................................................
1255.7.3get_nsec.c.......................................................................................................................
125
飞思卡尔半导体
V
5.7.4mqx_init.c.......................................................................................................................
1255.8为I/O驱动程序创建默认的初始化文件.............................................................................125 5.8.1initdev.c..........................................................................................................................
1255.9支持编译器的文件..............................................................................................................
1255.10构建新的BSP.....................................................................................................................
126 第六章FAQS................................................................................................................................
127 6.1概述......................................................................................................................................
1276.2事件......................................................................................................................................
1276.3全局构造..............................................................................................................................
1276.4空闲任务..............................................................................................................................
1276.5中断......................................................................................................................................
1276.6内存......................................................................................................................................
1286.7信息传递..............................................................................................................................
1286.8互斥......................................................................................................................................
1296.9信号量..................................................................................................................................
1296.10任务退出处理与任务异常处理........................................................................................1296.11任务队列............................................................................................................................
1296.12任务....................................................................................................................................
1306.13时间片................................................................................................................................
1306.14定时器................................................................................................................................
130
VI
飞思卡尔半导体
第一章前言
1.1关于MQX
MQX实时操作系统设计用于单一处理器、多处理器和分布式处理器等形式的嵌入式实时系统。
Freescale半导体公司成功地搭载MQX操作系统软件平台用于ColdFire和PowerPC系列微处理器。
相比于最初发布的MQX软件,FreescaleMQX软件更易于配置和使用。
现在单一发布版本就包含了MQX操作系统外加其它所有软件组件来支持特定的微处理器。
有关FreescaleMQX的发布版本的详细说明如下。
FreescaleMQX本文档将以“FreescaleMQX”作为本软件的标识。
MQX是一个运行时函数库,程序用它来实现实时多任务应用。
其主要特征为:大小可裁剪、面向组件的架构和便于使用。
MQX支持多处理器应用,并且可用于灵活配置嵌入式输入/输出产品,如网络、数据通讯和文档管理等。
本手册通篇都使用MQX作为MQX操作系统的缩写。
1.2关于本手册
使用本手册时需要参照:zMQX参考手册——包含MQX简单和复杂的数据类型,按字母顺序排列的MQX函数原型。
FreescaleMQX
FreescaleMQX发布版本还包含其它基于MQX操作系统的软件产品。
参见RTCSTCP/IP栈、USB主机开发套件、USB设备开发套件、MFS文件系统等用户指南和参考手册。
1.3版本3.0和2.50的新特点
FreescaleMQX
为了延续最初MQX发布版本的编号方式,FreescaleMQX发布版本第一版编号为3.0。
尽管主版本编号改变,MQX并没有主要特性改变,与2.50版本兼容。
关于FreescaleMQXRTOS新特性的最新信息,请参见相关版本的发布文档。
飞思卡尔半导体
1
FreescaleMQXRTOS版本3.0相比MQX版本2.50具有如下改进:zFreescaleMQXRTOS版本包含MFS、RTCS、USB等MQX关键组件。
zFreescaleMQXRTOS与其它MQX组件的默认开发环境是CodeWarriorDevelopment
Studio。
新版本还将支持其它开发环境。
z现在所有关键FreescaleMQXRTOS组件(PSP、BSP、RTCS、MFS、USB、...)的实时
编译配置都由编辑user_config.h文件完成,存放于顶层配置文件夹(config)下的板级相关路径。
而在以前的版本中,用户配置的宏通过命令行下的makefile或者CodeWarrior下的预处理文件传入整个编译过程。
zPSP组件是可裁剪的,基于特定的硬件平台。
PSP组件针对特定处理器设备。
PSP代码仍然保持相对的独立性,而且能够对特定的硬件平台进行更好的内核裁剪。
zMQX现在支持类型存储(typedmemory),即允许一些附加信息能在任务调试插件(task‐awaredebuggerplugin)中显示出来。
MQX2.50向下兼容2.40版本,并且具有如下改进:z减少对32位类型的依赖——类型由处理器本身数据位数决定。
例如一个16位的处理器,其一般数据类型大小是16位。
对于小规模架构的处理器降低了相应代码和数据的大小。
z时间由一个时钟滴答(tick)来度量——为允许更高分辨率的延迟、定时和时间测量,MQX用时钟滴答取代秒和毫秒来衡量时间。
延迟一段特定的时间或者直到一个特定的时间为止成为可能。
这一改进适用于所有使用超时功能的组件。
这一内在的改变对于用户来讲是透明的。
另外,MQX加入了一个扩展的日期结构来表示超过24世纪的年历日期,精度可以达到皮秒。
z支持存储管理单元(MMU)——MMU支持任务间的存储保护。
每个任务能建立它自己的被保护数据区域。
z多个内存池——应用程序可以从一个应用自身定义的全局存储区分配内存块,正如之前应用从默认的内存池分配内存块一样。
内存池和MQX版本2.40的区块(partition)类似,但允许用户分配可变大小的存储块。
z轻量级的存储模块——与存储组件类似,作为轻量级的组件,MQX在分配存储块时做较少的检查,且代码和数据量较小。
在分配和释放存储空间时,轻量级存储组件更快,但当一个任务结束时回收存储区域速度较慢。
z能创建处于阻塞状态的任务——应用程序能创建处于阻塞态的任务。
MQX像MQX2.40版本一样创建任务,但是不将其加入就绪队列。
z轻量级事件组——与事件组类似,除了它们更为简单。
z自动清除事件位——如果一个应用程序用自动清除属性创建了一个事件组,MQX在事件位被设置后自动地清除该事件位。
这一特性使得等待事件位的任务变为就绪态,但是该任务不需要清除事件位。
z轻量级信号量支持超时等待和轮询方式。
z任务可以重新启动——应用程序可以从任务的开始函数重新启动任务,且保持同样的任务描述符,任务ID号和任务栈。
z增强的区块组件——区块组件是一个完全注册的MQX组件。
应用程序可以以处理其它MQX组件相同的方式创建和注销这些区块。
2
飞思卡尔半导体
z增强的EDS服务器(用于嵌入式调试)——IPC支持EDS服务器,因此可以跨越一个多处理器MQX网络来进行调试。
z多处理器事件——应用程序能够通过多处理器MQX网路中的另一个处理器设置事件位。
z32位队列ID——应用程序除了用16位表示队列ID外还可以用32位表示它们。
因此,
应用程序能使用大于255的数值来表示队列号和处理器号。
你可以在编译PSP时使能这个选项。
z更快和更紧凑的MQX——应用程序可以使用额外的编译时间配置选项来建立一个更小的MQX且使用新类型的组件(轻量级组件)来处理存储应用和高速应用。
轻量级组件通常使用内存映射的数据结构(没有多处理器功能)。
它们比常规的组件速度更快;但是,它们只有较少的特性和保护机制。
轻量级组件包括:轻量级事件(新)——使用内存映射的数据结构定义事件组。
轻量级日志(新)——由固定大小的条目组成。
内核日志目前为一个轻量级日志。
轻量级存储组件(新)——使用较少的开销来维持大小可变的存储块堆。
MQX能配
置成将轻量级存储组件作为其默认存储组件。
轻量级信号量(lightweightsemaphores)——使用内存映射的数据结构来定义信号量。
轻量级定时器(新)——使用内存映射的数据结构来提供可重复的时间驱动服务。
1.4约定
1.4.1提示
提示指出有用的信息。
提示:从中断服务程序(ISR)中分配消息的最有效方法就是使用_msg_alloc()
1.4.2注释
注释标明重要信息。
注释:非严谨的信号量不支持优先级继承。
1.4.3注意
注意告诉一些命令或程序可能具有不可预期的副作用,或者有可能损坏文件或硬件。
注意:如果你更改了MQX的数据类型,一些来自MQXEmbedded的MQX™的PC端工具可能不能正常工作。
飞思卡尔半导体
3
第二章MQX概述
2.1MQX的组织结构
MQX由核心组件(必选)和可选性组件构成。
对于核心组件,只有那些MQX或应用程序调用的函数包含在映像中。
为了满足要求,应用程序可通过加入可选组件来扩展和配置核心组件。
图2-1显示了以核心组件为中心,外围环绕可选性组件的示意图。
图2-1表2-1总结了核心组件和可性选组件,它们都将在后面章节中给予描述。
表2-1核心组件和可选性组件
组件初始化任务管理调度任务同步和通信
处理器间通信
内容初始化和自动任务创建动态任务管理轮转和先进先出显式使用任务队列轻量级信号量信号量轻量级事件事件互斥消息任务队列
类型核心核心核心可选核心可选可选可选可选可选可选可选
4
飞思卡尔半导体
定时
存储管理
中断处理输入/输出驱动检测工具
错误处理队列操纵命名组件嵌入式调试
时间组件轻量级定时器定时器看门狗可变大小的存储块固定大小的存储块(区块)存储器管理单元(MMU),高速缓存和虚拟存储轻量级存储
输入/输出子系统格式化输入/输出栈的运用内核日志日志轻量级日志任务错误代码,异常处理,运行测试
EDS服务器
可选(BSP)可选可选可选核心可选可选
可选可选(BSP)可选(BSP)可选(BSP)核心可选可选可选核心核心可选可选
2.2初始化
初始化组件是核心组件。
_mqx()运行的同时,应用程序开始启动,初始化硬件并启动MQX。
MQX启动时,创建称为自启动任务的应用程序。
2.3任务管理
任务管理是核心组件。
与MQX启动时自动创建任务一样,应用程序运行时也能创建、管理和终止任务。
它能为同一个任务创建多个实例,并且在一个应用程序中没有任务总个数的限制。
应用程序可动态改变任一任务的属性。
当一个任务终止时,MQX释放任务资源。
此外,对于每个任务均可指定:z退出函数:当任务终止时由MQX调用。
z异常处理:当处于活动状态的任务发生异常时由MQX调用。
飞思卡尔半导体
5
2.4调度
调度遵从POSIX.4标准(实时扩展)并且支持如下策略:zFIFO(基于优先级的抢占机制)调度是核心组件——活动任务(activetask)是等待时
间最长且优先级最高的任务。
z轮转(又称时间片)调度也是核心组件——活动任务(activetask)是优先级最高,等待
时间最长且未消耗自身时间片的任务。
z显式调度(使用任务队列)是一种可选性组件——可以使用任务队列明确地调度任务或
创建相对复杂的同步机制。
因为任务队列提供尽可能少的功能,因此效率很高。
应用程序在创建任务队列时可指定使用FIFO或轮转调度策略。
2.5存储管理
2.5.1可变大小存储块管理
为了分配和释放可变大小的存储片(称为内存块),MQX提供了类似于大部分C运行函数库提供的malloc()和free()函数功能的核心服务。
可以从默认内存池的内部区或外部区分配存储块给任务或系统。
分配给任务的存储块是该任务的资源,当任务终止时,MQX将释放存储块,收回所分配的资源。
2.5.2固定大小存储块管理(区块)
区块组件是可选性组件。
可以分配和管理固定大小的存储片(称作区块)。
区块组件支持快速、固定大小的存储分配,减少了存储碎片,节约了存储资源。
区块可位于默认内存池的内部(动态区块)和外部(静态区块)。
可将区块分配给任务或系统。
分配给任务的存储块是该任务的资源,当任务终止时,MQX将释放存储块,收回所分配的资源。
2.5.3高速缓存控制
MQX功能函数能够控制某些CPU具有的指令缓存和数据缓存。
2.5.4存储器管理单元(MMU)控制
对于一些CPU而言,在使能高速缓存之前,必须先初始化存储管理单元(MMU)。
MQX功能函数能够初始化、使能和禁止MMU,以及为MMU添加一个存储区域。
你可以通过MMU页表来控制MMU。
2.5.5轻量级存储管理
当一个应用程序受到代码和数据大小限制时,可使用轻量级存储管理。
轻量级存储管理只有
6
飞思卡尔半导体
少量的接口函数,代码和数据量也较小。
因此,某些部分健壮性降低(移除了头部效验),速度变慢(任务销毁次数增多)。
如果你更改了实时编译配置选项,那么在分配存储空间时,MQX将使用轻量级存储组件。
更多内容,参见3.14节“实时编译配置MQX”。
2.6任务同步
2.6.1轻量级事件
轻量级事件(LWEvent)是可选性组件。
对于使用位状态改变来实现同步的任务而言,这是一个低开销方式。
轻量级事件需要的存储空间很少,且速度快。
2.6.2事件
事件组件是可选性组件,支持格式化为位字段对象的动态管理。
任务和中断服务例程可使用事件来实现同步,以位状态改变的形式传送简单信息。
有些是命名的快速事件组。
事件组具有自动清除事件位,因此,MQX在事件位被设置后立即清除该事件位。
应用程序能够设置位于远程处理器上的事件组内的事件位。
2.6.3轻量级信号量
轻量级信号量(LWSems)组件是核心组件。
对于实现共享资源同步存取的任务而言,这是一种低开销方式。
轻量级信号量组件需要的存储空间很少,运行速度快,轻量级信号量计数FIFO信号量且没有优先级继承。
2.6.4信号量
信号量组件是可选性组件。
信号量组件用以计数为形式的信号。
你可以使用信号量同步任务,控制对共享资源的访问权限,或实现生产/消费信号机制。
信号量组件提供FIFO队列、优先级队列和优先级继承,可以是严谨的,也可以是非严谨的。
有些是命名的快速信号量。
2.6.5互斥
互斥组件是可选性组件。
遵从POSIX.4a标准(线程扩展)。
在访问共享资源时,互斥组件实现任务间的相互排斥。
互斥提供轮询、FIFO队列、优先级队列、自旋和限制自旋队列、优先级继承以及优先级保护。
互斥是严谨的,也就是说,一个任务不能解锁互斥,除非它先锁定了互斥。
2.6.6消息
消息组件是可选性组件。
任务间通过向其它任务打开的消息队列发送消息进行通信。
每个任务打开自己的输入消息队列。
消息队列由队列ID唯一识别,它是在队列创建时由MQX分配。
只
飞思卡尔半导体
7
有打开了消息队列的任务才可以从队列接收消息。
任何任务可以发送消息到先前打开的消息队列中,只要它知道打开的队列的ID。
任务从消息池分配消息。
消息池有两种:系统消息池和私有消息池。
任何任务可以从系统消息池中分配消息(系统消息)。
具有消息池ID的任务可以从私有消息池中分配消息(私有消息)。
2.6.7任务队列
除了提供调度机制外,任务队列还提供简单有效的方法实现任务同步。
你可以将任务队列中的任务挂起或移除。
2.7处理器间通信
处理器间通信(IPC)组件是可选性组件。
一个应用程序能够在多个处理器上同时运行,因为每个处理器有一个MQX可执行映像。
可执行映像通过消息进行通信和协作,这些信息通过内存或处理器间的通信链传输。
每个映像中的应用程序任务不必相同,事实上,它们通常是不一样的。
2.8定时
2.8.1时间组件
时间组件是可选性组件,你可以在BSP级允许或禁止它。
时间有两种:消逝时间和绝对时间。
你可以改变绝对时间。
时间解析度依赖MQX启动时为目标硬件设置的应用程序解析度所决定。
2.8.2轻量级定时器
轻量级定时器组件是可选性组件,为周期性调用应用函数提供低开销机制。
轻量级定时器通过建立周期队列安装,在开始阶段加入一个定时器,在某偏移量处到期。
当向队列增加一个轻量级定时器时,可以指定一个告知函数,它将在定时器到期时由MQX滴答中断服务程序调用。
因为定时器从中断服务程序运行,所以并不是所有的MQX函数能够调用。
2.8.3定时器
定时器组件是可选性组件。
它提供周期执行应用程序的功能。
MQX支持一次性定时器(到期一次)和周期性定时器(在给定间隔重复到期)。
你可以设置定时器在某指定时间或时间段后启动。
设置了定时器后,可以指定一个告知函数,当定时器到期时由定时器任务调用。
告知函数可以用来同步任务,通过发送消息,设置事件,或者使用其它MQX同步机制实现。
8
飞思卡尔半导体
2.8.4看门狗
看门狗组件是可选性组件,它帮助用户检测任务级别的崩溃和死锁情况。
2.9中断和异常处理
中断和异常处理是可选组件(PSP级别)。
MQX为BSP定义范围内的所有硬件中断服务,并为活动任务保存最精简的场景信息。
如果CPU支持嵌套中断,那么MQX也完全支持。
一旦进入中断服务程序(ISR)内,应用程序可以重新定义中断级别。
为了进一步缩短中断延迟,MQX推迟任务调度,直到所有中断服务程序都运行之后。
除此之外,只有当中断服务程序准备好一个新任务时,MQX才重新调度。
为了减小堆栈规模,MQX支持独立的中断堆栈。
中断服务程序(ISR)不是任务;它是一个规模小、速度快、能对硬件中断迅速反应的例程。
中断服务程序通常用C语言编写,它的任务包括重置设备,获取设备数据,以及向相关任务发送信号。
通过非阻塞式MQX函数,中断服务程序可以用来给一个任务发送信号。
2.10输入/输出(I/O)驱动
输入/输出驱动是可选性构件(BSP级别),由格式化输入/输出和输入/输出子系统组成。
本手册内容不涉及I/O驱动。
2.10.1格式化输入/输出
MQX提供格式化输入/输出函数库,它是输入/输出子系统的应用程序接口(API)。
2.10.2输入/输出子系统
你可以动态安装输入/输出设备驱动程序。
此后,任何任务都可以打开它们。
2.11检测工具
2.11.1日志
日志组件是可选性组件,让你保存和恢复应用程序专属信息。
每个日志条目有一个时间戳和序列号。
你可以使用这些信息进行测试、调试、校验和分析运行情况。
2.11.2轻量级日志
轻量级日志类似于一般日志,但它仅使用固定大小的条目。
轻量级日志比普通的应用程序日志速度快,通常用作内核日志。
飞思卡尔半导体
9
2.11.3内核日志
内核日志是可选性组件,让你记录MQX活动状态。
你可以在指定的位置或者MQX选择的位置创建内核日志。
你也可以配置内核日志,以便记录所有MQX函数调用、场景切换以及中断服务情况。
内核日志用在性能评估工具软件中。
2.11.4栈的运用
MQX提供的核心功能,可以动态检查中断栈和任务栈,以便判断是否分配了足够的栈空间。
2.12出错处理
2.12.1任务出错代码
每个任务有一个任务出错代码,它和任务的场景相关。
特定MQX功能函数能够读取和更新任务出错代码。
2.12.2异常处理
你可以为所有未处理的中断指定一个默认的中断服务例程(ISR)。
对于中断服务程序产生的异常,你也可以指定一个特定的中断服务例程。
2.12.3实时测试
MQX提供了一些核心实时测试函数,在正常运行状态,应用程序能够调用它们。
以下组件具有测试函数:
z事件和轻量级事件z内核日志和轻量级日志z固定大小的存储块(区块)z可变大小的存储块和轻量级存储块z消息池和消息队列z互斥z命名组件z队列(应用程序定义的)z信号量和轻量级信号量z任务队列z定时器和轻量级定时器z看门狗
10
飞思卡尔半导体
2.13队列操纵
实现队列元素双向链表结构的核心组件。
可以初始化队列、加入、移除和读取队列元素。
2.14命名组件
命名组件是可选性组件。
它提供一个名称库,与动态定义的标量(如队列ID)一一对应。
2.15嵌入式调试
EDS服务器是可选性组件。
它与运行在EDS客户端的主机通信,读写MQX信息,从主机获取各种配置信息。
飞思卡尔半导体
11
第三章使用MQX
3.1前言
本章介绍如何使用MQX,内容包括可编译和运行的示例。
查看如下内容:
参见:
本章中提到的每个函数的原型
MQX附录
本章中提到的数据类型
MQX附录
3.2初始化并开始运行MQX
MQX开始于_mqx()函数,该函数以MQX初始化结构为参数。
基于这一结构中的参数,MQX完成如下任务:z载入并初始化MQX经常使用的数据,包括默认内存池,就绪队列,中断堆栈和任务栈;z初始化硬件(如芯片选择);z开启定时器;z设置默认的时间片值;z生成空闲任务,当没有其它任务就绪时将被激活;z生成由任务模板列表定义为自启动的任务;z开始调度多任务。
3.2.1MQX初始化结构
MQX初始化结构中定义应用程序和目标硬件的参数如下:
typedefstructmqx_initialization_struct{_mqx_uintPROCESSOR_NUMBER;pointerSTART_OF_KERNEL_MEMORY;pointerEND_OF_KERNEL_MEMORY;_mqx_uintINTERRUPT_STACK_SIZE;TASK_TEMPLATE_STRUCT_PTRTASK_TEMPLATE_LIST;_mqx_uintMQX_HARDWARE_INTERRUPT_LEVEL_MAX;_mqx_uintMAX_MSGPOOLS;_mqx_uintMAX_MSGQS;char_PTR_IO_CHANNEL;char_PTR_IO_OPEN_MODE;
12
飞思卡尔半导体
_mqx_uintRESERVED[2];
}MQX_INITIALIZATION_STRUCT,_PTR_MQX_INITIALIZATION_STRUCT_PTR;如需查看每个域的描述,参见MQX附录。
3.2.1.1默认MQX初始化结构
你可以为MQX初始化结构定义自己的初始值,或者使用由每个BSP提供的默认值。
该默认值被称为MQX_init_struct而且保存在相应的BSP目录下的mqx_init.c文件中。
该文件已经被编译并连接到MQX。
注意:对于任务相关的调试工具来说,MQX初始化结构必须命名为MQX_init_struct。
本章中的例子将使用如下MQX_init_struct:
MQX_INITIALIZATION_STRUCTMQX_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*/(pointer)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};
注:初始化RESERVED域的元素值为
0
3.2.2任务模板列表
任务模板列表(TASK_TEMPLATE_STRUCT)定义了一组初始化模板,基于该模板可以在处理器上生成任务。
初始化时,MQX生成每个任务的一个实例,任务模板将其定义为一个自启动任务。
同样,当应用程序运行时,它能按任务模板生成其它任务,该模板由任务模板定义或应用程序动态定义。
任务模板队列的结尾是一个填入全0的任务模板。
typedefstructtask_template_struct{_mqx_uintTASK_TEMPLATE_INDEX;void_CODE_PTR_TASK_ADDRESS)(uint_32);_mem_sizeTASK_STACKSIZE;_mqx_uintTASK_PRIORITY;char_PTR_TASK_NAME;
飞思卡尔半导体
13
_mqx_uintTASK_ATTRIBUTES;uint_32CREATION_PARAMETER;_mqx_uintDEFAULT_TIME_SLICE;
}TASK_TEMPLATE_STRUCT,_PTR_TASK_TEMPLATE_STRUCT_PTR;如需查看每个域的描述,请参见MQX附录。
3.2.2.1指定任务优先级
注意:如果指定某个任务优先级为
0,则该任务运行时屏蔽了所有中断。
当你在任务模板列表中指定任务优先级,请注意:zMQX为每个优先级生成一个就绪队列直至最低的优先级(最大的数值)z当一个应用程序运行时,它不能生成一个优先级低于任务模板列表中的最低优先级(较大的
数值)的任务。
3.2.2.2指定任务属性
你可以为一个任务指定如下属性的任意组合:z自启动——当MQX开始运行时,它生成任务的一个实例;zDSP——MQX保存DSP协处理器寄存器作为任务现场的一部分;z浮点——MQX保存浮点寄存器作为任务现场的一部分;z时间片——MQX为任务使用轮循调度(默认是FIFO调度)。
3.2.2.3默认任务模板列表
你可以初始化自己的任务模板列表,也可以使用默认的列表MQX_template_list。
3.2.2.4举例:一个任务模板列表
TASK_TEMPLATE_STRUCTMQX_template_list[]={{MAIN_TASK,world_task,0x2000,
5,"world_task",MQX_AUTO_START_TASK,0L,0},{HELLO,hello_task,0x2000,
5,"hello_task",MQX_TIME_SLICE_TASK,0L,100},{FLOAT,float_task,0x2000,
5,"Float_task",MQX_AUTO_START_TASK|MQX_FLOATING_POINT_TASK,0L,0},{0,0,0,0,0,0,0L,0}};
在这个例子中,world_task是一个自启动任务,因此在初始化时MQX生成一个参数为0的任务实例。
应用程序定义任务模板索引(MAIN_TASK)。
该任务优先级为
5。
World_task()函数是任务的入口,栈的大小是0x2000个寻址单元。
任务hello_task是一个时间片任务,如果使用默认的实时编译配置选项,其时间片数值为100毫秒。
相关的配置选项参见3.14节内容。
Float_task任务既是一个浮点任务,也是一个自启动任务。
14
飞思卡尔半导体
3.2.2.5实例:创建一个自启动任务
创建一个简单任务,其功能为输出“HelloWorld”并终止,程序编写如下:
/*hello.c*/#include
5,"hello",MQX_AUTO_START_TASK,0L,0},{0,0,0,0,0,0,0L,0}};voidhello_task(uint_32initial_data){printf("\nHelloWorld\n");_mqx_exit
(0);}
3.2.2.5.1编译程序并连接到MQX
1.进入如下目录:mqx\examples\hello2.参考MQX发布版本的说明文档,获得创建和运行程序的指令。
3.按照说明文档的指令运行该应用程序。
在输出设备上将会有如下显示:
HelloWorldFreescale
MQX
针对FreescaleMQX来说,CodewarriorDevelopmentStudio是MQX开发与构建的理想环境。
详细的“Hello”程序运行步骤参见下一节。
3.3使用FreescaleCodeWarriorDevelopmentStudio
本节将详细介绍使用FreescaleCodeWarriorDevelopmentStudio进行构建、运行、调试MQX应用程序的基本步骤。
ColdFireM52259评估板的“Hello”演示程序可作为范例。
关于使用CodeWarrior重建MQX操作系统和其它核心组件的详细内容,请参见4.5节“重建FreescaleMQXRTOS”。
1.从windows开始菜单运行CodeWarriorDevelopmentStudio2.在CodeWarrrior集成开发环境中使用File/Open菜单,打开为M52259评估板开发的“Hello”
飞思卡尔半导体
15
应用程序工程。
默认情况下,你可以在如下目录找到该工程文件:
C:\ProgramFiles\Freescale\FreescaleMQX\mqx\examples\hello\codewarrior\hello_m52259evb.mcp
图3-13.选择最适合你需求的构建目标。
例如,使用M52259评估板,你能够使用ExtMRAM调试目标以简化代码下载过程。
在该目标中,可执行代码使用外部MRAM存储器(而不是内部的Flash),同时使用片内SRAM存储数据。
4.按F7键(Project/Make菜单)生成项目。
注意:该过程要求你在宿主计算机lib目录下已有编译好的MQXPSP和BSP库。
FreescaleMQX要求在安装目录中有预编译的默认库,因此这一点必须满足。
在FreescaleMQX安装目录查看lib/m52259evb.cw,检查在mqx子目录中是否有库文件。
如果没有,请参照4.5节“重建FreescaleMQXRTOS”重新编译MQX系统。
5.如果上述步骤均已就绪,构建过程完成时应该没有任何错误或警告。
6.打开超级终端或者宿主计算机上的其它串行终端。
7.以如下配置打开串行终端:115200bps,8位数据,1位停止位,无奇偶校验位。
8.在CodeWarrior中,按F5(Project/Debug菜单)调用CodeWarrior调试器,并将可执行应用程序下载到MRAM存储器。
程序应当在main()函数的默认断点处停止。
16
飞思卡尔半导体
9.再按F5键继续执行应用程序。
10.在终端窗口中输出HelloWorld。
更多关于FreescaleMQX工程的CodeWarriorDevelopmentStudio详细信息,参见后续章节、发布版本的说明文档和Freescale评估板附送的实验步骤说明书。
3.4管理任务
从同一任务模板生成的多个任务可以并存,而且每个任务是一个独特的实例。
MQX保存了每个实例的现场,包括程序计数器,寄存器和堆栈。
每个任务有一个唯一的32位任务编号ID,MQX和其它任务通过该编号来区分不同的任务。
3.2节演示了当MQX初始化时一个任务如何被自动启动。
你也可以在应用程序运行时生成、管理或终止任务。
_task_abort_task_check_stack_task_create_task_create_blocked_task_destroy_task_disable_fp
_task_enable_fp_task_errno_task_get_creator_task_get_environment_task_get_error_task_get_error_ptr_task_get_exit_handler
运行任务退出句柄并释放资源后终止任务判断任务的栈是否超出限制创建并启动一个新的任务创建一个处于阻塞状态的任务释放资源后终止任务如果该任务属于浮点任务,则屏蔽该任务的浮点开关打开任务的浮点屏蔽开关从激活的任务中获取任务出错代码获取创建任务的任务ID获取任务的环境数据指针获取任务的出错代码获取任务的出错代码指针获取任务的退出句柄
飞思卡尔半导体
17
_task_get_id_task_get_id_from_name_task_get_index_from_id_task_get_parameter_task_get_parameter_for_task_get_processor_task_get_td_task_get_template_index_task_get_template_ptr_task_restart
_task_set_environment_task_set_error_task_set_exit_handler_task_set_parameter_task_set_parameter_for
获取任务ID从任务模板中获取指定名称的首任务ID获取指定任务ID的任务模板索引获取任务创建参数为一个任务获取创建参数获取任务驻留的处理器编号根据任务ID获取任务描述的指针根据任务名称获取任务模板的索引根据任务ID获取任务模板的指针在任务函数开始运行时重启一个任务,保留原来的任务描述、任务ID和任务栈为一个任务设置环境数据的指针设置任务出错代码设置任务的退出句柄设置任务的创建参数为一个任务设置任务的创建参数
3.4.1创建任务
任何任务(创建者)均可以通过调用_task_create()或者_task_create_blocked()创建其它任务(子任务),并传递处理器编号、任务模板索引和任务创建参数。
应用程序定义一个创建参数,通常用于为子任务提供初始化信息。
一个任务也可以创建一个在任务模板列表中没有定义过的任务,指定任务模板索引为
0,这时,MQX把任务创建参数当作一个指向任务模板的指针。
函数初始化子任务的堆栈。
_task_create()函数将子任务挂入任务优先级的就绪队列。
如果子任务比创建者的优先级还高,子任务将变成活动任务,因为它是目前优先级最高的就绪任务。
如果创建者有更高或者相等的优先级,则依然保持活动状态。
_task_create_blocked()函数创建一个被阻塞的任务。
该任务将保持非就绪状态直到另一个任务调用_task_ready()函数。
3.4.2获取任务IDs
一个任务能够使用_task_get_id()函数直接获取其任务ID。
如果使用任务ID为参数,你可以用关键字MQX_NULL_TASK_ID标识当前活动任务的ID。
一个任务能够使用_task_get_creator()函数直接获取其创建者的任务ID。
函数_task_create()返回子任务ID给创建者。
此外,任务ID还可以根据创建该任务的任务模板中的任务名称来确定。
该功能可通过_task_get_id_from_name()实现,返回的是在任务模板列表中第一次匹配该任务名称的任务ID。
3.4.3获取和设置一个任务环境
一个任务可以通过_task_set_environment函数保存一个和应用程序相关的环境指针。
其它任
18
飞思卡尔半导体
务可以通过_task_get_environment()函数访问该环境指针。
3.4.4管理任务错误
每一个任务都有与任务现场相关的出错代码(任务出错代码)。
一些MQX函数检测到错误时会及时更新任务出错代码。
如果一个MQX函数检测到一个错误而应用程序忽略了该错误,则很可能会有其它错误继续出现。
通常第一个错误最能说明问题所在;后续的错误则可能存在误导。
在MQX将出错代码设置为某一个值而不是MQX_OK之后,为了提供一个可靠的机会去诊断错误,MQX通常不会进一步改变任务错误代码直到任务明确地将其重置为MQX_OK。
一个任务可以通过如下函数获取任务错误代码:_task_get_error()_task_errno()一个任务通过调用_task_set_error()函数重置其任务出错代码为MQX_OK。
该函数返回先前任务的出错代码并设置任务出错代码为MQX_OK。
一个任务能通过调用_task_set_error()函数设置其任务错误代码为一个值而不是MQX_OK。
然而,只有当当前任务的错误代码是MQX_OK时,MQX才会更新任务的错误代码。
3.4.5重启任务
一个应用程序能够通过调用_task_restart()函数重启一个任务,该函数从该任务函数的起始位置重启任务,保留了同样的任务描述符、任务ID和任务栈。
3.4.6终止任务
一个任务能够终止自身或者其它任何已知任务ID的任务。
当一个任务被终止时,他的子任务并不终止。
当一个任务终止时,MQX释放了该任务的所有MQX资源。
这些资源包括:
动态分配的存储器块和区块轻量级信号量消息队列消息互斥量非严谨的信号量传递后的严谨信号量取消排队的连接任务描述符应用程序能够通过调用_task_destroy()或者“礼貌地”调用_task_abort()函数来立刻终止一个任务(在MQX释放任务所有资源之后)。
当一个任务被“礼貌地”终止,如果该任务被阻塞,MQX将该任务置入合适的就绪队列。
当即将被终止的任务变为活动状态时,将运行一个定义为应用程序的任务退出句柄。
该句柄能够清
飞思卡尔半导体
19
除MQX无法处理的资源。
任务退出句柄可以通过_task_set_exit_handler()函数设置,也可以通过
_task_get_exit_handler()函数获取。
如果任务从它的任务体返回,则MQX也会调用任务退出句柄。
3.4.7实例:创建任务
该例子基于第21页例子增加了第二个任务(world_task)。
我们确认例子中的任务模板列表包括了world_task的信息,并修改hello_task以使它不是一个自启动任务。
World_task任务是一个自启动任务。
当MQX启动时,它创建了world_task。
World_task通过以word_task为参数调用_task_create()函数去创建hello_task任务。
MQX使用hello_task模板生成hello_task的一个实例。
如果_task_create()函数调用成功,将返回新的子任务的ID;否则将返回MQX_NULL_TASK_ID。
新的hello_task任务将被置入任务优先级的就绪队列。
因为它拥有比world_task更高的优先级,它将变为活动状态。
活动的任务打印输出Hello。
之后World_task任务将变为活动状态并检查hello_task任务是否成功创建。
如果是,world_task将输出World;否则,world_task将输出一个错误信息。
最后MQX退出。
如果你更改world_task的优先级,使它的优先级和hello_task相同,则只输出World。
因为world_task和hello_task具有相同的优先级并且不会放弃控制权,所以world_task在hello_task之前运行。
既然hello_task没有机会再次运行,则world_task在输出World之后调用_mqx_exit(),不会再有任何输出。
3.4.7.1实例代码
/*hello2.c*/#include
5,"world",MQX_AUTO_START_TASK,0L,0},{HELLO_TASK,hello_task,500,
4,"hello",0,0L,0},{0,0,0,0,0,0,0L,0}};/*TASK*-----------------------------------------------------
20
飞思卡尔半导体
*
*TaskName:world_task
*Comments:
*Thistaskcreateshello_taskandthenprints"World".
*
*END*-----------------------------------------------------*/
voidworld_task(uint_32initial_data)
{
_task_idhello_task_id;
hello_task_id=_task_create(
0,HELLO_TASK,0);
if(hello_task_id==MQX_NULL_TASK_ID){
printf("\nCouldnotcreatehello_task\n");
}else{
printf("World\n");
}
_mqx_exit
(0);
}
/*TASK*-----------------------------------------------------
*
*TaskName:hello_task
*Comments:
*Thistaskprints"Hello".
*
*END*-----------------------------------------------------*/
voidhello_task(uint_32initial_data)
{
printf("Hello\n");
_task_block();
}
3.4.7.2在MQX下编译和连接应用程序
1.进入如下目录mqx\examples\hello22.参照MQX发行版本的说明文档,查找关于如何创建和运行应用程序的指令。
3.按照说明文档中的指令运行应用程序。
在输出设备上将显示如下:
Hello
WorldFreescaleMQX
针对FreescaleMQX来说,CodewarriorDevelopmentStudio是MQX开发与构建的理想环境。
更多细节请参
飞思卡尔半导体
21
阅3.3节“使用FreescaleCodewarriorDevelopmentStudio”。
3.5调度任务
MQX提供如下任务调度策略:zFIFO(先来先服务)z轮循z使用任务队列(详细内容参见3.7.8节)
你可以设置处理器和每个任务的调度策略为使用这些调度策略任意组合的多个任务。
FIFO
或者轮循方式,这样,应用程序可能包含
3.5.1FIFO调度
FIFO是默认的调度策略。
使用先来先服务调度策略,接下来运行的任务(变为活动状态的)总是拥有最高优先级并且等待最久时间的那个任务。
活动状态的任务保持运行直至遇到如下状况:z活动状态的任务自愿放弃处理器,因为它调用了一个处于阻塞状态的MQX函数。
z产生了中断,因为它拥有比活动任务更高的优先级。
z更高优先级的任务已经处于就绪状态。
3.5.2轮循调度
轮循调度与FIFO调度比较类似,但不同之处在于每个轮循任务都被分配了最大的时间(时
间片),在该时间范围内,该任务才可以被激活。
当且仅当任务的MQX_TIME_SLICE_TASK属性在任务模板中被设置时才可以使用轮循调度
策略。
任务的时间片取决于模板中DEFAULT_TIME_SLICE属性值。
但如果该值为
0,任务的时
间片即为默认的处理器时间片。
最初,处理器的默认时间片为周期性定时器中断时间间隔的10
倍。
由于绝大多数BSP的时间间隔为5毫秒,则初始的处理器默认时间片为50毫秒。
你可以通
过调用_sched_set_rr_interval()或者_sched_set_rr_interval_ticks()函数,以处理器的任务
IDMQX_DEFAULT_TASK_ID为参数,修改默认的处理器时间片。
当一个活动的轮循任务的时间
片用完时,MQX将会保存该任务的现场参数,执行切换操作,并检查就绪队列选择激活合适的任
务。
MQX将到期的任务移动至任务就绪队列的末尾,并在就绪队列中产生对下一个任务的控制。
如果在就绪队列中没有其它的任务,则到期的任务将继续运行。
在轮循调度策略中,相同优先级的任务将以一种时间均等的方式共享处理器时间。
表3-1汇总:获取与设置调度信息
_sched_get_max_priority
获取允许设置的最高优先级;返
回值为
0
_sched_get_min_priority
获取允许设置的最低优先级
_sched_get_policy
获取调度策略
_sched_get_rr_interval
获取时间片,单位为毫秒
22
飞思卡尔半导体
_sched_get_rr_interval_ticks
获取时间片,单位为时钟滴答
_sched_set_policy
设置调度策略
_sched_set_rr_interval
设置时间片,单位为毫秒
_sched_set_rr_interval_ticks
设置时间片,单位为时钟滴答
表3-2汇总:任务调度
_sched_yield
移动活动任务至就绪队列的末
尾,并将处理器指向就绪队列中
具有相同优先级的下一个任务
_task_block
阻塞任务
_task_get_priority
获取任务的优先级
_task_ready
激活一个任务为就绪状态
_task_set_priority
设置任务的优先级
_task_start_preemption
允许任务抢占
_task__preemption
禁止任务抢占
每个任务都处于下列某种逻辑状态:
阻塞状态——任务未准备好而不能被激活,因为该任务在等待某些情况发生;当这些情况发
生时,该任务才变为就绪状态;
就绪状态——任务准备好可以被激活,但未进入激活状态,因为其优先级等于或低于当前的
激活任务;
激活状态——该任务在运行中。
如果激活状态任务变为阻塞状态或抢占状态,则MQX将执行切换操作,此时将从就绪队列
中选择合适的任务并激活。
MQX将选择优先级最高的任务进入激活状态。
如果多个具有相同优先
级的任务进入就绪状态,则就绪队列中的首任务先被激活。
也就是说,每个就绪队列执行先来先
服务规则。
3.5.2.1抢占
激活状态的任务可以被抢占。
当一个更高优先级的任务变为就绪并因而变成活动任务时,抢占将会发生。
先前的活动任务依然处于就绪状态,但不再是活动任务。
当一个中断句柄产生一个更高优先级的就绪任务,或者活动任务生成一个更高优先级的就绪任务时,抢占会发生。
3.6内存管理
3.6.1使用可变块管理内存
默认情况下,MQX从自己的默认内存池分配内存块。
任务也能在默认内存池之外产生内存池,并从中分配内存块。
两种分配操作类似,均调用了绝大多数C语言动态库中的malloc()和free()函数完成操作。
注意:不允许将内存块作为消息使用,使用消息时必须从消息池中分配(参见3.7.7节)内存块可以是私有内存块(属于分配它的任务所有)或者系统内存块(不属于任何任务)。
当
飞思卡尔半导体
23
任务结束时,MQX返回该任务的私有内存块给内存。
当MQX分配内存块时,将至少分配所要求大小的内存块(也即分配的内存块可能更大)。
此外,任务可以调用_mem_transfer()函数转换内存块的所有权给其它任务。
表3-3汇总:采用可变大小的内存块管理内存
_mem_alloc
从默认内存池分配私有内存块
_mem_alloc_from
从指定的内存池分配私有内存块
_mem_alloc_zero
从默认的内存池分配以0填充的私有内存块
_mem_alloc_zero_from
从指定的内存池分配以0填充的私有内存块
_mem_alloc_system
从默认的内存池分配系统内存块
_mem_alloc_system_from
从指定内存池分配系统内存块
_mem_alloc_system_zero
从默认内存池分配以0填充的系统内存块
_mem_alloc_system_zero_from
从指定内存池分配以0填充的系统内存块
_mem_copy
拷贝某位置的内存数据到其它位置
_mem_create_pool
在默认内存池之外生成内存池
_mem_extend
追加额外的内存给默认内存池;附加的内存必
须在当前默认内存池之外,但不必与其邻接
_mem_extend_pool
追加额外的内存给某非默认内存池;额外的内
存必须在该内存池之外,但不必与其邻接
_mem_free
释放默认内存池内、外的内存块
_mem_free_part
释放部分内存块(如果内存块比申请的大,或
者比需求的大)
_mem_get_error
获取使用_mem_test()函数产生错误时指向内
存位置的指针
_mem_get_error_pool
获取使用_mem_test_pool()函数产生错误时指
向内存位置的指针
_mem_get_highwater
获取默认内存池分配的内存块的高位地址(尽
管可能已经被释放)
_mem_get_highwater_pool
获取已分配内存池的高位地址(尽管可能已经
被释放)
_mem_get_size
获取内存块的大小,其大小可能大于申请值
_mem_swap_endian
转换为其它端格式
_mem_test
测试默认的内存池;即检查内部校验码以确定
内存完整性是否被破坏(通常被破坏是由于应
用程序写内存块出界)
_mem_test_and_set
测试并设置内存位置
_mem_test_pool
测试内存池的错误,参见_mem_test()
_mem_transfer
转交内存块所有权给另一任务
_mem_zero
设置全部/部分内存块为全
0
24
飞思卡尔半导体
3.6.2利用可变大小块管理轻量级内存
轻量级内存函数与常规内存函数较为类似,参见3.6.1节“利用可变块管理内存”,但它们的代码和数据量开销较小。
如果你修改了一个MQX实时编译配置选项,当分配内存时MQX将使用轻量级内存组件。
更多信息参见3.14节。
表3-4汇总:使用可变大小块操纵轻量级内存
轻量级内存使用特定的结构和常量,它们均在lwmem.h中定义
_lwmem_alloc
从默认的轻量级内存池分配私有的轻量级内
存块
_lwmem_alloc_from
从指定的轻量级内存池分配私有的轻量级内
存块
_lwmem_alloc_zero
从默认的轻量级内存池分配私有的轻量级内
存块,并以全0填充
_lwmem_alloc_zero_from
从指定的轻量级内存池分配私有的轻量级内
存块,并以全0填充
_lwmem_alloc_system
从默认的轻量级内存池分配系统轻量级内存
块
_lwmem_alloc_system_from
从指定的轻量级内存池分配系统轻量级内存
块
_lwmem_alloc_system_zero
从默认的轻量级内存池分配系统的轻量级内
存块,并以全0填充
_lwmem_alloc_system_zero_from
从指定的轻量级内存池分配系统的轻量级内
存块,并以全0填充
_lwmem_create_pool
生成轻量级内存池
_lwmem_free
释放轻量级内存块
_lwmem_get_size
获取轻量级内存块的大小,其大小可能大于
申请的大小
_lwmem_set_default_pool
设置内存池为默认的轻量级内存池
_lwmem_test
测试所有轻量级内存池
_lwmem_transfer
转交轻量级内存块的所有权为其它任务所有
3.6.3使用固定大小块管理内存(区块)
使用区块组件,你可以管理固定大小内存块的分区,区块的大小是当任务创建分区的时候指定的。
在默认的内存池中有可增长的动态区块,而在默认内存池之外又有不可增长的静态区块。
3.6.3.1为动态区块生成区块组件
你可以显式地使用_partition_ponent()函数生成区块组件。
否则,如果不显式地创
飞思卡尔半导体
25
建,MQX将在应用程序第一次生成分区时创建它,并且没有其它参数。
3.6.3.2生成区块
有两种类型的区块可供选择:
区块类型
创建者
调用函数
动态
默认内存池
_partition_create()
静态
非默认内存池
_partition_create_at()
如果你生成静态区块,你必须保证该内存不会覆盖应用程序所使用的代码或者数据空间。
3.6.3.3分配和释放区块
应用程序能够分配两种类型的区块:动态区块或静态区块。
区块类型
分配函数
资源归属
调用者
私有Private
_partition_alloc()
申请它的任务
系统System
_partition_alloc_system()
不属于任何任务
如果任务被终止时,它的私有区块也被释放。
仅被自己任何任务
3.6.3.4撤销动态区块
如果一个动态区块中的所有分区块都被释放,则任何任务均可调用_partition_destroy()函数撤销区块。
此外,你不能撤销一个静态区块。
3.6.3.5举例:两个区块
如下流程图显示一个静态区块和一个动态区块。
表3-
5
_partition_alloc_partition_alloc_system_partition_alloc_system_zero_partition_alloc_zero
图3-2汇总:使用固定大小块管理内存(区块)
从分区分配私有区块从分区分配系统区块从分区分配以全0填充的系统区块从分区分配以全0填充的私有区块
26
飞思卡尔半导体
_partition_calculate_blocks
_partition_calculate_size_partition_create_partition_create_at
_partition_ponent_partition_destroy_partition_extend
_partition_free_partition_get_block_size_partition_get_free_blocks_partition_get_max_used_blocks
_partition_get_total_blocks_partition_get_total_size_partition_test_partition_transfer
根据区块的大小和分区的大小(针对静态区块来说),计算区块的数量根据分区块的大小和块的数量计算区块的大小从默认的内存池创建区块(动态区块)在默认的内存池之外,在指定的位置创建分区(静态区块)创建区块组件撤销未分配任何分区块的动态区块增加内存到静态分区,增加的内存按照该分区中块的大小被划分成相同大小的区块返回分区块给该分区获取分区中分区块的大小获取分区中未分配的分区块的数量获取分区中已分配的分区块的数量,也即,高位地址标识已经同时分配的最大数量,但未必是当前分配的数量获取分区中分区块的数量获取分区的大小,包括扩展分区测试区块组件转交分区块的所有权给其它任务(包括系统等);只有新的所有者才有权释放分区块
3.6.4操纵缓冲寄存器
MQX函数允许我们操纵某些CPU的指令寄存器和数据寄存器。
为了我们能够写一个有或者没有缓冲寄存器系统的应用程序,MQX将这些函数封装成了宏。
对于那些没有缓冲寄存器的CPU来说,宏不映射到任何函数;对于一些CPU来说,使用了统一的缓冲寄存器(该缓冲寄存器既用于数据也用于代码),则_DCACHE_和_ICACHE_宏块映射到了相同的函数。
3.6.4.1清除数据缓存
MQX使用关键词flush表示清除数据缓存中的所有数据。
在缓存中那些未写数据被写入物理存储器中。
3.6.4.2使数据/指令缓存失效
MQX使用关键词invalidate表示使所有缓存实体失效。
缓存中遗留的数据或指令如果没有被
写入存储器,则将被丢失。
后续的访问会重新加载缓存,其数据或者指令来自物理内存。
表3-6汇总:控制数据缓存
_DCACHE_DISABLE
禁用数据缓存
_DCACHE_ENABLE
启用数据缓存
飞思卡尔半导体
27
_DCACHE_FLUSH_DCACHE_FLUSH_LINE_DCACHE_FLUSH_MLINES_DCACHE_INVALIDATE_DCACHE_INVALIDATE_LINE_DCACHE_INVALIDATE_MLINES
清除数据缓存清除指定地址的数据缓存数据清除指定区域的数据缓存数据使数据缓存失效使指定地址的数据缓存失效使指定区域的数据缓存失效
表3-
7
_ICACHE_DISABLE_ICACHE_ENABLE_ICACHE_INVALIDATE_ICACHE_INVALIDATE_LINE_ICACHE_INVALIDATE_MLINES
汇总:控制指令缓存
禁用指令缓存启用指令缓存使指令寄存器失效使指定地址的指令寄存器失效使指定区域的指令寄存器失效
3.6.5控制MMU(虚拟存储器)
对于某些CPU来说,你在启用缓存之前必须初始化内存管理单元(MemoryManagementUnit,MMU)。
MQX函数允许初始化、启用、禁用MMU,或者为其增加一个存储区域。
你能够通过MMU页表管理MMU。
虚拟存储器组件允许应用程序控制MMU页表。
下图表示了虚拟地址、MMU页表、MMU页、物理页以及物理地址之间的关系。
图3-3使用虚拟存储组件,应用程序能够管理虚拟内存,使它映射到相应的物理地址。
应用程序能够使用虚拟内存组件为一个任务生成虚拟现场。
虚拟现场为该任务提供私有的内存空间,而且仅当该任务活动时才可见。
28
飞思卡尔半导体
当BSP被初始化时,需要调用如下函数:
表3-8汇总:管理虚拟内存
_mmu_add_vcontext
为虚拟现场增加内存区域
_mmu_add_vregion
为MMU页表增加内存区域,以便供所有任务
和MQX使用
_mmu_create_vcontext
为一任务生成虚拟现场
_mmu_create_vtask
使用初始化的虚拟现场生成一任务
_mmu_destroy_vcontext
撤销一任务的虚拟现场
_mmu_get_vmem_attributes
获取一MMU页的虚拟内存属性
_mmu_get_vpage_size
获取一MMU页的大小
_mmu_set_vmem_attributes
设置一MMU页的虚拟内存属性
_mmu_vdisable
禁用虚拟存储器
_mmu_venable
启用虚拟存储器
_mmu_vinit
初始化MMU,使用MMU页表
_mmu_
获取虚拟地址对应的物理地址
3.6.5.1举例:使用虚拟存储初始化MMU
增加一定数量的内存区用于指令缓存和数据缓存,所有任务均可访问该内存区:
_mqx_uint_bsp_enable_operation(void){..._mmu_vinit(MPC860_MMU_PAGE_SIZE_4K,NULL);/*Setupandinitializetheinstructioncache:*/_mmu_add_vregion(BSP_FLASH_BASE,BSP_FLASH_BASE,BSP_FLASH_SIZE,PSP_MMU_CODE_CACHE|PSP_MMU_CACHED);_mmu_add_vregion(BSP_DIMM_BASE,BSP_DIMM_BASE,BSP_DIMM_SIZE,PSP_MMU_CODE_CACHE|PSP_MMU_CACHED);_mmu_add_vregion(BSP_RAM_BASE,BSP_RAM_BASE,BSP_RAM_SIZE,PSP_MMU_CODE_CACHE|PSP_MMU_CACHED);/*Setupandinitializethedatacache:*/_mmu_add_vregion(BSP_FLASH_BASE,BSP_FLASH_BASE,BSP_FLASH_SIZE,PSP_MMU_DATA_CACHE|PSP_MMU_CACHE_INHIBITED);_mmu_add_vregion(BSP_PCI_MEMORY_BASE,BSP_PCI_MEMORY_BASE,BSP_PCI_MEMORY_SIZE,PSP_MMU_DATA_CACHE|PSP_MMU_CACHE_INHIBITED);_mmu_add_vregion(BSP_PCI_IO_BASE,BSP_PCI_IO_BASE,BSP_PCI_IO_SIZE,PSP_MMU_DATA_CACHE|PSP_MMU_CACHE_INHIBITED);_mmu_add_vregion(BSP_DIMM_BASE,BSP_DIMM_BASE,BSP_DIMM_SIZE,
飞思卡尔半导体
29
PSP_MMU_DATA_CACHE|PSP_MMU_CACHE_INHIBITED);_mmu_add_vregion(BSP_RAM_BASE,BSP_RAM_BASE,BSP_COMMON_RAM_SIZE,PSP_MMU_DATA_CACHE|PSP_MMU_CACHE_INHIBITED);_mmu_venable();_ICACHE_ENABLE
(0);_DCACHE_ENABLE
(0);...}
3.6.5.2举例:创建虚拟现场
在地址0xA0000000处设置活动任务可访问64KB的私有内存:
...{pointervirtual_mem_ptr;uint_32size;virtual_mem_ptr=(pointer)0xA0000000;size=0x10000L;...result=_mmu_create_vcontext(MQX_NULL_TASK_ID);if(result!
=MQX_OK){}result=_mmu_add_vcontext(MQX_NULL_TASK_ID,virtual_mem_ptr,size,0);if(result!
=MQX_OK){}...
3.6.5.3举例:使用虚拟现场创建任务
使用虚拟现场创建任务,并拷贝公共数据:
.../*Tasktemplatenumberforthevirtual-contexttask:*/#defineVMEM_TTN10/*Globalvariable:*/uchar_ptrdata_to_duplicate[0x10000]={0x1,0x2,0x3};...{pointervirtual_mem_ptr;virtual_mem_ptr=(pointer)0xA0000000;...
30
飞思卡尔半导体
result=_mmu_create_vtask(VMEM_TTN,
0,&data_to_duplicate,virtual_mem_ptr,sizeof(data_to_duplicate),0);if(result==MQX_NULL_TASK_ID){}result=_mmu_create_vtask(VMEM_TTN,
0,&data_to_duplicate,virtual_mem_ptr,sizeof(data_to_duplicate),0);if(result==MQX_NULL_TASK_ID){}...
3.7任务同步
你可以通过如下一种或者多种机制实现任务之间的同步,这些机制将会在后续章节中介绍:z事件——任务能够等待一组事件位被置位。
任务可以设置或者清除这组事件位;z轻量级事件——事件的简化实现;z信号量——任务等待信号量从非0逐步增长,其它任务可以增长信号量。
MQX信号量通过优
先级继承防止优先级倒置。
关于优先级倒置的更多讨论,参见3.7.3.2节。
z轻量级信号量——简单计数信号量z互斥——任务可以使用互斥保证某一时刻仅有一个任务访问共享的数据。
为了访问共享数据,
任务可对互斥量加锁,如果该互斥量已经被加锁则等待。
当任务完成对共享数据的访问,则解锁该互斥量。
互斥通过优先级继承和优先级保护来防止优先级倒置。
详细内容参见3.7.3节。
z消息传递——MQX允许任务之间传递数据。
当一个任务在消息中填充了数据并发送到一个特别的消息队列。
其它任务可以等待该消息队列的消息到达,也即接收消息。
z任务队列——允许应用程序挂起并恢复任务。
3.7.1事件
事件能够被用来同步多个任务,或者实现任务与ISR之间的同步。
事件组件包括事件组,它是事件位的集合。
事件组中的事件位的数量就是_mqx_uint中定义
的位的数量。
任务可以等待事件组中的事件位。
如果事件位没有被置位,则任务将阻塞。
任何其它任务或
者ISR均可以置位事件位。
当事件位被置位,MQX将从等待的任务中选择满足条件的任务进入
就绪队列。
如果事件组有自动清除中的事件位,只要该事件位被置位,MQX将立即清除事件位。
并将该任务置入就绪队列。
Freescale
为了在目标平台上优化代码和数据存储器的要求,事件组
MQX件将不会默认地在MQX内核中编译。
为了测试这一特征,你
首先需要在MQX用户配置文件中启用它,并重新编译MQX
PSP、BSP和其它核心组件。
详细内容参见4.5节“重建Freescale
飞思卡尔半导体
31
MQXRTOS”。
此外,有两种事件组:命名的事件组,以唯一的字符串标识;快速事件组,以唯一的数字标
识。
应用程序能够通过字符串中特定的处理器号码打开远程处理器上的事件组,之后应用程序可
以设置该事件组的任何事件位。
但应用程序不能等待远程事件组中任何事件位。
表3-9汇总:使用事件组件
事件使用特定的结构和常数,详细定义参见event.h文件
_event_clear
清除事件组中的特定事件位
_event_close
关闭与事件组的连接
_event_create
生成命名的事件组
_event_create_auto_clear
生成命名的事件组,并包含自动清除事件位
_event_ponent
生成事件组件
_event_create_fast
生成快速事件组
_event_create_fase_auto_clear
生成快速事件组,并包含自动事件位
_event_destroy
撤销命名的事件组
_event_destroy_fast
撤销快速事件组
_event_get_value
获取事件组的值
_event_get_wait_count
获取等待事件组中事件位的任务数
_event_open
打开与命名事件组的连接
_event_open_fast
打开与快速事件组的连接
_event_set
设置本地/远程处理器上事件组中特定的事件位
_event_test
测试事件组件
_event_wait_all
为事件组中所有指定事件位等待指定毫秒数
_event_wait_all_for
为事件组中所有指定事件位等待指定时钟滴答周期
(包括硬件滴答)
_event_wait_all_ticks
为事件组中所有指定事件位等待指定的时钟滴答数。
_event_wait_all_untill
等待事件组中所有指定事件位,直到指定滴答时间到
_event_wait_any
为事件组中任何指定事件位等待指定毫秒数
_event_wait_any_for
为事件组中任何指定事件位等待指定时钟滴答周期
_event_wait_any_ticks
为事件组中任何指定事件位等待指定的时钟滴答数
_event_wait_any_until
等待事件组中任何指定事件位,直到指定滴答时间到
3.7.1.1生成事件组件
你可以调用_event_ponent()显示地生成事件组件。
如果不显式地生成,则MQX将在应用程序首次生成事件组时自动使用默认值生成事件组件。
参数
含义
默认值
初始值
能够创建的事件组的初始数量
8
增量
生成所有事件组时事件组的增量,直到
8
达到最大值为止
最大值
如果增量非
0,允许创建的事件组的最
0
32
飞思卡尔半导体
大数量
3.7.1.2创建事件组
在任务使用事件组件之前,必须创建一个事件组。
创建的事件组类型
调用函数
参数
快速事件组
_event_create_fast()
索引(事件组件被创建时必
(包含自动清除事件位)_event_create_fast_auto_clear()须符合指定限制条件)
命名事件组
_event_create()
字符串名称
(包含自动清除事件位)_event_create_auto_clear()
如果事件组生成时带有自动清除事件位,只要这些事件位被置位,MQX就会清除它们。
这一动作使等待这些位的任务进入就绪状态,不需要任务去清除这些位。
3.7.1.3打开与事件组的连接
在任务能够使用事件组件之前,必须打开与事件组的连接。
打开的事件组类型调用函数
参数
快速
_event_open_fast()
命名
_event_open()
两个函数均给事件组返回唯一的句柄。
符合指定限制的索引,而且是事件组件被创建时生成的字符串名称
3.7.1.4等待事件位
任务通过函数_event_wait_all()或_event_wait_any()等待事件组中一定模式的事件位。
当事件位被置位时,MQX将等待该位的任务处于就绪状态。
如果事件组在创建时带有自动清除事件位(也即函数_event_create_auto_clear()或_event_create_fast_auto_clear()),则MQX清除该位,以便等待的任务不必清除它。
3.7.1.5设置事件位
一任务通过函数_event_set()设置事件组中一定模式的事件位。
事件组可以在本地或远程处理器上。
当一个事件位被置位时,等待它的任务即变为就绪。
如果事件组创建时带有自动清除事件位,则只要这些事件位一旦被置位MQX就会清除它们。
3.7.1.6清除事件位
任务通过调用_event_clear()函数清除事件组中一定模式的事件位。
然而,如果事件组创建时带有自动清除事件位,则只要这些事件位一被置位MQX就会清除它们。
3.7.1.7关闭与事件组的连接
当任务不再使用事件组时,将可以通过_event_close()函数关闭与它的连接。
3.7.1.8撤销事件组
当任务被阻塞,且在等待即将被撤销的事件组中的事件位时,MQX会将它们移至就绪队列。
飞思卡尔半导体
33
3.7.1.9举例:使用事件
模拟时钟滴答ISR在每次运行时设置事件位。
服务任务在每次时钟滴答发生时执行某一动作。
因此任务需要等待模拟时钟滴答程序设置的事件位。
3.7.1.9.1样例代码
/*event.c*/#include
5,"service",MQX_AUTO_START_TASK,0L,0},{ISR_TASK,simulated_ISR_task,500,
5,"simulated_ISR",0,0L,0},{0,0,0,0,0,0,0L,0}};/*TASK*----------------------------------------------------**TaskName:simulated_ISR_task*Comments:*Thistaskopensaconnectiontotheevent.After*delayingtheeventbitsareset.*END*-----------------------------------------------------*/voidsimulated_ISR_task(uint_32initial_data){pointerevent_ptr;/*openeventconnection*/if(_event_open("global",&event_ptr)!
=MQX_OK){printf("\nOpenEventfailed");_mqx_exit
(0);}while(TRUE){_time_delay(1000);
34
飞思卡尔半导体
if(_event_set(event_ptr,0x01)!
=MQX_OK){printf("\nSetEventfailed");_mqx_exit
(0);}}}/*TASK*----------------------------------------------------**TaskName:service_task*Comments:*Thistaskcreatesaneventandthesimulated_ISR_task*task.Itopensaconnectiontotheeventandwaits.*Afterallbitshavebeenset"Tick"isprintedand*theeventiscleared.*END*-----------------------------------------------------*/voidservice_task(uint_32initial_data){pointerevent_ptr;_task_idsecond_task_id;/*setupevent*/if(_event_create("global")!
=MQX_OK){printf("\nMakeeventfailed");_mqx_exit
(0);}if(_event_open("global",&event_ptr)!
=MQX_OK){printf("\nOpeneventfailed");_mqx_exit
(0);}/*createtask*/second_task_id=_task_create(
0,ISR_TASK,0);if(second_task_id==MQX_NULL_TASK_ID){printf("Couldnotcreatesimulated_ISR_task\n");_mqx_exit
(0);}while(TRUE){if(_event_wait_all(event_ptr,0x01,0)!
=MQX_OK){printf("\nEventWaitfailed");_mqx_exit
(0);}
飞思卡尔半导体
35
if(_event_clear(event_ptr,0x01)!
=MQX_OK){printf("\nEventClearFailed");_mqx_exit
(0);}printf("Tick\n");}}
3.7.1.9.2用MQX编译和连接应用程序
1.进入以下目录:Mqx\examples\event
2.参阅MQX发布版本的说明文档,查找创建和运行应用程序的相关指令。
3.按照说明文档中指令运行该程序。
事件任务在每次事件位被置位时都会输出一条消息。
Freescale
对于FreescaleMQX来说,CodeWarriorDevelopment
MQX
Studio是MQX开发与重建的理想环境。
详细内容参见
第3.3节“使用FreescaleCodeWarriorDevelopment
Studio”。
3.7.2轻量级事件
轻量级事件是一种简化的、低成本实现的事件。
轻量级事件组件包含轻量级事件组,后者是事件位的集合。
轻量级事件组中的事件位的数目
是mqx_uint中定义的位的数目。
任何任务都可以等待轻量级事件组中的事件位。
如果事件位没有被置位,任务将阻塞。
任何
其它任务或者ISR均可置位事件位。
当事件位被置位时,MQX将所有满足等待条件的等待任务置入任务就绪队列。
如果轻量级事件组有自动清除事件位,只要这些事件位被置位,MQX将清除它们并使一个任务进入就绪状态。
轻量级事件组由静态数据结构创建而且不是多处理器的。
表3-10汇总:使用轻量级事件组件
轻量级事件使用特定的结构与常量,详细内容定义于lwevent.h文件中
_lwevent_clear
清除轻量级事件组中的指定事件位
_lwevent_create
创建轻量级事件组,指出是否包含自动清除事件位
_lwevent_destroy
撤销轻量级事件组
_lwevent_set
设置轻量级事件组的指定事件位
_lwevent_test
测试轻量级事件组件
_lwevent_wait_for
为轻量级事件组中所有或任一指定事件位等待一个指定的时钟滴答周期
_lwevent_wait_ticks
为轻量级事件组中所有或任一指定事件位等待指定的时钟滴答数
_lwevent_wait_until
等待轻量级事件组中所有或任一指定事件位,直到一个指定的时钟滴答时
间到
36
飞思卡尔半导体
3.7.2.1生成轻量级事件组
要生成一轻量级事件组,应用程序需要声明一个LWEVENT_STRUCT类型的变量,调用_lwevent_create()函数进行初始化,用一个指针指向该变量,并用一个标志标识事件组是否有自动清除事件位。
3.7.2.2等待事件位
任务可以调用_lwevent_wait系列函数之一去等待轻量级事件组中一定模式的事件位。
当等待条件不满足,函数等待指定的时间后终止。
3.7.2.3设置事件位
任务可以调用_lwevent_set()函数设置轻量级事件组中的一定模式事件位。
如果有任务等待的事件位得到满足,则MQX将该任务置入就绪队列。
如果事件组有自动清除事件位,则MQX仅将等待的首任务置为就绪状态。
3.7.2.4清除事件位
任务可以调用_lwevent_clear()函数清除轻量级事件组中的一定模式事件位。
然而,如果轻量级事件组在创建时带有自动清除事件位,只要这些事件一被置位,MQX就会清除它们。
3.7.2.5撤销轻量级事件组
当一个任务不再需要轻量级事件组,则可以调用_lwevent_destroy()函数撤销该事件组。
3.7.3关于信号量类型的对象
MQX提供了轻量级信号量(LWSems)、信号量和互斥功能。
你可以使用两种信号量实现任务同步与互斥操作。
任务等待信号量,如果信号量为
0,则MQX阻塞该任务;否则,MQX降低信号量,并给该任务一信号量,该任务继续运行。
如果带有该信号量的任务结束运行时,则它会传递信号量;任务保持就绪状态。
如果任务正在等待信号量,MQX将该任务置入就绪队列;否则,MQX增加信号量。
你可以使用互斥实现互斥操作。
互斥有时也被称为二进制信号量,因为它的计数仅能是0或
1。
3.7.3.1限制
如果信号量类型的对象是严谨的,任务在释放对象之前必须等待并获得该对象;如果对象是非严谨的,则任务在释放对象前不必获得该对象。
3.7.3.2优先级倒置
任务的优先级倒置是一个经典问题,指的是相关任务的优先级被倒置。
当任务使用信号量或互斥操作获取共享资源时,优先级倒置可能会发生。
3.7.3.3举例:优先级倒置
有三个不同优先级的任务,中优先级的任务阻止了高优先级任务先运行,如下图所示。
序号
任务
1
任务
2
任务
3
飞思卡尔半导体
37
(高优先级P1)
(中优先级P2)
(低优先级P3)
1
.运行
2
.获取信号量
3
.就绪
4
.抢先任务3并运行
5
.就绪
6
.抢先任务2并运行
7
.获取任务3的信号量
8
.分配内存块,等待信号量
9
.运行并保持
3.7.3.4使用优先级继承防止优先级倒置
当你生成MQX信号量或互斥操作时,你可以设置一种属性:优先级继承,该属性可用于防止优先级倒置。
如果你设置了优先级继承,在任务锁定信号量或互斥操作时,任务的优先级将不会比等待该信号量或互斥操作的任何任务的优先级低。
这样如果一个高优先级的任务等待信号量或互斥,MQX会临时性地提高任务的优先级至等待任务的优先级水平。
表3-11优先级继承属性
序
任务
1
号
(高优先级P1)
任务2(中优先级P2)
任务3(低优先级P3)
1
.运行
2
.获取信号量
3
.就绪
4
.抢先任务3并运行
5
.就绪
6
.抢先任务2并运行
7
.获取任务3的信号量
8
.提升任务3的优先级至P1
并分配内存块
9
.抢先任务1并运行
10
.完成工作并传递信号量
11
.优先级降至P3
12.抢先任务3和任务2并运行13.获取信号量
3.7.3.5使用优先级保护防止优先级倒置
当你生成MQX互斥时,你可以设置优先级保护的互斥属性和互斥优先级。
这些属性可以防止优先级倒置。
38
飞思卡尔半导体
如果一个要求锁定互斥的任务的优先级并没有互斥优先级的等级高,MQX则临时性地提高任务的优先级至互斥的优先级,只要任务锁定了该互斥。
表3-12互斥属性
序号
任务1(高优先级P1)
任务2(中优先级P2)
任务3(低优先级P3)
1
.运行
2
.锁定互斥(在优先
级P1下);提升优先
级至P1
3
.就绪
4
.不抢先任务
3
5
.就绪
6
.不抢先任务
3
7
.以互斥方式完成任
务并解锁
8
.优先级降至P3
9
.抢先任务3并运行
10
.锁定互斥
表3-13轻量级信号量、信号量与互斥的比较
特性
轻量级信号量
信号量
互斥
超时
是
是
否
队列方式
FIFO
FIFO
FIFO
优先
优先
只是自旋
限制自旋
严谨
否
否或是
是
优先级继承
否
是
是
优先级保护
否
否
是
大小
最小
最大
在轻量级信号量和
信号量之间
速度
最快
最慢
在轻量级信号量和
信号量之间
3.7.4轻量级信号量
轻量级信号量是一种简化的、低成本的信号量实现。
轻量级信号量由静态数据结构生成,并且不是多处理器的。
飞思卡尔半导体
39
_lwsem_create_lwsem_destroy_lwsem_poll_lwsem_post_lwsem_test_lwsem_wait_lwsem_wait_for_lwsem_wait_ticks_lwsem_wait_until
表3-14汇总:使用轻量级信号量
创建轻量级信号量撤销轻量级信号量提升轻量级信号量(非阻塞)传递轻量级信号量测试轻量级信号量组件等待轻量级信号量为轻量级信号量等待指定的时钟滴答周期为轻量级信号量等待指定的时钟滴答数等待轻量级信号量直到指定的时钟滴答数结束
3.7.4.1生成轻量级信号量
为了生成轻量级信号量,你可以声明一LWSEM_STRUCT类型的变量并调用_lwsem_create()函数进行初始化,该函数有一指针指向变量并设置了一个初始的信号量计数。
该信号量计数表明了并发地访问信号量资源的请求数,并设置为初始值。
3.7.4.2等待并传递轻量级信号量
任务可以调用_lwsem_wait()函数等待轻量级信号量。
如果信号量计数大于
0,则MQX将其减量,任务继续运行。
如果信号量计数等于
0,MQX阻塞该任务直至某些其它任务传递了信号量。
为了释放轻量级信号量,任务可以调用_lwsem_post()函数传递它。
如果没有其它任务在等待该信号量,则MQX增加该信号量的计数。
由于轻量级信号量是非严谨的,任务可以不等待而先传递它;因此信号量计数没有限制,也即可以增长至超过初始值。
3.7.4.3撤销一轻量级信号量
当一个任务不再需要轻量级信号量时,可以调用_lwsem_destroy()函数撤销该信号量。
3.7.4.4举例:生产者与消费者
生产者与消费者任务可通过轻量级信号量来同步。
1.读任务生成:
——多个写任务并给每个任务分配一个唯一的字符;——一个写信号量LWSem——一个读信号量LWSem2.每个写任务在写入字符到缓冲区之前都必须等待写信号量。
当字符被写入,每个写任务传递读信号量,表示字符现在允许读任务读取了。
3.每个读任务在从缓冲区读取字符之前必须等待读信号量。
在字符被读取后,每个读任务必须传递给写信号量,表示缓冲区现在已经可以写入其它字符了。
3.7.4.4.1数据结构与定义
/*read.h*/
40
飞思卡尔半导体
/*NumberofWriterTasks*/#defineNUM_WRITERS3/*TaskIDs*/#defineWRITE_TASK5#defineREAD_TASK6/*Globaldatastructureessiblebyreadandwritetasks.**Containstwolightweightsemaphoresthatgovernesstothe**datavariable.*/typedefstructsw_fifo{LWSEM_STRUCTREAD_SEM;LWSEM_STRUCTWRITE_SEM;ucharDATA;}SW_FIFO,_PTR_SW_FIFO_PTR;/*Functionprototypes*/externvoidwrite_task(uint_32initial_data);externvoidread_task(uint_32initial_data);externSW_FIFOfifo;
3.7.4.4.2任务模板
/*ttl.c*/#include
5,"write",0,0L,0},{READ_TASK,read_task,500,
5,"read",MQX_AUTO_START_TASK,0L,0},{0,0,0,0,0,0,0L,0}};
3.7.4.4.3写任务的代码
/*write.c*/#include
=MQX_OK){printf("\n_lwsem_waitfailed");_mqx_exit
(0);}fifo.DATA=(uchar)initial_data;_lwsem_post(&fifo.READ_SEM);}}
3.7.4.4.4读任务的代码
/*read.c*/#include
=MQX_OK){printf("\nCreatingread_semfailed:0x%X",result);_mqx_exit
(0);}result=_lwsem_create(&fifo.WRITE_SEM,1);if(result!
=MQX_OK){printf("\nCreatingwrite_semfailed:0x%X",result);_mqx_exit
(0);}/*Createwritetasks*/for(i=0;i
=MQX_OK){printf("\n_lwsem_waitfailed:0x%X",result);_mqx_exit
(0);}putchar('\n');putchar(fifo.DATA);_lwsem_post(&fifo.WRITE_SEM);}}
3.7.4.4.5用MQX编译和连接应用程序
1.进入如下目录:mqx\examples\lwsem2.参阅MQX发布版本的说明文档,查找构建和运行应用程序的相关指令。
3.按照说明文档中的指令运行应用程序。
在输出设备上显示如下内容:AABCAB…
飞思卡尔半导体
43
FreescaleMQX
对于FreescaleMQX来说,CodeWarrior集成开发环境是MQX开发和构建的理想平台。
详细内容参考第3.3节“使用FreescaleCodeWarrior集成开发环境”。
3.7.5信号量
信号量可以用于同步任务和互斥操作。
任务关于信号量的主要操作包括等待信号量和传递信
号量。
Freescale
为了在目标平台上优化代码和数据存储器需求,信
MQX
号量组件默认地不在MQX内核中编译。
为了测试这
一
特征,你首先需要在MQX用户配置文件中启用并重新
编译MQXPSP、BSP和其它核心组件。
详细内容参见
4.5节“重建FreescaleMQXRTOS”。
表3-15
信号量使用特定的结构与参数,详见sem.h文件_sem_close_sem_create_sem_ponent_sem_create_fast_sem_destroy_sem_destroy_fast_sem_get_value_sem_get_wait_count_sem_open_sem_open_fast_sem_post_sem_test_sem_wait_sem_wait_for_sem_wait_ticks_sem_wait_until
汇总:使用信号量
关闭与信号量的连接创建信号量创建信号量组件创建快速信号量撤销命名的信号量撤销快速信号量获取当前信号量计数获取等待信号量的任务数打开与命名信号量的连接打开与快速信号量的连接传递信号量测试信号量组件等待信号量一定毫秒数等待信号量一个时钟滴答周期等待信号量一定时钟滴答数等待信号量直到一定时间到(时钟滴答)
3.7.5.1信号量使用纵览
为了使用信号量,任务需要执行如下步骤,每一步骤都将在后续章节中详细叙述:
1.创建信号量组件(可选)
2.生成信号量
3.打开与信号量的连接
44
飞思卡尔半导体
4.如果信号量是严谨的,它将等待信号量
5.当完成使用信号量,它将及时传递信号量
6.如果不再使用信号量,它将关闭与信号量的连接
7.如果信号量正在保护一个共享资源,而该共享资源已经不存在或者不再能访问,则任务可以撤销该信号量
3.7.5.2生成信号量组件
你可以显式地调用_sem_ponent()函数生成信号量组件。
如果不显式地生成,MQX则在应用程序首次创建信号量时使用默认的参数创建组件。
参数及其默认值与事件组件的参数相同,详见3.7.1.1节。
3.7.5.3生成信号量
在使用信号量之前,任务需要创建信号量。
创建该类型信号量:
调用:
参数
Fast
_sem_create_fast()
索引,它必须在信号量组件
被创建时的指定的范围内。
Named
_sem_create()
字符串名称
当任务创建信号量时,需要指定如下内容:z计数初始值——信号量计数的初始值标识信号量拥有的锁数量(一个任务可以获得多个
锁)。
z优先级队列——如果优先级队列被设置,等待信号量的任务队列必须按照优先级队列,
而且MQX置信号量为最高优先级的等待任务。
z如果优先级队列没有被设置,则队列按照先来先服务顺序,而且MQX置信号量为最久等
待的任务。
z优先级继承——如果优先级继承被设置,而且有更高优先级的任务在等待信号量,则MQX
会提高任务的优先级至等待任务的优先级。
更多详细内容,请参考3.7.3.4节关于优先级继承的讨论。
为了使用优先级继承,信号量必须是严谨的。
z严谨性——如果声明信号量是严谨的,则任务在能够传递信号量之前必须等待信号量。
如果信号量是严谨的,初始值是信号量计数的最大值。
如果信号量是不严谨的,则计数不受限制。
3.7.5.4打开与信号量的连接
一个任务在使用信号量之前,必须先打开与信号量的连接。
打开一个与该类型信号量的连接:
调用:
参数
Fast
_sem_open_fast()
索引,它必须在信号量组件
被创建时的指定的范围内。
Named
_sem_open()
字符串名称
以上两个函数都给信号量返回一个唯一句柄。
飞思卡尔半导体
45
3.7.5.5等待信号量与传递信号量
任务调用_sem_wait_系列函数之一等待信号量。
如果信号量计数为
0,MQX阻塞该任务直到其它任务传递该信号量(_sem_post())或者特定的任务的定时时间到。
如果计数不为
0,则MQX将计数减量,任务继续运行。
当任务传递信号量并且有多个任务正在等待信号量时,MQX将它们置入就绪队列。
如果没有任务在等待,则MQX增加信号量计数。
在这两种情况下,传递信号量的任务保持就绪状态。
3.7.5.6关闭与信号量的连接
当任务不再需要使用信号量时,它可以调用_sem_close()函数关闭与该信号量的连接。
3.7.5.7撤销信号量
当信号量不再被需要时,任务可以撤销它。
销毁该类型信号量:
调用:
参数:
Fast
_sem_destroy_fast()
索引,它必须在信号量组件
被创建时的指定的范围内。
Named
_sem_destroy()
字符串名称
同样,任务可以确认是否强制销毁。
如果强制销毁,MQX将等待该信号量的任务置为就绪,
并在所有任务传递信号量之后撤销该信号量。
如果不是强制销毁方式,则MQX在最后一个等待任务获得并传递信号量之后撤销该信号量。
(如果信号量是严谨的,则通常采用这一方式)
3.7.5.8举例:任务同步与互斥操作
该例子基于3.7.4.1节轻量级信号量例题,它给出信号量如何实现任务的同步与互斥操作。
该例子实现了多任务可以读、可以写的FIFO机制。
访问FIFO数据结构时需要进行互斥操作。
当FIFO满时写入数据任务,或者在FIFO空时读取数据任务,都要求实现任务同步操作。
为此,需要设置如下三个信号量:z索引信号量——为了在FIFO中实现互斥z读信号量——为了同步读任务z写信号量——为了同步写任务该例子涉及到三个任务:主程序、读和写。
主程序初始化信号量,创建读和写任务。
3.7.5.8.1数据结构与定义
/*main.h**Thisfilecontainsdefinitionsforthesemaphoreexample.*/#defineMAIN_TASK5#defineWRITE_TASK6#defineREAD_TASK7#defineARRAY_SIZE5#defineNUM_WRITERS2/*Globaldatastructureessiblebyreadandwritetasks.
46
飞思卡尔半导体
**ContainsaDATAarraythatsimulatesaFIFO.READ_INDEX**andWRITE_INDEXmarkthelocationinthearraythattheread**andwritetasksareessing.Alldataisprotectedby**semaphores.*/typedefstruct{_task_idDATA[ARRAY_SIZE];uint_32READ_INDEX;uint_32WRITE_INDEX;}SW_FIFO,_PTR_SW_FIFO_PTR;/*Functionprototypes*/externvoidmain_task(uint_32initial_data);externvoidwrite_task(uint_32initial_data);externvoidread_task(uint_32initial_data);externSW_FIFOfifo;
3.7.5.8.2任务模板
/*ttl.c*/#include
5,"main",MQX_AUTO_START_TASK,0L,0},{WRITE_TASK,write_task,600,
5,"write",0,0L,0},{READ_TASK,read_task,1000,
5,"read",0,0L,0},{0,0,0,0,0,0,0L,0}};
3.7.5.8.3主程序任务的代码主程序任务创建信号量组件、索引、读写信号量以及读写任务。
/*main.c*/#include
=MQX_OK){printf("\nCreatingponentfailed");_mqx_exit
(0);}if(_sem_create("write",ARRAY_SIZE,0)!
=MQX_OK){printf("\nCreatingwritesemaphorefailed");_mqx_exit
(0);}if(_sem_create("read",0,0)!
=MQX_OK){printf("\nCreatingreadsemaphorefailed");_mqx_exit
(0);}if(_sem_create("index",1,0)!
=MQX_OK){printf("\nCreatingindexsemaphorefailed");_mqx_exit
(0);}/*Createtasks:*/for(i=0;i
0,READ_TASK,0);printf("\nread_taskcreated,id0x%lx",task_id);}
48
飞思卡尔半导体
3.7.5.8.4读任务的代码
/*read.c*/#include
=MQX_OK){printf("\nOpeningwritesemaphorefailed");_mqx_exit
(0);}if(_sem_open("index",&index_sem)!
=MQX_OK){printf("\nOpeningindexsemaphorefailed");_mqx_exit
(0);}if(_sem_open("read",&read_sem)!
=MQX_OK){printf("\nOpeningreadsemaphorefailed");_mqx_exit
(0);}while(TRUE){/*Waitforthesemaphores:*/if(_sem_wait(read_sem,0)!
=MQX_OK){printf("\nWaitingforreadsemaphorefailed");_mqx_exit
(0);}if(_sem_wait(index_sem,0)!
=MQX_OK){
飞思卡尔半导体
49
printf("\nWaitingforindexsemaphorefailed");_mqx_exit
(0);}printf("\n0x%lx",fifo.DATA[fifo.READ_INDEX++]);if(fifo.READ_INDEX>=ARRAY_SIZE){fifo.READ_INDEX=0;}/*Postthesemaphores:*/_sem_post(index_sem);_sem_post(write_sem);}}
3.7.5.8.5写任务的代码
/*write.c*/#include
=MQX_OK){printf("\nOpeningwritesemaphorefailed");_mqx_exit
(0);}if(_sem_open("index",&index_sem)!
=MQX_OK){printf("\nOpeningindexsemaphorefailed");_mqx_exit
(0);
50
飞思卡尔半导体
}if(_sem_open("read",&read_sem)!
=MQX_OK){printf("\nOpeningreadsemaphorefailed");_mqx_exit
(0);}while(TRUE){/*Waitforthesemaphores:*/if(_sem_wait(write_sem,0)!
=MQX_OK){printf("\nWaitingforwritesemaphorefailed");_mqx_exit
(0);}if(_sem_wait(index_sem,0)!
=MQX_OK){printf("\nWaitingforindexsemaphorefailed");_mqx_exit
(0);}fifo.DATA[fifo.WRITE_INDEX++]=_task_get_id();if(fifo.WRITE_INDEX>=ARRAY_SIZE){fifo.WRITE_INDEX=0;}/*Postthesemaphores:*/_sem_post(index_sem);_sem_post(read_sem);}}
3.7.5.8.6编译程序并连接MQX
1.进入如下目录:mqx\examples\sem
2.参考MQX发布版本的说明文档,查找关于如何创建并运行应用程序的相关指令。
3.按照说明文档的指令运行该应用程序。
读任务输出了写入FIFO的数据。
修改程序以去除优先级继承,并再次运行应用程序。
Freescale
对于FreescaleMQX来说,CodeWarrior集成开发环
MQX
境是MQX开发和构建的理想平台。
详细内容参见第3.3
节“使用FreescaleCodeWarrior集成开发环境”。
3.7.6互斥
互斥通常用来实现互斥操作,也即一次仅允许一个任务访问共享资源,例如数据或者设备等。
要访问共享资源,任务需要锁定与资源关联的互斥。
一任务拥有互斥,直到它解锁为止。
Freescale
为了在目标平台上优化代码和数据内存需求,互斥
飞思卡尔半导体
51
MQX
组件不会默认地在MQX内核中编译。
为了测试这个特
性,首先你需要在MQX用户配置文件中启用它,并重
新编译MQXPSP、BSP及其它核心组件。
更多细节请
参阅第4.5节“重建FreescaleMQXRTOS”。
互斥和POSIX.4a(线程扩展)保持兼容,并提供了优先级继承和保护功能以防止优先级倒置。
表3-16汇总:使用互斥
互斥使用特定的结构与常量,详细定义参见mutex.h
_mutex_ponent
创建互斥组件
_mutex_destroy
撤销互斥
_mutex_get_priority_ceiling
获取互斥优先级
_mutex_get_wait_count
获取等待互斥的任务数
_mutex_init
初始化互斥
_mutex_lock
锁定互斥
_mutex_set_priority_ceiling
设置互斥优先级
_mutex_test
测试互斥组件
_mutex_try_lock
尝试锁定互斥
_mutex_unlock
解锁互斥
3.7.6.1创建互斥组件
你可以调用_mutex_ponent()函数显示地创建互斥组件。
如果不显式地创建,则MQX将在应用程序首次初始化互斥时创建它,并且不带参数。
3.7.6.2互斥属性
互斥可以根据任务的等待与调度协议包含若干属性。
3.7.6.3互斥等待协议
互斥可以包含若干个等待协议之
一,这将影响到申请已被锁定资源的任务。
等待协议
如果互斥已经被锁定,请求任务做如下事情:
排队(默认)
分配内存块,直到另一个任务解锁互斥。
当互斥被锁定时,要求锁定的第一个任务(不管优先级)锁定互斥。
优先级排队
分配内存块,直到另一个任务解锁互斥。
当互斥被锁定时,最高优先级任务请求锁定并锁定互斥。
Spin-only
无限期地自旋(被分配时间片),直到另一个任务解锁互斥。
这意味着MQX保存请求任务的现场,并在相同的优先就绪队列中分配下一个任务。
当就绪队列中的所有任务都运行了,请求任务再次激活。
如果互斥仍然被锁定,重复自旋。
Limitedspin
如果另一个任务先解锁互斥,自旋小于或等于指定的次数。
Spin-only协议仅当共享互斥的任务属于如下两种情况之一时才有效:
52
飞思卡尔半导体
z时间片任务z具有相同的优先级
如果具有不同优先级的非时间片任务试图共享一个Spin-only互斥,资源已被一低优先级的任务锁定,则更高优先级的任务将永远不能获得锁(除非低优先级的任务阻塞)。
Spin-only协议的互斥易于导致死锁,因此不推荐使用。
MQX为了保持与POSIX的兼容性而使用它。
3.7.6.4调度协议
互斥为了避免优先级倒置,可以有特别的调度协议。
这些规则可能在任务已经锁定互斥的时
候影响任务的优先级。
默认是两个协议都不起作用。
调度协议
含义
优先级继承优先级保护
如果已经锁定互斥的任务(任务A)的优先级并不和正在等待锁定互斥的最高优先级任务(任务B)一样高,当任务A具有互斥时,MQX将任务A的优先级提升,使它和任务B的优先级相同。
一个互斥具有一个优先级。
如果请求锁定互斥的任务(任务A)的优先级低于互斥的优先级,只要任务A具有互斥锁定,MQX就提升任务A的优先级至互斥的优先级。
3.7.6.5创建与初始化互斥
任务定义互斥时首先声明一个MUTEX_STRUCT类型的变量。
为了根据默认的等待协议属性和非特定的调度协议去初始化互斥,任务可以调用_mutex_init()函数,获得一个指向互斥变量的指针和一个空指针。
但是,如果要创建一个非默认互斥,任务需要执行如下操作:
1.定义MUTEX_ATTR_STRUCT类型的互斥属性结构
2.调用_mutatr_init()函数初始化属性结构
3.调用如下各种函数设置相应的属性:
_mutatr_set_prority_ceiling()
_mutatr_set_sched_protocol()
_mutatr_set_spin_limit()
_mutatr_set_wait_protocol()
1.