嵌入式操作系统概述.docx

上传人:b****4 文档编号:3777256 上传时间:2022-11-25 格式:DOCX 页数:12 大小:803.20KB
下载 相关 举报
嵌入式操作系统概述.docx_第1页
第1页 / 共12页
嵌入式操作系统概述.docx_第2页
第2页 / 共12页
嵌入式操作系统概述.docx_第3页
第3页 / 共12页
嵌入式操作系统概述.docx_第4页
第4页 / 共12页
嵌入式操作系统概述.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

嵌入式操作系统概述.docx

《嵌入式操作系统概述.docx》由会员分享,可在线阅读,更多相关《嵌入式操作系统概述.docx(12页珍藏版)》请在冰豆网上搜索。

嵌入式操作系统概述.docx

嵌入式操作系统概述

1.1嵌入式操作系统概述

传统的嵌入式系统以前后台方式工作,一般主程序是一个无限循环,在后台按照一定的执行顺序调用不同的子程序模块来实现应用需求;中断服务子程序则在前台处理响应时间要求高的突发事件。

随着系统功能的增强和用户需求的增加,给任务调度和共享资源的管理带来了很高的复杂性,使得开发和排错的难度越来越大,甚至在某些情况下难以满足应用需求。

嵌入式操作系统可以将负杂的应用分解成多个任务,这些任务在系统内部分时运行,任务之间以优先级作为切换的依据,由操作系统按照一定的机制进行调度,大大降低了用户功能实现的复杂度。

其优势主要体现在如下几方面:

1.便于应用系统的设计、维护和扩展,提高了代码的课重用性和可读性。

多任务分解了复杂的应用,可以把系统分解成一个个相对独立的子功能模块,这些子功能模块组合在一起形成了一个写作良好的有机整体。

模块化的设计方法可以让整个系统变得简单直观,各模块功能单一,模块之间关系清晰、明确,模块可重复使用,开发、维护周期大大缩短。

由于整个系统有多个模块组成,所以增加新功能也只是为系统再增加几个新模块而已,对整个系统没有太大影响,系统易于扩展。

2.可裁剪,适合于资源紧张的嵌入式系统。

嵌入式系统与通用系统的一个主要区别就是其需求的明确并且个性化。

嵌入式操作系统可裁剪的特性,使得当系统不需要某些功能时,可将其裁减掉,以节省硬件资源,降低系统成本。

3.实时性,使对时间要求苛刻的事件能够得到及时处理。

大多数嵌入式操作系统都提供任务的实时调度保证,使得对时间敏感的任务在确定的时间内能够得到及时响应,这对工业控制等应用领域非常重要。

4.有利于设计出稳定可靠的应用系统。

嵌入式操作系统提供了大量的系统功能,而且一般都经过了严格的测试,使得在此基础上开发应用系统更简便且易于排错。

有些嵌入式操作系统还具有足够的开放性,允许用户深入了解和掌握其内部结构原理,并根据自己的需求特点编写应用系统,更好地发挥操作系统的效率和可靠性。

在嵌入式系统中引入操作系统也有两个不利的方面,一是操作系统会占用一部分系统资源,如CPU时间、存储空间、定时器,但这种目前嵌入式CPU性能越来越强大的情况下以不是什么问题;二是商用操作系统使用费较高,但对于一些开源的嵌入式操作系统,将其用于研究和教学是不需付费的,只有将其用于产品上才需缴纳授权费。

随着嵌入式CPU性能的提升,在嵌入式系统上运行操作系统,并在操作系统管理下实现用户多任务的运行,已经逐渐成为嵌入式开发的一种趋势。

目前比较主流的嵌入式操作系统有VxWorks、WinCE、Linux、uCOSII等。

其中VxWorks最早由美国WindRiver公司开发,内核最小为8K,以优良的可靠性和实时性被广泛应用于军事、航空航天等领域,但其昂贵的价格限制了它在民用产品上的推广。

WinCE是微软推出的Windows风格的针对嵌入式领域的操作系统,最小内核不足350K,提供在宿主机上的完善的编程、调试和代码管理工具,其缺点是占用系统资源较多,底层代码不开放,且购买费用较高。

Linux是一款性能优良的开源操作系统,在嵌入式领域应用逐渐增多,由于支持者众多,有丰富的软件资源可以利用,网络功能完善,但其实时性不好,且只能运行在具有MMU的ARM9内核的CPU,不支持以ARM7为内核的CPU。

