uc_os2简介

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

uC/OS-II Basic Programmers’Guide
本文面向首次接触uC/OS-II的程序员,为他们介绍一下这个系统的一些基本特征和编程上的注意事项,并介绍几个值得了解的API。

本文作者已经成功的将uC/OS-II移植到几种不同CPU之上。

包括EPSON S1C33和Sunplus unSP™等,积累了丰富的经验,现在愿意和朋友们分享这些经历。

希望本文的资料对于希望使用这个系统来开发的朋友有所帮助,作者乐意与您分享任何您成功的喜悦。

This passage is written for the basic programmers who are first developed with the uC/OS-II real time OS。

I will talk about the basic structure of this system。

And I will discuss how to use some of the useful API。

I will also discuss the imp of the mutilty-tasking in
uC/OS-II。

(一)uC/OS-II 简介
uC/OS-II是一种基于优先级的可抢先的硬实时内核。

自从92年发布以来,在世界各地都获得了广泛的应用,它是一种专门为嵌入式设备设计的内核,目前已经被移植到40多种不同结构的CPU上,运行在从8位到64位的各种系统之上。

尤其值得一提的是,该系统自从2.51版本之后,就通过了美国FAA认证,可以运行在诸如航天器等对安全要求极为苛刻的系统之上。

鉴于uC/OS-II可以免费获得代码,对于嵌入式RTOS而言,选择uC/OS无疑是最经济的选择。

(二)uC/OS-II应用程序基本结构
应用uC/OS-II,自然要为它开发应用程序,下面论述基于uC/OS-II的应用程序的基本结构以及注意事项。

每一个uC/OS-II应用至少要有一个任务。

而每一个任务必须被写成无限循环的形式。

以下是推荐的结构:
void task ( void* pdata )
{
INT8U err;
InitTimer(); // 可选
For( ;; )
{
// 你的应用程序代码
…….
……..
OSTimeDly(1); // 可选
}
}
以上就是基本结构,至于为什么要写成无限循环的形式呢?那是因为系统会为每一个任务保留一个堆栈空间,由系统在任务切换的时候换恢复上下文,并执行一条reti 指令返回。

如果允许任务执行到最后一个花括号(那一般都意味着一条ret指令)的话,很可能会破坏系统堆栈空间从而使应用程序的执行不确定。

换句话说,就是“跑飞”了。

所以,每一个任务必须被写成无限循环的形式。

程序员一定要相信,自己的任务是会放弃CPU 使用权的,而不管是系统强制(通过ISR)还是主动放弃(通过调用OS API)。

现在来谈论上面程序中的InitTimer()函数,这个函数应该由系统提供,程序员有义务在优先级最高的任务内调用它而且不能在for循环内调用。

注意,这个函数是和所使用的CPU相关的,每种系统都有自己的Timer初始化程序。

在uC/OS-II的帮助手册内,作者特地强调绝对不能在OSInit()或者OSStart()内调用Timer初始化程序,那会破坏系统的可移植性同时带来性能上的损失。

所以,一个折中的办法就是象上面这样,在优先级最高的程序内调用,这样可以保证当OSStart()调用系统内部函数OSStartHighRdy()开始多任务后,首先执行的就是Timer初始化程序。

或者专门开一个优先级最高的任务,只做一件事情,那就是执行Timer初始化,之后通过调用OSTaskSuspend()将自己挂起来,永远不再执行。

不过这样会浪费一个TCB空间。

对于那些RAM吃紧的系统来说,还是不用为好。

(三)一些重要的uC/OS-II API介绍
任何一个操作系统都会提供大量的API供程序员使用,uC/OS-II也不例外。

由于
uC/OS-II面向的是嵌入式开发,并不要求大而全,所以内核提供的API也就大多和多任务息息相关。

主要的有以下几类:
1)任务类
2)消息类
3)同步类
4)时间类
5)临界区与事件类
我个人认为对于初级程序员而言,任务类和时间类是必须要首先掌握的两种类型的API。

下面我就来介绍比较重要的:
1)OSTaskCreate函数
这个函数应该至少再main函数内调用一次,在OSInit函数调用之后调用。

作用就是创建一个任务。

目前有四个参数,分别是任务的入口地址,任务的参数,任务堆栈的首地址和任务的优先级。

