uCOS-II的任务切换机理及中断调度优化
ucosII任务切换是怎样实现的

UC/OS-II学习笔记之——任务切换是怎样实现的问题是,o Uc/OS-II如何切换任务?通过任务调度器OS_Sched(),那么谁在调用这个函数?o CPU在这里肯定有作用,因为任务切换必然涉及到CPU寄存器的入栈和出栈,那么这一块工作是如何完成的?书上讲:为了做到任务切换,运行OS_TASK_SW(),人为模仿一次中断。
中断服务子程序或陷阱处理(trap hardler),也称作事故处理(exception handler),必须给汇编语言函数OSCtxSw()提供中断向量[1.92]。
那么,“人为模仿一次中断”是什么意思?是指:OS_TASK_SW()触发了一个中断,由中断完成了任务切换?阅读源代码,查找答案……#define OS_TASK_SW()OSCtxSw()//这是一个宏调用,定义在os_cpu.h,Os_cpu_a.asm中定义了OSCtxSwNVIC_INT_CTRL EQU0xE000ED04NVIC_PENDSVSET EQU0x10000000;******************************************************************************;PERFORM A CONTEXT SWITCH(From task level);void OSCtxSw(void);;Note(s):1)OSCtxSw()is called when OS wants to perform a task context switch.This function;triggers the PendSV exception which is where the real work is done.;******************************************************************************OSCtxSwLDR R0,=NVIC_INT_CTRL;Trigger the PendSV exception(causes context switch) LDR R1,=NVIC_PENDSVSETSTR R1,[R0]BX LR注释说明该段汇编代码触发了一个PendSV的异常。
uCOS-II的任务切换机理及中断调度优化

uCOS-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) 任务处理部分中的内容都是与任务的操作密切相关的。
包括任务的建立、删除、挂起、恢复等等。
uCOS-II中的任务切换机制

uCOS-II中的任务切换机制【@.1 函数周期与死循环】一般函数的生命周期很简单,从开始调用函数起,直到函数返回,即结束。
这样一来就完成了这个函数的使命,它也就不再需要了。
对于一般的函数就是这样,但是回过头想想,对于一个系统、OS、或者工业控制中的一个控制器重的系统个,函数返回是很轻易很随便的就能返回吗?返回就意味着函数结束,死亡,若是想系统这样一个很大的函数,它的返回就意味着系统结束。
因此,对于系统的函数返回有些时候我们不希望它返回,返回时是需要好好设计的,像嵌入式中的控制程序我们也并不需要它返回,直接关机就好了。
因此,一个系统往往就是一个很大的循环,不停的扫描,而我们编程的时候对于这个死循环是需要好好设计的。
考虑以下一个控制要求,@.按键控制电机启、停、正转反转,并每秒发送CAN报文报告当前情况。
我们可以有多种方法实现这一要求:方法一:每次在循环体重扫描当前按键的电平,从而进入对应的控制电机函数,如果所有电平都没有信号则直接进入下一个循环。
发送CAN报文就直接用一个定时中断。
这样的好处就是编程简单直白,每次循环进入不同的电机控制函数,坏处很明显,一定要等待到下一个循环才能进入其他的电机控制函数,每次循环的时间不好控制,不管你用函数指针还是if/else来判断,每次循环一定要等待电机动作结束才能进入下一个循环。
方法二:改用外部中断来处理按键。
仅当按键按下时触发外部中断,从而控制响应的电机进行操作。
这样的好处就是循环体简单,可以仅仅就是一个计数器加一,所有控制都等中断来实现。
但这样带来的问题也很明显,就是中断嵌套问题。
比如当电机正转时按下停止按钮,这时由于是在中断中,停止按钮是否真的能够得到响应?这就涉及到中断嵌套问题,并不见得所有CPU都能支持中断嵌套,我的这一篇文章对中断嵌套问题进行了一个讨论。
方法三:采用RTOS的思想,加入任务调度系统。
每次任务调度系统就是一个小小的循环,对于各个任务进行轮询,当这次轮询发现某任务是已经就绪的优先级最高的任务,则交给CPU处理,所有中断与任务,任务与任务之间有通讯机制可以交换信息。
Ucos-II基础原理讲解,任务创建及中断问题