在各种嵌入式操作系统的软件中,uC/OS-II是规模最小的系统之一,它实际上就是一个微内核,结构精巧,且源码公开、可固化、可裁剪,最小内核可编译至2K。

uC/OS-II由美国的JeanJ.Labrosse开发,他于1992年推出了uC/OS的第一个版本。

后来,在此基础上经过修改和扩充之后又推出了第二版,通过了美国航空管理局(FederalAviationAdministration)的认证,以其实时性好,执行效率高、占用空间小和可扩展性强而受到了工业界及教育界的欢迎。

1.2uC/OS-II实现原理

uC/OS-II把要解决的问题分为多个任务,每个任务都是整个应用的一部分,都被赋予一定的优先级,有自己的一套CPU寄存器和栈空间,如图3-1所示。

每个任务将自己的状态保存在自己的栈空间中,一旦获得CPU的使用权,便将自己的状态从栈空间复制到CPU寄存器中,并开始运行。

如运行中的任务因某种原因挂起,则将当前的运行状态保持到自己的堆栈中,等待下次获得CPU使用权时继续运行,而在操作系统的调度下另一具有最高优先权的任务将获得CPU的使用权而运行。

图3-1多任务的栈结构

系统的任务调度有两种方式。

一种是中断级任务调度。

系统的运行需要时钟节拍的驱动,uC/OS-II一般占用一个系统的硬件定时器,在多任务调度开始后,定时器开始计时,在每个节拍中断(或其他外部事件引起的中断)处理中,系统把正在运行中的任务挂起,进行任务调度,即找出所有处于就绪态任务中优先级最高的任务,并将CPU的使用权交给它去运行。

另一种调度是任务级调度,通过在任务中发软中断命令或直接调用调度函数,找出就绪态的最高优先级任务区执行。

可见至少在每一个时钟节拍到来时,会引发一次任务调度,使就绪的最高优先级的任务得以运行,从而保证了系统的实时性。

实际的节拍频率取决于应用程序的时间精度要求,一般为10~100Hz,节拍频率越高,系统的额外负担越重。

在uC/OS-II中最多可以支持64个任务,每个任务被赋予0-63不同的优先级等级,在没有用户任务执行时,系统就运行最低优先级的系统任务idletask。

典型地,每个任务都是一个无限的循环。

每个任务都处于5种状态之一的状态下,即休眠态,就绪态、运行态、挂起态(等待某一事件发生)和被中断态。

休眠态相当于该任务驻留在内存中,但并不被多任务内核所调度。

就绪意味着该任务已经准备好,可以运行,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行。

运行态的任务是指该任务掌握了CPU的控制权,正在运行中。

挂起状态也叫做等待事件态,指该任务在等待某一事件的发生,(例如等待某外设的I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等等)。

最后,发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,就进入了被中断状态。

操作系统内核负责管理各个任务,为每个任务分配CPU时间,进行任务的切换,并负责任务间的通信。

图3-2任务的状态

1.3uC/OS-II到ARM处理器的移植

1.3.1移植要点

由于嵌入式系统是针对用户的具体需求而设计,而不同系统实现功能不同,导致底层的硬件平台无法统一。

所以将嵌入式操作系统移植到具体的硬件系统上,就要根据CPU的不同而重新编写底层代码。

uC/OS-II在特定处理器上的移植工作绝大部分集中在多任务切换的实现上,因为这部分代码主要是用来保存和恢复处理器现场,许多操作如读写寄存器操作不能用C语言,只能使用特定的处理器的汇编语言来完成。

uC/OS-II的结构如图3-3所示,

图3-3uC/OS-II结构

将uC/OS-II移植到ARM处理器上,主要的工作集中在三个和体系结构相关的文件OS_CPU_C.C、OS_CPU_C.H以及OS_CPU_A.S中。

代码量大约是500行。

这三个文件是

❑定义函数OS_ENTER_CRITICAL和OS_EXIT_CRITICAL。

❑定义函数OS_TASK_SW执行任务切换。

❑定义函数OSCtxSw实现用户级上下文切换,用纯汇编实现。

❑定义函数OSIntCtxSw实现中断级任务切换,用纯汇编实现。

❑定义函数OSTickISR。

❑定义OSTaskStkInit来初始化任务的堆栈。

1.3.2OS_CPU.H的移植

1.数据类型定义

由于不同的CPU支持的数据宽度不同,而不同的编译器会使用不同的字节长度表示同一数据类型,本系统采用GNU的arm-elf-gcc编译器,所以在这个文件里要针对GCC编译器对数据类型进行重新定义。

