嵌入式实时操作系统MQX学习笔记(《嵌入式实时操作系统MQX应用开发技术》)

最近,基于MQX学习了ARM中操作系统的实现。

**********************************************************************************

转载请注明:

http://blog.csdn.net/guo8113/article/details/44197813

作者水平有限,错误之处还请斧正。

交流群QQ:139696200

***********************************************************************************

MQX是一款免费、开源,由刚被收购的Freescale维护的RTOS,实时性高,胆码最小为16KB,RAM开销最小为2KB。MQX主要由内核kernel、处理器支持包PSP和板级支持包BSP组成。最新版本为4.1.2,获取http://www.freescale.com/zh-Hans/webapp/sps/site/prod_summary.jsp?code=MQX&parentCode=null&nodeId=01521060795BB6#。更多详细介绍:http://cache.freescale.com/files/soft_dev_tools/doc/fact_sheet/MQXFS.pdf

1.MQX的启动

ARM cortex-m4上电后,取出首个中断向量表项的内容作为MSP指针(系统栈)取出第二个中断向量表项的内容作为启动函数(_boot)指针赋值给程序计数器PC(R15),程序开始执行。然后进入汇编的(boot.s(_boot)、startup.c(startup))。在boot.s中关终端,切换当前使用栈为进程栈(PSP)等,最后跳转进入_thumb_startup函数,完成禁用看门狗,初始化内存数据、初始化系统时钟等操作,然后进入main函数,调用MQX操作系统的入口函数_mqx(),开始启动MQX。

好玩的是MQX中很多API都是_开头的,另外对一个Linux程序员来说,其支持POSIX的文件与部分驱动的使用是令人兴奋的,可是其进程通信又恢复到了下划线式的函数名。

在_mqx()中需要初始化RTOS的大招:内核数据区(全局的内核数据结构体,靠这个数据区管理所有的软硬件资源:如任务列表、驱动列表、用于进程通信的各种队列等),然后初始化BSP、配置时钟滴答、加载设备驱动、初始化任务管理子系统、创建空闲任务、创建自启动任务、启动调度器。内核数据区大小不固定,前面是.vector,.DATA(已初始化的全局变量),.BSS(存放程序中未初始化的全局变量,属于静态内存分配)段,后面是可以使用的系统内存块和私有内存块(这个在内存管理中介绍)。


2.MQX的中断与时钟

分成2层来实现。内核ISR和用户ISR。MQX的中断采用独立的中断栈,与任务栈分开,提高安全性。在内核ISR系统初始化前需要为系统开辟中断栈并将栈地址给内核数据结构中的INTERRUPT_STACK_PTR,然后通过Flash中的静态中断向量表建立RAM中的稀疏中断向量表。MQX默认采用HASH表以8个向量为一组管理中断向量表,然后将稀疏中断向量表的首地址、最小中断向量号和最大向量号等保存到内核数据结构中,MQX实现了一个神奇的函数:_GET_KERNEL_DATA()用以获取内核数据区的基地址。

用户使用中断需要用_int_install_isr向MQX的稀疏中断向量链表安装用户的ISR,发生中断时,内核中断ISR响应中断,在内核数据区中查找用户注册的ISR,执行ISR、解决中断嵌套等。MQX的内核ISR为_int_kernel_isr函数由汇编编写。

SVC和PendSV中断(也叫异常),是完成OS中任务切换的重要元素,在任务调度中介绍。

MQX默认使用SysTick硬件完成系统的滴答设计,延时函数_time_delay执行机制:获取内核数据区,获得当前任务描述符结构体指针,根据延时的时间将当前任务放入延时任务队列中,放弃CPU的控制权,进行系统调度。延时队列中按照延时由短到长排列,不同优先级的任务拥有不同的任务队列。

3.MQX驱动

根据管理的方式不同,分为:轻量级(LW)驱动(直接提供C API,无需安装到设备驱动管理队列,如GPIO ADC),非安装类驱动(应用级驱动如SD、CAN、RTC等,提供C API;复杂驱动如MFS USB、MQX shell等,一般提供静态库,和C的API直接调用),安装类(I2S,I2C、SPI、Flash等,被看成一种文件形式,通过POSIX的fopen等函数进行操作,需要安装驱动到驱动管理队列)

安装类驱动管理采用三层模型:标准ANSI-C输入输出层、MQX输入输出子系统层、底层设备驱动层。MQX输入输出子系统提供API,如_io_dev_install,_io_get_handle,等,_io_dev_install函数的参数包括设备标识符(char字符串)、设备驱动打开函数的指针与初始化参数、关闭函数指针.......。设备驱动管理队列为io_device_struct,主要成员有char_ptr IDENTIFIER;IO_OPEN_FPTR IO_OPEN;IO_WRITE_FPTR IO_WRITE;等。双向链表的头指针赋值给内核数据区的io_device指针。

4.任务管理与调度

QX采用_task_create()进行任务的创建,同时通过任务模板管理任务:

TASK_TEMPLATE_STRUCT MQX_template_list[]=

{//任务编号,任务函数,任务栈大小,优先级,任务名,任务属性:MQX_AUTO_START_TASK等。}

_task_create会调用_task_build_internal这个函数,_task_build_internal中查看是否使用模板,然后调用_task_init_internal真正的创建任务。

MQX采用POSIX的调度策略,有FIFO和RR(时间片轮转)调度策略。调度函数最终通过SVC和PendSV(可挂起系统调用)完成。SVC(系统服务调用)指令在ARM7中被称为软件中断SWI指令,从cortex-M3开始采用SVC。MQX中的_sced_execute_schduler_internal等系统服务主要由执行调度(SVC_RUN_SCHED)(SVC_TASK_BLOCK)(SVC_TASK_SWITCH)构成,他们通过执行svc #num;指令触发SVC中断,在_svc_handler中断服务例程中处理。在MQX中num服务号1为执行调度,2为任务阻塞,3为任务切换。SVC触发后必须立即得到响应,二PendSV不同,它可以像普通中断一样被推迟执行,推迟方法是网PendSV挂起寄存器中写1,推迟后优先级不够高则等待。

他们的响应流程如图所示。

嵌入式实时操作系统MQX学习笔记(《嵌入式实时操作系统MQX应用开发技术》)_第1张图片

5.任务同步与通信

有轻量级事件(通过kernel_data->LWEVENTS链表管理)与事件、轻量级信号量、信号量、互斥量、消息对列等。

6.内存管理

_mem_alloc/_mem_alloc_system等。分为私有内存块和系统应用的系统内存块。系统内存分配从空闲内存链表查询大小符合要求的空闲块,让高优先级的任务先得到访问内存的服务,如果大于所需内存块则分成两块来用,分的的内存需要块头信息,因此比实际需要的要大;在内存释放时,需要进行前后空闲内存的合并。同时又可变大小内存池和固定大小内存管理模式。

你可能感兴趣的:(RTOS,实时操作系统,MQX)