调用本函数后,系统会首先从TCB空闲列表内申请一个空的TCB 指针,然后将会根据用户给出参数初始化任务堆栈,并在内部的任务就绪表内标记该任务为就绪状态。

最后返回,这样一个任务就创建成功了。

2)OSTaskSuspend函数
这个函数很简单,一看名字就该明白它的作用,它可以将指定的任务挂起。

如果挂起的是当前任务的话,那么还会引发系统执行任务切换先导函数OSShed来进行一次任务切换。

这个函数只有一个参数,那就是指定任务的优先级。

那为什么是优先级呢?事实上在系统内部,优先级除了表示一个任务执行的先后次序外,还起着分别每一个任务的作用,换句话说,优先级也就是任务的ID。

所以uC/OS-II不允许出现相同优先级的任务。

3)OSTaskResume函数
这个函数和上面的函数正好相反,它用于将指定的已经挂起的函数恢复成就绪状态。

如果恢复任务的优先级高于当前任务,那么还为引发一次任务切换。

其参数类似OSTaskSuspend函数,为指定任务的优先级。

需要特别说明是,本函数并不要求和OSTaskSuspend函数成对使用。

4)OS_ENTER_CRITICAL宏
很多人都以为它是个函数,其实不然,仔细分析一下OS_CPU.H文件,它和下面马上要谈到的OS_EXIT_CRITICAL都是宏。

他们都是涉及特定CPU的实现。

一般都被替换为一条或者几条嵌入式汇编代码。

由于系统希望向上层程序员隐藏内部实现,故而一般都宣称执行此条指令后系统进入临界区。

其实,它就是关个中断而已。

这样,只要任务不主动放弃CPU使用权,别的任务就没有占用CPU的机会了,相对这个任务而言,它就是独占了。

所以说进入临界区了。

这个宏能少用还是少用,因为它会破坏系统的一些服务,尤其是时间服务。

并使系统对外界响应性能降低。

5)OS_EXIT_CRITICAL宏
这个是和上面介绍的宏配套使用另一个宏,它在系统手册里的说明是退出临界区。

其实它就是重新开中断。

需要注意的是,它必须和上面的宏成对出现,否则会带来意想不到的后果。

最坏的情况下,系统会崩溃。

我们推荐程序员们尽量少使用这两个宏调用,因为他们的确会破坏系统的多任务性能。

6)OSTimeDly函数
这应该程序员们调用最多的一个函数了,这个函数完成功能很简单,就是先挂起当起当前任务,然后进行任务切换,在指定的时间到来之后,将当前任务恢复为就绪状态,但是并不一定运行,如果恢复后是优先级最高就绪任务的话,那么运行之。

简单点说,就是可以任务延时一定时间后再次执行它,或者说,暂时放弃CPU的使用权。

一个任务可以不显式的调用这些可以导致放弃CPU使用权的API,但那样多任务性能会大大降低,因为此时仅仅依靠时钟机制在进行任务切换。

一个好的任务应该在完成一些操作主动放弃使用权,好东西要大家分享嘛!
(四)uC/OS-II 多任务实现机制分析
前面已经说过,uC/OS-II是一种基于优先级的可抢先的多任务内核。

那么,它的多任务机制到底如何实现的呢?了解这些原理,可以帮助我们写出更加健壮的代码来。

由于我们面向的初级程序员,本文不打算写成又一篇uC/OS-II的源码分析,那样的文章太多了,本文打算从实现原理的角度探讨这个问题。

首先我们来看看为什么多任务机制可以实现?其实在单一CPU的情况下,是不存在真正的多任务机制的,存在的只有不同的任务轮流使用CPU,所以本质上还是单任务的。

但由于CPU执行速度非常快,加上任务切换十分频繁并且切换的很快,所以我们感觉好像有很多任务同时在运行一样。

这就是所谓的多任务机制。

由上面的描述,不难发现,要实现多任务机制,那么目标CPU必须具备一种在运行期更改PC的途径,否则无法做到切换。

不幸的使,直接设置PC指针,目前还没有哪个CPU支持这样的指令。

但是一般CPU都允许通过类似JMP,CALL这样的指令来间接的修改PC。

我们的多任务机制的实现也正是基于这个出发点。

事实上,我们使用CALL 指令或者软中断指令来修改PC,主要是软中断。

但在一些CPU上,并不存在软中断这
样的概念,所以,我们在那些CPU上,使用几条PUSH指令加上一条CALL指令来模拟一次软中断的发生。