此外,该文件还定义了堆栈单位OS_STK,定义了在处理器现场保存和恢复时所使用的数据类型,这些必须和处理器的寄存器长度一致,即32位整形量。

对堆栈的类型定义如下:

typedefunsignedintOS_STK;

这样定义了堆栈为32位无符号整形,以后为任务分配堆栈,可以使用OS_STK来定义,如:

OS_STKTaskLED_STK[64];

2.ARM处理器相关宏定义

为了增强代码的可移植性,该文件还定义了ARM处理器中退出临界区和进入临界区的宏定义:

#defineOS_ENTER_CRITICAL()ARMDisableInt()

#defineOS_EXIT_CRITICAL()ARMEnableInt()

3.堆栈增长方向

堆栈增长方向也由该文件定义,当进行函数调用时,入口参数和返回地址一般都会保存在当前任务的堆栈中,编译器的编译选项和由此生成的堆栈指令就会决定堆栈的增长方向。

当这个宏值为0时,表示堆栈是从低地址向高地址增长的;为1时,表示堆栈是从高地址向低地址递减的。

在ARM处理器习惯采用递减堆栈,所以定义如下:

#defineOS_STK_GROWTH1

1.3.3OS_CPU.C的移植

1.任务堆栈初始化

任务堆栈初始化函数OSTaskCreate()或OSTaskCreateExt()用来初始化任务的堆栈并返回新的堆栈指针stk。

任务初始状态的堆栈模拟发生一次中断后的堆栈结构,这样在任务被调度运行时,只需执行相应的出栈操作即可。

在ARM体系结构下,任务堆栈空间由高至低依次将保存着pc、lr、r12、r11、r10、…r1、r0、CPSR、SPSR,图3-4说明了OSTaskStkInit()初始化后的也是新创建任务的堆栈内容。

堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈栈顶指针,OSTaskCreate()或OSTaskCreateExt()将指针保存在任务的OS_TCB中。

图3-4OSTaskStkInit()初始化后的堆栈内容

2.系统hook函数

在这个文件里面还需要定义几个操作系统要求的hook函数。

这些用户定义的钩子函数将在相应的操作系统调用后执行指定的用户操作。

一般情况下如果没有特殊需求,只需要简单地将它们都实现为空函数就可以。

这些函数包括:

OSSTaskCreateHook()

OSTaskDelHook()

OSTaskSwHook()

OSTaskStatHook()

OSTimeTickHook()

3.中断级任务切换函数

该函数由OSIntExit()和OSExIntExit()调用。

它的作用是在ISR(中断服务程序)中如发现有高优先级任务的任务就绪(如等待的延时时间到),则在中断退出后并不返回被中断任务,而是直接调度就绪的高优先级任务执行。

这样做的目的主要是能够尽快地让高优先级的任务得到响应,保证系统的实时性能。

该函数通过设置一个全局变量need_to_swap_context标志以表示在中断服务程序中进行任务切换,然后在OSTickISR()中判断该变量以进行正确的动作。

其函数如下:

voidOSIntCtxSw(void)

{

need_to_swap_context=1;

}

1.3.4OS_CPU_A.S的移植

在OS_CPU_A.S中需要编写四组函数:

OSStartHighRd()、OS_TASK_SW()、ARMDisableInt()和ARMEnableInt()、OSTickISR()。

这些函数实现系统的节拍中断和任务切换,直接与底层硬件相关,必须用汇编语言实现。

1.时钟节拍中断服务函数

uC/OS-II的运行是靠时钟节拍驱动的。

时钟节拍是特定的周期性中断,这个中断可以看作是系统心脏的脉动。

时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍,以及当任务等待事件发生时,提供等待超时的依据。

时钟节拍率越快,系统的实时控制精度越高,但系统的额外开销就越大,所以时钟节拍间隔取决于不同的应用。

本系统的时钟节拍中断服务函数OSTickISR()使用S3C44B0的timer0作为时钟节拍源,产生间隔10mS的时钟节拍。

OSTickISR()首先在被中断任务堆栈中保存CPU寄存器的值,然后调用OSIntEnter()。

随后,OSTickISR()调用OSTimeTick(),检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。

OSTickISR()的最后调用OSIntExit(),如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,OSIntExit()将进行任务调度,此时OSIntExit()将不再返回调用者,而是用新任务的堆栈中的寄存器数值恢复CPU现场,实现任务切换。

如果当前中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,OSIntExit()将返回调用者OSTickISR(),最后由OSTickISR()返回被中断的任务。