Ucos-II基础原理讲解,任务创建及中断问题2013-6-26 周三Haibara AI Ucos-II在移植过程中的特性,首先要理解所加入的实时操作系统是一个给予定时器节拍的系统。
怎么理解这个问题呢,由该定时器产生脉冲来驱动不同的任务调度,且由于该系统是基于可剥夺内核类型,任务之间的切换时间间隔也由该定时器完成。
不宜过大,否则会造成CPU利用率不高,不宜过小,否则会造成CPU在执行任务时非常被动,时间特别赶。
就想人的心脏一样,动力是固定的,你非要去跑个100公里,心脏不加速你是会窒息而死的。
我们正常心脏跳动是60次左右,同样,单片机也需要一个这样的相对宽松的节拍驱动。
注:众所周知,脉冲本质上仍由晶振提供(或实时时钟),原理都一样,再次请各位不要较真,就暂时以晶振为例。
大家可以考虑一下,晶振的作用是否特别类似于人体的心脏呢?答案是很显然的。
心脏跳动给人体提供输送血液和养料的压力,晶振的设计原理也正是源于此。
他可以像人体心脏一样提供动力输送各个外设(相当于人体器官)所需要的能量。
说这么多,就是要大家理解,人可以实时做出反应,因为人是有生命的,也就是他有反应、判断和处理能力,可以决定什么时间应该做什么。
但是单片机是没有生命的,换句话说,他所谓的处理能力源于人们的代码和程序。
那么,怎么让单片机像人一样拥有生命呢?答案很简单,给他指令,帮助他决定什么时间该做什么,这也就是所谓的实时操作系统。
不只是ucos,其他实时操作系统也是这个道理。
下面我以代码为例,讲述一下ucos的工作流程,如有错误,还请各位前辈指点。
首先任务是怎么样开始执行的?os_err = OSTaskCreateExt((void (*)(void *)) App_TaskStart, */1 (void * ) 0, 2(OS_STK * )&App_TaskStartStk[APP_TASK_START_STK_SIZE - 1],3(INT8U ) APP_TASK_START_PRIO,4(INT16U ) APP_TASK_START_PRIO,5(OS_STK * )&App_TaskStartStk[0],6(INT32U ) APP_TASK_START_STK_SIZE,7(void * )0,8(INT16U )(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));9这个东西好像是非常复杂,但是实际上并不需要细究(当然非要细究也是允许的,只不过是C语言而已,看也不是看不懂,对于初学者而言并不推荐而已)。
实验二-UCOS-II任务管理

班级学号姓名同组人实验日期室温大气压成绩实验二 UCOS-II任务管理一、实验目的1、掌握UCOS-II中任务管理的函数的应用。
2、掌握UCOS-II在STM32平台下对硬件的控制。
3、掌握开发UCOS-II应用的程序结构。
二、实验步骤1、UCOSII工作原理UCOSII提供系统时钟节拍,实现任务切换和任务延时等功能。
这个时钟节拍由OS_TICKS_PER_SEC(在os_cfg.h中定义)设置,一般我们设置UCOSII的系统时钟节拍为1ms~100ms。
本次实验利用STM32的SYSTICK定时器来提供UCOSII时钟节拍。
UCOSII的任何任务都是通过一个叫任务控制块(TCB)的东西来控制的,每个任务管理块有3个最重要的参数:(1)任务函数指针;(2)任务堆栈指针;(3)任务优先级。
在UCOSII中,使用CPU的时候,优先级高(数值小)的任务比优先级低的任务具有优先使用权,即任务就绪表中总是优先级最高的任务获得CPU使用权,只有高优先级的任务让出CPU使用权(比如延时)时,低优先级的任务才能获得CPU使用权。
UCOSII不支持多个任务优先级相同,也就是每个任务的优先级必须不一样。
任务的调度其实就是CPU 运行环境的切换,即:PC指针、SP指针和寄存器组等内容的存取过程UCOSII的每个任务都是一个死循环。
每个任务都处在以下5种状态之一的状态下,这5种状态是:睡眠状态、就绪状态、运行状态、等待状态(等待某一事件发生)和中断服务状态。
睡眠状态,任务在没有被配备任务控制块或被剥夺了任务控制块时的状态。
就绪状态,系统为任务配备了任务控制块且在任务就绪表中进行了就绪登记,任务已经准备好了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行,这时任务的状态叫做就绪状态。
运行状态,该任务获得CPU使用权,并正在运行中,此时的任务状态叫做运行状态等待状态,正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务就会把CPU的使用权让给别的任务而使任务进入等待状态。
uCOSii中断处理过程详解