回想一下你在微机原理课程上学过的知识,当发生中断的时候,CPU保存当前的PC和状态寄存器的值到堆栈里,然后将PC设置为中断服务程序的入口地址,再下来一个机器周期,就可以去执行中断服务程序了。

执行完毕之后,一般都是执行一条RETI指令,这条指令会把当前堆栈里的值弹出恢复到状态寄存器和PC里。

这样,系统就会回到中断以前的地方继续执行了。

那么设想一下?如果再中断的时候,人为的更改了堆栈里的值,那会发生什么?或者通过更改当前堆栈指针的值,又会发生什么呢?如果更改是随意的,那么结果是无法预料的错误。

因为我们无法确定机器下一条会执行些什么指令,但是如果更改是计划好的,按照一定规则的话,那么我们就可以实现多任务机制。

事实上,这就是目前几乎所有的OS的核心部分。

不过他们的实现不像这样简单罢了。

下面,我们来看看uC/OS-II再这方面是怎么处理的。

再uC/OS-II里,每个任务都有一个任务控制块(Task Control Block),这是一个比较复杂的数据结构。

在任务控制快的偏移为0的地方,存储着一个指针,它记录了所属任务的专用堆栈地址。

事实上,再uC/OS-II 内,每个任务都有自己的专用堆栈,彼此之间不能侵犯。

这点要求程序员再他们的程序中保证。

一般的做法是把他们申明成静态数组。

而且要申明成OS_STK类型。

当任务有了自己的堆栈,那么就可以将每一个任务堆栈再那里记录到前面谈到的任务控制快偏移为0的地方。

以后每当发生任务切换,系统必然会先进入一个中断,这一般是通过软中断或者时钟中断实现。

然后系统会先把当前任务的堆栈地址保存起来,仅接着恢复要切换的任务的堆栈地址。

由于哪个任务的堆栈里一定也存的是地址(还记得我们前面说过的,每当发生任务切换,系统必然会先进入一个中断,而一旦中断CPU就会把地址压入堆栈),这样,就达到了修改PC为下一个任务的地址的目的。

以上就是uC/OS-II的多任务实现机制,我们在这里大费笔墨谈论这个问题,是希望我们的程序员们可以善加利用这个机制,写出更健壮,更富有效率的代码来。

-------------------------------------------------------------------------------------------------
Uc/OS-II的任务切换机理及中断调度优化
摘要:μC/OS-II是一种适用于嵌入式系统的抢占式实时多任务操作系统,开放源代码,便于学习和使用。

介绍μC/OS-II在任务级和中断级的任务切换原理,以及这一操作系统基于嵌入式系统的对于中断的处理;相对于内存资源较少的单片机,着重讨论一种优化的实用堆栈格式和切换形式,以提高资源的利用率;结合MSP430单片机,做具体的分析。

关键词:实时多任务操作系统μC/OS MSP430 中断堆栈
引言
在嵌入式操作系统领域,由Jean J. Labrosse开发的μC/OS,由于开放源代码和强大而稳定的功能,曾经一度在嵌入式系统领域引起强烈反响。

而其本人也早已成为了嵌入式系统会议(美国)的顾问
委员会的成员。

不管是对于初学者,还是有经验的工程师,μC/OS开放源
代码的方式使其不但知其然,还知其所以然。

通过对于系统内
部结构的深入了解,能更加方便地进行开发和调试;并且在这
种条件下,完全可以按照设计要求进行合理的裁减、扩充、配
置和移植。

通常,购买RTOS往往需要一大笔资金,使得一般
的学习者望而却步;而μC/OS对于学校研究完全免费,只有在
应用于盈利项目时才需要支付少量的版权费,特别适合一般使
用者的学习、研究和开发。

自1992第1版问世以来,已有成
千上万的开发者把它成功地应用于各种系统,安全性和稳定性
已经得到认证,现已经通过美国FAA认证。

1 μC/OS-II的几大组成部分
μC/OS-II可以大致分成核心、任务处理、时间处理、任务
同步与通信,CPU的移植等5个部分。

核心部分(OSCore.c) 是操作系统的处理核心,包括操作
系统初始化、操作系统运行、中断进出的前导、时钟节拍、任
务调度、事件处理等多部分。

能够维持系统基本工作的部分都
在这里。

任务处理部分(OSTask.c) 任务处理部分中的内容都是与任务的操作密切相关的。

