前言
有人说:“好代码要看,坏代码也要看”。由于工作过程中看多了凌乱的代码,特找来UCOS来看,一看果真是有美感的代码。关于嵌入式实时系统,起初发明嵌入式实时操作系统的发展进程我猜想应该是这样的:裸机编程时发现太多的“延时”资源没有被利用好,那么“延时”又完全可以用定时器中断来计时,在计时未到之前,则完全可以运行接下来的函数。再加上一些对任务是否需要执行的标志位的判断,对各个任务的轮询执行,这样一个最基本的“系统”便出现了。而当任务逐渐增多时,有时候无法保证最需要运行的那个任务就立即会被执行,那么使用定时器中断调度任务的嵌入式实时操作系统便被发明了出来。
UCOS算是RTOS中的典型。学习UCOS入门推荐书籍任哲的《嵌入式实时操作系统μCOS-II原理及应用》,这本书适合从头到尾看。而UCOS的权威邵贝贝的《嵌入式实时操作系统uCOS-II》,则适合遇到哪里不懂或者查看源代码有疑问时翻阅。
本文基于S3C2440的开发板移植好的UCOS II,本人也是刚学习UCOS,以下都是我个人的理解,如果有误导到大家敬请指出。
UCOS的移植
虽然网上有很多移植好的版本,但是我认为了解如何移植一个系统,是了解这个系统很好的切入点。移植是因为各个CPU之间的架构不同,UCOS给我们预留了三个文件:OS_CPU.H、Os_cpu_a.s、Os_cpu_c.c。也就是说用汇编写的代码放进.s中,用C写的放进.c中,而H文件是对数据类型的定义和宏定义。关于这三个文件需要定义什么,教材中都有很详细的解释,无非是堆栈增长方向、进出临界区的方式、任务切换进入的方式、时钟节拍函数、堆栈的初始化、钩子函数相关的内容。这些都是和最底层的CPU架构相关。
而因为以上内容,如在S3C2440的启动文件中也需要一处做修改,即IRQ中断发生后的跳转函数不能再像如下裸机一样这么简单,需要另外加一些内容,这是因为在UCOS中任何外部中断都可能会触发任务调度。
; Setup IRQ handler
ldr
r0,=HandleIRQ
;This routine is needed
ldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
ldr r1,=IsrIRQ ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
其中 IsrIRQ 函数需要修改。
在UCOS中必须要有一个定时器中断,以供UCOS准确延迟,假设除了定时器没有任何外部中断的情况下,如果优先级最高的那个任务中没有调用延迟函数的话,那么永远都不会发生任务的切换,所以UCOS规定每个任务都必须要有一定时间的延迟函数。我们必须设置好一个定时器pISR_TIMER0= (uint32) OSTickISR;并编写好OSTickISR函数,在OSTickISR函数中调用UCOS本身的OSTimeTick()函数即可。
任务的创建
任务创建调用OSTaskCreate函数即可。通过向此函数传递的参数我们就可以了解到UCOS中一个任务的基本组成部分,需要传递该任务的函数名、函数的参数、该任务的堆栈指针指向、该任务的优先级。在OSTaskCreate函数中会对该堆栈进行初始化,并且在OS_TCB链表中找出一个空闲OS_TCB结构(当然当前没有空闲就报错了),并根据以上信息对此OS_TCB结构体进行初始化,这个OS_TCB结构体就代表这个任务的新建。
任务的切换
OS的核心在于任务的安排和切换。在OS_Sched函数中的OSCtxSw这个函数完成了任务的切换。UCOS中有任务级切换OSCtxSw和中断级切换OSIntCtxSw,一个是在任务执行过程中调用的,一个是在中断中被调用的。首先我们来看一下OSCtxSw的内容