一. UCOSII的中断过程简介系统接收到中断请求后,如果CPU处于开中断状态,系统就会中止正在运行的当前任务,而按中断向量的指向去运行中断服务子程序,当中断服务子程序运行完成后,系统会根据具体情况返回到被中止的任务继续运行,或转向另一个中断优先级别更高的就绪任务。
由于UCOS II是可剥夺型的内核,所以中断服务程序结束后,系统会根据实际情况进行一次任务调度,如果有优先级更高的任务,就去执行优先级更高的任务,而不一定要返回被中断了的任务。
二.UCOSII的中断过程的示意图三.具体中断过程1.中断到来,如果被CPU识别,CPU将查中断向量表,根据中断向量表,获得中断服务子程序的入口地址。
2.将CPU寄存器的内容压入当前任务的任务堆栈中(依处理器的而定,也可能压入被压入被中断了的任务堆栈中。
3.通知操作系统将进入中断服务子程序。
即:调用OSIntEnter()或OSIntNesting直接加1。
4.If(OSIntNesting==1){OSTCBCur->OSTCBStrPtr=SP;} //如果是第一层中断,则将堆栈指针保存到被中断任务的任务控制块中5.清中断源,否则在开中断后,这类中断将反复的打入,导致系统崩贵6.执行用户ISR7.中断服务完成后,调用OSIntExit().如果没有高优先级的任务被中断服务子程序激活而进入就绪态,那么就执行被中断了的任务,且只占用很短的时间.8.恢复所有CPU寄存器的值.9.执行中断返回指令.四.相关代码与编译器相关的数据类型:typedef unsigned char BOOLEAN;typedef unsigned char INT8U;typedef unsigned int OS_STK; //堆栈入口宽度为16 位(一) void OSIntEnter (void)的理解uCOS_II.H中定义:#ifdef OS_GLOBALS#define OS_EXT#else#define OS_EXT extern#endif //定义全局宏OS_EXT#ifndef TRUE#define TRUE 1#endifOS_EXT BOOLEAN OSRunning; //定义外部BOOLEAN类型全局变量,用来指示//核是否在运行OS_EXT INT8U OSIntNesting;//定义外部8位无符号整型数全局变量,用来表//示中断嵌套层数OS_CORE.C中的OSIntEnter()函数原型:void OSIntEnter (void){if (OSRunning == TRUE) //如果内核正在运行则进入if{if (OSIntNesting < 255) //如果嵌套层数小于255,则可以继//续{OSIntNesting++; //嵌套层数加1}}}(二)在中断服务子程序中加if ( OSIntNesting == 1){…}的原因uCOS_II.H中定义:typedef struct os_tcb {OS_STK *OSTCBStkPtr;//声明指向任务堆栈栈顶的16位指针………………} OS_TCB;//定义名为OS_TCB的结构体数据类型,即任务控制块的数据结构OS_EXT OS_TCB *OSTCBCur;//声明一个指向任务控制块的全局指针变量//用于指向当前任务的任务控制块中断服务程序中添加的代码:if ( OSIntNesting == 1){OSTCBCur->OSTCBStkPtr = SP; // 如果是第一层中断,则将被中断任务//的堆栈指针保存在被中断任务的任务//任务控制块中}关于uCOS-II的中断服务程序(ISR)中必须加“OSIntNesting == 1”的原因==避免调整堆栈指针.出现这个问题的根源是当低优先级的任务被中断,当中断完成后由于有高优先级的任务就绪,则必须调度高优先级的任务,原来的低优先级任务继续被中断着,但是此时的低优先级任务的堆栈已经被破坏,已不能被调度程序直接调度了,要想被调度而必须调整堆栈指针。
μCOSII 内核任务调度模块的分析与改进

μC/OSII内核任务调度模块的分析与改进作者:■ 沈阳工业大学张旭牛连强付红旭摘要:关键词:引言:现今,嵌入式系统的应用已十分广泛,也是新兴计算模式如普适计算等的支撑技术之一。
在嵌入式技术发展的初期,由于嵌入式系统控制的外设和执行的任务较少,利用简单的循环和少量模块调用即可满足要求。
随着应用的复杂化,多数嵌入式操作系统都采用了微内核结构。
在当前较流行的操作系统如VxWor ks、QNX、pSOS、WindowsCE、μC/OSII以及RTlinux中,μC/OSII是一个完整、可移植、可固化、可裁减的抢占式多任务内核,可用于各类8位、16位和32位单片机和DSP[1]。
微内核与具体硬件环境相结合构成了嵌入式系统,硬件环境则通常由单片机为核心配合一些辅助电路和外围设备构成[2]。
在嵌入式系统中使用的单片机较多,主要有80C51、AVR和ARM等系列。
其中,ARM系列的应用较为广泛,并包括多个系列,尤其是ARM7系列更多用于多媒体和嵌入式设备,包括Internet设备、网络和调制解调器设备以及移动电话和PDA等无线设备,且开发环境容易获得,价格也较低廉。
任务调度模块是嵌入式内核中最为核心的部分,任务调度算法的好坏以及执行效率直接关系到嵌入式内核的应用范围及实时性程度[3]。
任务调度算法中,主要涉及的问题包括任务切换、现场保护以及优先级的确定和任务调度方法等。
在应用中,必须将μC/OSII内核中与此相关的代码移植到具体硬件环境下。
理论上,可以有多种任务调度模块的移植方法,但选择一种适合硬件环境的方法至关重要。
此外,由于μC/ OSII任务调度模块采用可抢占式的固定优先级分配方法,适用于较为简单的应用[4],而在实时系统中还有多种优先级的分配要求,这就要求在实际应用中对μC/OSII的任务调度模块加以改造,以适应不同的优先级分配要求。
当然,改造要以不破坏原系统的高效性为前提。
1 内核移植分析μC/OSII内核较小,全部代码量约6000~7000行,共15个文件,其中90%的代码用C语言完成,已被移植到40多种单片机上[1]。
嵌入式实时操作系统μCOSII原理及应用习题答案(第四版)

嵌入式实时操作系统μCOSII原理及应用习题答案(第四版)嵌入式实时操作系统μCOSII原理及应用习题答案(第四版)嵌入式操作系统是一种特殊的操作系统,用于控制和管理嵌入式系统。
实时操作系统(RTOS)是一种在给定的时间约束下,能够及时响应外部事件的操作系统。
μC/OS-II是一种广泛应用于嵌入式系统的实时操作系统。
本文将介绍μC/OS-II的原理及应用,并提供第四版的习题答案。
一、μC/OS-II原理1. 任务(Task)管理:μC/OS-II采用优先级抢占式调度算法,支持多任务。
每个任务具有自己的任务控制块(TCB),用于记录任务的状态、优先级、堆栈等信息。
任务之间可以通过任务切换进行调度,具有不同的优先级来确保系统的实时性。
2. 信号量(Semaphore)机制:信号量用于任务之间的同步和互斥操作。
μC/OS-II提供了两种信号量机制:二值信号量和计数信号量。
二值信号量用于任务之间的互斥操作,而计数信号量用于任务之间的同步操作。
3. 事件标志组(Event Flag Group)机制:事件标志组用于任务之间的同步和通信操作。
一个事件标志组中可以包含多个事件标志位,每个标志位都可以独立设置或清除。
任务可以等待一个或多个事件标志位的发生,并在发生时得到通知。
4. 消息邮箱(Mailbox)机制:消息邮箱用于任务之间的通信。
每个消息邮箱中可以存放一个或多个消息,任务可以通过发送和接收消息来进行通信。
消息邮箱还支持阻塞和非阻塞两种方式。
5. 定时器(Timer)管理:μC/OS-II提供了软件定时器的功能,可以设置定时器来触发任务或其他操作。
定时器可以基于时间片、滴答定时器或硬件定时器实现。
二、μC/OS-II应用1. 实时任务调度:μC/OS-II可以在多个任务之间进行优先级调度,保证任务的及时执行。
通过设置任务的优先级和时间片,可以确保高优先级任务优先执行,从而满足实时性要求。
同时,μC/OS-II还提供了任务切换和上下文切换机制,确保任务之间的切换及时有效。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
摘要:μ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寄存器RETIOSIntEnter()就是将全局变量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中断返回指令返回到新的任务。