OSTickISR()先关闭中断,然后清除timer0中断标记(只有清除当前中断标记才能够引发下一次中断)。

接着将调用IrqStart(),uC/OS-II要求在中断服务程序开头将记录中断嵌套层数的全局变量OSIntNesting加1。

随后OSTickISR()调用OSTimeTick(),检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。

然后调用IrqFinish()函数,IrqFinish()将调用OSIntExit()函数,如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,OSIntExit()将进行任务调度,并在OSIntCtxSw()函数中设置need_to_swap_context标记为1。

接下来OSTickISR()判断need_to_swap_context标记是否为1,如果为1则进行任务调度,将不再返回被中断的任务,而是用新任务的堆栈中的寄存器数值恢复CPU现场,然后实现任务切换。

如果当前中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,OSTickISR()将返回被中断的任务。

由于OSTickISR()是操作系统的核心,给出其代码和详细注释如下:

.GLOBALOSTickISR

OSTickISR:

STMDBsp!

{r0-r11,lr}@寄存器入栈,保护现场

mrsr0,CPSR@取当前状态CPSR

orrr0,r0,#0x80@置中断屏蔽位

msrCPSR_cxsf,r0@存CPSR,关中断

LDRr0,=I_ISPC@中断清除寄存器地址送r0

LDRr1,=BIT_TIMER0@timer0中断位送r1

STRr1,[r0]@清除timer0中断标志

BLIrqStart@实现OSIntNesting++

BLOSTimeTick@判断是否有延时等待任务就绪

BLIrqFinish@调用OSIntExit(),如是当前中断是中

@断嵌套最外层,置need_to_swap_context为1

LDRr0,=need_to_swap_context@取need_to_swap_context标志

LDRr2,[r0]

CMPr2,#1

LDREQpc,=_CON_SW@need_to_swap_context为1则任务切换

_NOT_CON_SW:

@notcontextswitching

LDMIAsp!

{r0-r11,lr}@need_to_swap_context为0则出栈

SUBSpc,lr,#4@不进行切换,直接返回

_CON_SW:

@setneed_to_swap_contextis'0'

MOVr1,#0@将need_to_swap_context清0

STRr1,[r0]

@nowcontextswitching

LDMIAsp!

{r0-r11,lr}@当前状态出栈

SUBlr,lr,#4@lr=lr-4

STRlr,SAVED_LR@保存lr

@ChangeSupervisormode

MRSlr,SPSR@切换到监控模式

ANDlr,lr,#0xFFFFFFE0

ORRlr,lr,#0x13

MSRCPSR_cxsf,lr

@NowSupervisormode