包括任务的建立、删除、挂起、恢复等等。

因为μC/OS-II是以任务为基本单位调度的,所以这部分内容也相当重要。

时钟部分(OSTime.c) μC/OS-II中的最小时钟单位是timetick(时钟节拍)。

任务延时等操作是在这里完成的。

任务同步和通信部分为事件处理部分,包括信号量、邮箱、邮箱队列、事件标志等部分;主要用于任务间的互相联系和对临界资源的访问。

与CPU的接口部分是指μC/OS-II针对所使用的CPU的移植部分。

由于μC/OS-II是一个通用性的操作系统,所以对于关键问题上的实现,还是需要根据具体CPU的具体内容和要求作相应的移植。

这部分内容由于牵涉到SP等系统指针,所以通常用汇编语言编写。

主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关处理部分等内容。

2 对于MSP430的中断处理
2.1 函数调用和中断调用的操作
MSP430最常使用的C编译器应该就是IAR Embedd-ed WorkBench。

对于这一编译器来说,通过分析和研究,发现它有以下规律。

(1)函数调用
如果是函数级调用,编译器会在函数调用时先把当前函数PC压栈,然后调用函数,PC值改变。

如果被调用的函数带有参数,那么,编译器按照以下的规则进行。

最左边的两个参数如果不是struct(结构体)或者union(联合体),将被赋值到寄存器,否则将被压栈。

函数剩下的参数都将被压栈。

根据最左边的那两个参数的类型,分别赋值给R12(对于32位类型赋值给R12:R13)和R14(对于32位类型赋值给R14:R15)。

(2)中断调用
如果是在中断中调用中断服务子程序的话,编译器将把当前执行语句的PC压栈,同时再把SR压栈。

接着,根据中断服务子程序的复杂程度,选择把R12~R15中的寄存器压栈。

然后,执行中断服务子程序。

中断处理结束后再把Rx寄存器出栈,SR出栈,PC出栈。

把系统恢复到中断前的状态,使程序接着被中断的部分继续运行。

2.2 任务级和中断级的任务切换步骤和原理
(1)任务级的任务切换原理
μC/OS-II是一个多任务的操作系统,在没有用户自己定义的中断情况下,任务间的切换步骤是这样的:任务间的切换一般会调用OSSched()函数。

函数的结构如下:
void OSSched(void){
关中断
如果(不是中断嵌套并且系统可以被调度){
确定优先级最高的任务
如果(最高级的任务不是当前的任务){
调用OSCtxSw();
}
}
开中断
}
我们把这个函数称作任务调度的前导函数。

它先判断要进行任务切换的条件,如果条件允许进行任务调度,则调用OSCtxSw()。

这个函数是真正实现任务调度的函数。

由于期间要对堆栈进行操作,所以OSCtxSw()一般用汇编语言写成。

它将正在运行的任务的CPU的SR寄存器推入堆栈,然后把R4~R15压栈。

接着把当前的SP保存在TCB->OSTCBStkPtr中,然后把最高优先级的TCB->OSTCBStkPtr 的值赋值给SP。

这时候,SP就已经指到最高优先级任务的任务堆栈了。

然后进行出栈工作,把R15~R4出栈。

接着使用RETI返回,这样就把SR和PC出栈了。

简单地说,μC/OS-II切换到最高优先级的任务,只是恢复最高优先级任务所有的寄存器并运行中断返回指令(RETI),实际上,所作的只是人为地模仿了一次中断。

(2)中断级的任务切换原理
μC/OS-II的中断服务子程序和一般前后台的操作有少许不同,往往需要这样操作:
保存全部CPU寄存器
调用OSIntEnter()或OSIntNesting++
开放中断
执行用户代码
关闭中断
调用OSIntExit();
恢复所有CPU寄存器
RETI
OSIntEnter()就是将全局变量OSIntNesting加1。

OSIntNesting是中断嵌套层数的变量。

μC/OS-II通过它确保在中断嵌套的时候,不进行任务调度。

执行完用户的代码后,μC/OS-II调用OSIntExit(),一个与OSSched()很像的函数。

在这个函数中,系统首先把OSIntNesting减1,然后判断是否中断嵌套。

如果不是的话,并且当前任务不是最高优先级的任务,那么找到优先级最高的任务,执行OSIntCtxSw()这一出中断任务切换函数。

因为,在这之前已经做好了压栈工作;在这个函数中,要进行R15~R4的出栈工作。

而且,由于在之前调用函数的时候,可能已经有一些寄存器被压入了堆栈。

所以要进行堆栈指针的调整,使得能够从正确的位置出栈。

3 使用μC/OS-II存在的问题和解决方法
由于μC/OS-II在应用的时候会占用单片机上的一些资源,如系统时钟、RAM、Flash或者ROM,从而减少了用户程序对资源的利用。

对于MSP430来说,RAM的占用是特别突出的问题。

对于8、16位的单片机来说,片内的RAM容量都很小,MSP430也是如此(最大的片内RAM也只有2KB,例如MSP430F149)。

如果使用扩展内存,会大大增加设计难度。

通过对μC/OS-II的分析可以得知,μC/OS-II占用的RAM主要是用在每个任务的TCB、每个任务的堆栈等方面。

通过进一步分析,发现任务堆栈大的原因是因为MSP430的硬件设计中没有把中断堆栈和任务堆栈分开。

这样就造成了在应用μC/OS-II的时候,考虑每个任务的任务堆栈大小时,不单单需要计算任务中局部变量和函数嵌套层数,还需要考虑中断的最大嵌套层数。

因为,对于μC/OS-II原始的中断处理的设计、中断处理过程中的中断嵌套中所需要压栈的寄存器大小和局部变量的内存大小,都需要算在每个任务的任务堆栈中,则对于每一个任务都需要预留这一部分内存,所以大量的RAM被浪费。

从这里可以看出,解决这一问题的直接方法就是把中断堆栈和每个任务自己的堆栈分开。

这样,在计算每个任务堆栈的时候,就不需要把中断处理中(包括中断嵌套过程中)的内存的占用计算到每个任务的任务堆栈中,只需要计算每个任务本身需要的内存大小,从而提高了RAM的利用率,可以缓解内存紧张的问题。

在这种设计方案中,中断堆栈区也就是利用原有的MSP430中的系统堆栈区。

在前后台的设计形式中,中断中的压栈和出栈的操作都是在系统的堆栈区完成的。

基于μC/OS-II的任务切换的原理,我们对于任务堆栈的功能和系统堆栈的功能做了以下划分:任务在运行过程中产生中断和任务切换的时候,PC和SR以及寄存器Rx都保存在各个任务自己的任务堆栈中;而中断嵌套产生的压栈和出栈的操作都是放在系统堆栈中进行的。

这种划分方式是基于尽量将中断任务与普通任务分开的思想设计的。

从前面对于IAR EW的默认操作分析来看,堆栈的结构可以有两种。

一种是把μC/OS-II的任务堆栈设计成图1所示的形式。

这种方法是把编译器默认的压栈操作放在前面,然后再把剩下的寄存器进栈。

但是,由于编译器在处理复杂程度不同的中断服务程序的时候,压入栈的寄存器的数量不定,所以会对以后其余寄存器的压栈和出栈操作增加复杂度。

这里,我们采用了图2所示的方式生成堆栈。

在这种堆栈中,PC和SR压栈后,通过调整SP指针,使得R4~R15寄存器覆盖编译器默认压栈的寄存器。

这样,处理的难度会小一点。

对于这样的设计方式,CPU必须能够:
◆有相应的CPU寄存器能够模仿SP的一些功能,能使用相应的指令来完成类似SP的一些操作;
◆作为SP使用的寄存器在编译过程中最好不被编译器默认使用。

在IAR的编译器中,有一个选项可以避免在编译过程中使用到R4、R5。

这两点MSP430都可以做到。

下面对一个正在运行的优先级为6的任务中断后,会发生的几种情况进行分析。

1)在中断的处理过程中没有更高优先级的中断产生,即不会产生中断嵌套。

图3所示为中断发生后对于任务优先级为6的任务堆栈所进行的操作。

中断发生后,PC和SR被系统压栈②,对于IAR C编译器来说,会按照复杂度不同的中断服务程序的要求,默认地进行一些寄存器的压栈操作③。

因为我们要求的堆栈格式是如图2所示的,我们要把SP调整到SR后面④,然后进行R4~R15的压栈操作,形成我们所要求的堆栈格式⑤。

进行任务堆栈的压栈工作以后,就可以调整SP的指针到系统堆栈了,如图4所示。

压栈后的SP指向最后一个压栈内容①。