STRr12,[sp,#-8]@保存r12

LDRr12,SAVED_LR@存lr于r12

STMFDsp!

{r12}@r12入栈,表示当前任务的PC地址

SUBsp,sp,#4@减栈指针

LDMIAsp!

{r12}@恢复r12

STMFDsp!

{lr}@保存lr

STMFDsp!

{r0-r12}@保存寄存器r0~r12

MRSr4,CPSR@取当前状态CPSR

STMFDsp!

{r4}@保存CPSR

MRSr4,SPSR@取SPSR

STMFDsp!

{r4}@保存saveSPSR

@OSPrioCur=OSPrioHighRdy

LDRr4,addr_OSPrioCur@当前任务优先级地址送r4

LDRr5,addr_OSPrioHighRdy@高优先级任务的优先级地址送r5

LDRBr6,[r5]@更新当前任务优先级设为最高优先级

STRBr6,[r4]

@GetcurrenttaskTCBaddress

LDRr4,addr_OSTCBCur@取当前任务控制块地址

LDRr5,[r4]

STRsp,[r5]@在任务TCB中保存SP指针

@GethighestprioritytaskTCBaddress

LDRr6,addr_OSTCBHighRdy@取高优先级任务的TCB地址

LDRr6,[r6]

LDRsp,[r6]@获取新的任务的栈指针

@OSTCBCur=OSTCBHighRdy

STRr6,[r4]@设置新的当前任务TCB地址

LDMFDsp!

{r4}@新任务的SPSR出栈

MSRSPSR_cxsf,r4@恢复SPSR

LDMFDsp!

{r4}@新任务的CPSR出栈

MSRCPSR_cxsf,r4@恢复CPSR

LDMFDsp!

{r0-r12,lr,pc}@恢复寄存器、PC,新任务运行

2.退出临界区和进入临界区函数

他们分别是退出临界区和进入临界区的宏指令实现,用于在进入临界区之前关闭中断,在退出临界区的时候恢复原来的中断状态,在ARM中可以通过直接置位和清除当前状态寄存器CPSR中的中断屏蔽位来实现。

3.任务级上下文切换函数

任务级的上下文切换函数OS_TASK_SW(),它是当任务因为被阻塞而主动请求CPU调度时被执行,由于此时的任务切换都是在非异常模式下进行的,因此其实现类似于中断级别的任务切换又有所区别。

它的工作是先将当前任务的CPU现场保存到该任务堆栈中,然后获得最高优先级任务的堆栈指针,从该堆栈中恢复此任务的CPU现场,使之继续执行,这样就完成了一次任务切换。

4.OSStartHighRdy

OSStartHighRdy()函数是在OSStart()多任务启动之后,负责从最高优先级任务的TCB控制块中获得该任务的堆栈指针sp,通过sp依次将CPU现场恢复,这时系统就将控制权交给用户创建的该任务进程,直到该任务被阻塞或者被其他更高优先级的任务抢占CPU。

由于任务创建时堆栈的结构就是按照中断后的堆栈结构初始化的,执行中断返回指令就可实现任务的启动。

该函数仅仅在多任务启动时被执行一次,用来启动第一个,也就是最高优先级的任务执行。

在OSStartHighRdy()启动之前,必须先调用OSInit()函数进行系统初始化,并且至少已经创建一个任务。

.GLOBALOSStartHighRdy

OSStartHighRdy:

LDRr4,addr_OSTCBCur@获得当前TCB地址

LDRr5,addr_OSTCBHighRdy@获得最高优先级TCB地址

LDRr5,[r5]@获得最高优先级任务的堆栈地址

LDRsp,[r5]@将栈指针指向任务堆栈

STRr5,[r4]@将最高优先级任务堆栈送当前TCB

LDMFDsp!

{r4}@以下为出栈操作,相当于中断返回

MSRSPSR_cxsf,r4@从栈顶恢复SPSR

LDMFDsp!

{r4}@从栈顶恢复CPSR

MSRCPSR_cxsf,r4@

LDMFDsp!

{r0-r12,lr,pc}@恢复各寄存器,新任务地址送PC,

@实现最高优先级任务启动

1.3.5多任务应用程序的编写和移植测试

上述文件添加工作完成后,经编译、修正错误后,可以进行运行测试。

在工程中建立C语言入口函数Maim.c文件,在CPU初始化自后跳转至此,所有的C程序从这里开始运行,进行操作系统的初始化和多任务调度的启动。

它主要包括如下步骤

1、调用函数ARMTargetInit初始化ARM处理器;

2、调用OSInit进行操作系统初始化;

3、调用OSTaskCreate函数两个任务:

TaskLED和TaskSEG;

4、调用ARMTargetStart函数启动时钟节拍中断;

5、调用OSStart启动系统任务调度。

多任务可以在Main.c中依次建立,也可以先建立第一个任务,在该任务的执行过程中再建立其他任务,但在OSStart()之前必须至少建立一个用户任务。

这里每个任务可以是一个无限的循环,也可以是在一次执行完毕后被删除掉。

为了验证uC/OS-II的多任务管理性能,本测试程序建立了6个任务,分别实现数码管的实时时钟显示、LCD液晶屏的显示画面切换、4个LED灯按0.25s、0.5s、1s和2s的周期闪烁。

在传统的前后台系统中,这么多与时间相关的任务同时执行,其相互间的协调非常困难,而在uC/OS-II操作系统的支持下,只需向系统中添加具有不同延时的任务而已。

实际的运行效果如图3-所示,经长时间运行很稳定,证明了uC/OS-II操作系统的优越性。

3.2任务的划分原则

在多任务控制系统中,任务的划分是非常重要的,任务划分的过细会使任务间的耦合过紧,任务切换频繁,CPU额外消耗增加,任务划分过粗使原本并行执行的任务只能串行执行,降低了系统的吞吐量。

另外任务划分的合理还可以提高系统的实时性。

参考文献[22],在将一个软件系统分解成并行任务时,主要需考虑的是:

系统内功能的异步性,任务划分一般遵守H.GOMAA原则。

由文献[22]知H.GOMAA任务划分原则如下。

1、I/O依赖性[dependencyonI/O]:

因为依赖于I/O的任务速度会受到I/O设备的限制,所以尽量是有多少个I/O设备对应划分多少个任务。

2、时间关键性的功能[Time-criticalfunctions]:

这部分模块要

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 小学教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1