我们把SP的值赋值给优先级6任务的TCB->OSTCBStkPtr,以便进行任务调度的时候出栈使用②。

接着,就把SP调整到系统堆栈处③。

在中断处理过程中,可能会出现压栈的操作,那么这种情况下SP的指针会随之移动。

由于现在是中断堆栈中,所以不会破坏任务堆栈的格式。

由于没有中断嵌套,在中断处理中没有别的中断发生,那么返回的步骤和上述的进栈操作正好相反。

在中断处理完了以后,SP会自动回到图4中③的SP位置。

接着,系统会查询到优先级最高的任务,然后把SP的指针移到优先级最高的任务的任务堆栈,进行R15~R4的出栈工作,最后用RETI中断返回指令返回到新的任务。

因为我们把所有的任务堆栈都规定成相同的格式,所以它们之间不会产生问题。

这里需要注意的是,因为系统在C编译器的中断处理中会对中断进入时默认压栈的寄存器出栈,所以在设计出栈的程序时,要先把这些内容压栈,这样才能正确出栈。

2)在中断的处理过程中,有别的中断产生,产生中断嵌套。

如图5所示,由于在处理中断的时候,SP已经被移到系统堆栈去了,只有当中断退出的时候才可能把SP移到别的任务的任务堆栈中。

所以在中断的时候进行中断嵌套,那么对于中断的处理和第一次是一样的,所不同的是,这次保存在堆栈中的不是任务运行中的寄存器,而是中断处理中的寄存器,而且是保存在系统堆栈中而不是任务堆栈中。

从这里就可以看出优化内存的效果。

所有的中断嵌套中的寄存器压栈都压在系统堆栈中,这样对于任务堆栈内存大小的要求大大降低。

因为μC/OS-II在进入中断中,会把
全局变量OSIntNesting++;在退出中
断的时候,又会把OSIntNesting--。


退出中断进行任务切换之前,μC/OS-II
会先判断OSIntNesting是否为0,是0
才会进行任务调度。

当第二中断运行结束
以后,退出中断嵌套的时候,
OSIntNesting不为0,也就不会进行任
务调度。

因此,仍旧在系统堆栈出栈,那
么系统会继续前面没有完成的中断服务程
序。

接着退出中断的顺序和非中断嵌套的
顺序是一样的。

在中断处理完以后,SP
会自动回到图4中③的SP位置。

接着,
系统会查询到优先级最高的任务,然后把
SP的指针移到优先级最高的任务的任务
堆栈。

进行R15~R4的出栈工作,最后
用RETI中断返回指令返回到新的任务。

中断的情况基本上就是上述两种。


于有些文献中提到的在中断中会调度到更
高优先级的任务的情况,笔者觉得是不应
该发生的。

因为从上面的分析可以看出,
默认的(μC/OS-II的设计思路)中断处
理会同时对全局变量OSIntNesting进行
增减处理,以给出是否需要任务调度的条
件。

那么即使在中断服务程序中把更高优
先级的任务就绪,也会等到中断退出以后再进行调度,除非是在中断中直接调用更高优先级的任务函数。

但这种方法应该是和μC/OS-II的原则相违背的,沿用的是以前前后台设计的思路。

对于这样的设计方式,时钟节拍的处理方式必须和一般的中断处理方式是一样的。

一般来说,MSP430使用WATCHDOG时钟中断作为时钟节拍的产生源。

从本质上来说,时钟节拍本身也是中断处理过程,所以对于时钟节拍的处理应该和其它的中断处理过程相同。

实际上,在时钟节拍的处理过程中也可能会存在中断嵌套的问题。

中断堆栈和任务堆栈分离设计的程序流程如图6所示。

4 几点建议
①编写中断程序的时候,有条件尽量使用汇编语言。

因为这样可以避免一些编译器自己进行的操作,减少指针调整的次数。

②在用C编写中断服务的时候,因为有些功能必须调用汇编的函数才能实现。

调用函数时,有些时候压栈的PC会破坏堆栈的结构。

这个时候需要把堆栈进行适当的调整,保证堆栈格式的正确。

③中断处理过程中调用OSIntExit()的时候,由于μC/OS-II的原始设计中SP指针有时是不调整
的,所以在OSIntExit()返回了以后,还要判断一下是否中断嵌套。

因为有的时候是需要切换任务的。

相关文档
最新文档