实验6 uCOSII在ARM上的移植.docx
- 文档编号:20748028
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:18
- 大小:283.95KB
实验6 uCOSII在ARM上的移植.docx
《实验6 uCOSII在ARM上的移植.docx》由会员分享,可在线阅读,更多相关《实验6 uCOSII在ARM上的移植.docx(18页珍藏版)》请在冰豆网上搜索。
实验6uCOSII在ARM上的移植
实验六uC/OS-II在ARM上的移植
一、实验目的
1.了解UC/OS-II内核的主要结构。
2.掌握将UC/OS-II内核移植到ARM9处理器上的基本方法。
二、预备知识
1.掌握在ADS1.2集成开发环境中编写和调试程序的基本过程。
2.会使用UltraEdit编辑C语言源程序。
3.了解ARM9处理器的结构。
4.了解UC/OS-II系统结构。
三、uc/os简介
uC/OS-II是支持微处理器和微控制器的具有可移植性,可剪裁,抢先实时,多任务管理的微内核实时操作系统。
z可移植性
体现在绝大部分的uC/OS-II的源代码是用移植性很强的标准C语言写的;源程序中只有和微处理器硬件相关的那部分使用汇编语言写,并且这些汇编代码已经压缩到最低限度;
uC/OS-II可在绝大多数8位、16位、32位、64位微处理器、微控制器、数字信号处理器(DSP上运行。
z可剪裁
当用户不需要使用uC/OS-II的全部功能模块时,可以在应用程序中通过语句来定义所需的uC/OS-II功能模块,以减少不必要的存储器空间的开支。
z抢先实时
总是运行就绪条件下优先级最高的任务。
大多数商业操作系统内核都是占先式的。
z多任务管理
uC/OS-II可以管理64个任务,其中8个系统任务,应用程序最多可以有56个任务。
它的每个任务的优先级都不相同。
z中断管理
中断可以使正在执行的任务暂时挂起,如果优先级更高的任务被该中断唤醒,则高优先级的任务在中断嵌套全部退出后立即执行,中断嵌套层数可达255层。
除以上介绍的特点外,uC/OS-II还包括其它一些特点,如:
公开源代码、可固化、可确定性、任务栈、提供很多系统服务、稳定性和可靠性强等。
四、uC/OS-II的体系结构
uC/OS-II包括三个部分,应用软件基于uC/OS-II之上,uC/OS-II的第一部分是核心代码部分,这部分代码与处理器无关,包括七个源代码文件和一个头文件,七个源代码文件分别是核心部分,包括事件的管理,消息队列的管理,存储管理,消息管理,信号量处理,任务调度和定时管理。
uC/OS-II的第二部分是设置代码部分,包括两个头文件,用户在设置代码中可以配置事件控制块的数目以及是否包含消息管理相关的代码等。
uC/OS-II的第三部分是与处理器的移植代码部分,这一部分包括一个头文件,一个汇编文件和一个C代码文件,在uC/OS-II的移植过程中,用户所需要关注的就是这三个文件。
五、uC/OS-II的移植条件
移植uC/OS-II到处理器上必须满足以下几个条件:
处理器的C编译器能产生可重入代码,可重入代码指的是可以被多个任务同时调用,而不会破坏数据的一段代码,或者说代码具有在执行过程中打断后再次被调用的能
力。
用C语言就可以打开和关闭中断。
uC/OS-II在C语言代码中通过使用以下两个宏
OS_ENTER_CRITICAL(
OS_EXIT_CRITICAL(
打开和关闭中断,从而保护临界代码,这是uC/OS-II要求用C语言可以打开关闭中断的原因。
处理器支持中断并且能产生定时中断;
处理器支持容纳一定量数据的硬件堆栈,比如说有些8位控制器只有10根地址线,最多可访问1K存储单元,这样的条件下移植就很困难;
处理器有将堆栈指针和其他CPU寄存器读出和存储到堆栈或内存中的指令,ARM处理器中汇编指令stmfd可以将所有寄存器压栈,对应也有一个出栈的指令ldmfd;
六、移植工作包括以下几个内容:
1.用#define设置一个常量的值(OS_CPU.H。
2.声明10个数据类型(OS_CPU.H。
3.用#define声明三个宏(OS_CPU.H。
4.用C语言编写六个简单的函数(OS_CPU_C.C。
5.编写四个汇编语言函数(OS_CPU_A.ASM。
移植uCOS分三个步骤,依次是常量和宏定义、移植汇编代码文件OS_CPU_A.ASM、移植C语言源代码文件OS_CPU_C.C。
(1常量和宏定义
首先是定义与编译器相关的数据类型,uC/OS-II为了保证可移植性,程序中没有直接使用int,unsingedint等定义,而是自己定义了一套数据类型,如INT16U表示16位无符号整型,对于ARM这样的32位内核,INT16U是unsignedshort型,如果是16位的处理器,则是unsignedint型。
其次是定义允许和禁止中断。
然后是定义栈的增长方向。
最后是定义OS_TASK_SW宏,这个宏是uC/OS-II从低优先级任务切换到高优先级任务时调用,可以采用下面两种方式定义,如果处理器支持软中断,可以使用软中断将中断向量指向OSCtxSw函数,或者直接调用OSCrxSw函数。
关于OSCtxSW函数,我们将在下一节提到。
(2移植汇编代码文件OS_CPU_A.ASM
OS_CPU_A.ASM文件里面有四个函数需要移植。
第一个函数是OSStartHighRdy,这个函数由OSStart函数调用,OSStart(负责使就绪状态的任务开始运行,其中OSStartHighRdy负责获取新任务的堆栈指针并从堆栈指针中恢复新任务的所有处理器寄存器。
这个函数要移植的原因就是因为它涉及到保存处理器寄存器到堆栈,这是我们前面提到的处理器支持移植的条件。
第二个函数是OSCtxSw,这个函数由OS_TASK_SW宏调用,OS_TASK_SW由OSSched(调用,OSSched(负责任务之间的切换;
OSCtxSw(在OSSched(函数中负责保存当前任务对应的处理器寄存器到堆栈中,并将需要恢复的任务对应的处理器寄存器从堆栈中恢复出来。
第三个函数是OSIntCtxSw,这个函数由OSIntExit(调用,OSTickISR由OSTickISR调用,负责在定时中断中的任务之间的切换,刚才讲的函数OSCtxSw和本函数都时负责任务之间的切换,区别主要就在于一个在定时中断中间负责,一个不是。
OSIntCtxSw(主要保存当前任务堆栈指针,并将新任务对应的处理器寄存器从堆栈中恢复出来。
(3移植C语言源代码文件OS_CPU_C.C
源文件OS_CPU_C.C中有六个函数需要移植,对于其中五个HooK函数,它们又称为钩子函数,主要用来扩展uC/OS-II功能,必须声明,但并不一定要包含任何代码。
所以唯一必需移植的函数只有OSTaskStkInit函数,这个函数在任务创建时调用,负责初始化任务的堆栈结构。
这个函数在几乎所有型号的ARM处理器中移植时都可以采用一种形式,大家在移植中可以使用。
完成了上述工作以后,μCOS-II就可以正常运行在ARM处理器上了。
附录
1.0uC/OS-II实时系统的概念
实时系统的特点是,如果逻辑和时序出现偏差将会引起严重后果的系统。
有两种类型的实时系统:
软实时系统和硬实时系统。
在软实时系统中系统的宗旨是使各个任务运行得越快越好,并不要求限定某一任务必须在多长时间内完成。
在硬实时系统中,各任务不仅要执行无误而且要做到准时。
大多数实时系统是二者的结合。
实时系统的应用涵盖广泛的领域,而多数实时系统又是嵌入式的。
这意味着计算机建在系统内部,用户看不到有个计算机在系统里面。
2.0前后台系统(Foreground/BackgroundSystem
不复杂的小系统一般设计成如图2.1所示的样子。
这种系统可称为前后台系统或超循环系统(Super-Loops。
应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background。
中断服务程序处理异步事件,这部分可以看成前台行为(foreground。
后台也可以叫做任务级。
前台也叫中断级。
时间相关性很强的关键操作(Criticaloperation一定是靠中断服务来保证的。
因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理,这种系统在处理信息的及时性上,比实际可以做到的要差。
这个指标称作任务级响应时间。
最坏情况下的任务级响应时间取决于整个循环的执行时间。
因为循环的执行时间不是常数,程序经过某一特定部分的准确时间也是不能确定的。
进而,如果程序修改了,循环的时序也会受到影响。
图2-1前后台系统
很多基于微处理器的产品采用前后台系统设计,例如微波炉、电话机、玩具等。
在另外一些基于微处理器的应用中,从省电的角度出发,平时微处理器处在停机状态(halt,所有的事都靠中断服务来完成。
2.0代码的临界段
代码的临界段也称为临界区,指处理时不可分割的代码。
一旦这部分代码开始执行,则不允许任何中断打入。
为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。
2.1资源
任何为任务所占用的实体都可称为资源。
资源可以是输入输出设备,例如打印机、键盘、显示器,资源也可以是一个变量,一个结构或一个数组等。
2.2共享资源
可以被一个以上任务使用的资源叫做共享资源。
为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。
这叫做互斥(mutualexclusion。
在2.18节“互斥”中,将对技术上如何保证互斥条件做进一步讨论。
2.3多任务
多任务运行的实现实际上是靠CPU(中央处理单元在许多任务之间转换、调度。
CPU只有一个,轮番服务于一系列任务中的某一个。
多任务运行很像前后台系统,但后台任务有多个。
多任务运行使CPU的利用率得到最大的发挥,并使应用程序模块化。
在实时应用中,多任务化的最大特点是,开发人员可以将很复杂的应用程序层次化。
使用多任务,应用程序将更容易设计与维护。
2.4任务
一个任务,也称作一个线程,是一个简单的程序,该程序可以认为CPU完全只属该程序自己。
实时应用程序的设计过程,包括如何把问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务被赋予一定的优先级,有它自己的一套CPU寄存器和自己的栈空间(如图2.2所示。
图2.2多任务
典型地、每个任务都是一个无限的循环。
每个任务都处在以下5种状态之一的状态下,这5种状态是休眠态,就绪态、运行态、挂起态(等待某一事件发生和被中断态(参见图2.3休眠态相当于该任务驻留在内存中,但并不被多任务内核所调度。
就绪意味着该任务已经准备好,可以运行了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行。
运行态的任务是指该任务掌握了CPU的控制权,正在运行中。
挂起状态也可以叫做等待事件态WAITING,指该任务在等待,等待某一事件的发生,(例如等待某外设的I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等等。
最后,发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,就进入了被中断状态。
图2.3表示μC/OS-Ⅱ中一些函数提供的服务,这些函数使任务从一种状态变到另一种状态。
图2.3任务的状态
2.5任务切换(ContextSwitchorTaskSwitch
ContextSwitch在有的书中翻译成上下文切换,实际含义是任务切换,或CPU寄存器内容切换。
当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context,即CPU寄存器中的全部内容。
这些内容保存在任务的当前状况保存区(Task’sContextStoragearea,也就是任务自己的栈区之中。
(见图2.2。
入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。
这个过程叫做任务切换。
任务切换过程增加了应用程序的额外负荷。
CPU的内部寄存器越多,额外负荷就越重。
做任务切换所需要的时间取决于CPU有多少寄存器要入栈。
实时内核的性能不应该以每秒钟能做多少次任务切换来评价。
2.6内核(Kernel
多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之间的通讯。
内核提供的基本服务是任务切换。
之所以使用实时内核可以大大简化应用系统的设计,是因为实时内核允许将应用分成若干个任务,由实时内核来管理它们。
内核本身也增加了应用程序的额外负荷,代码空间增加ROM的用量,内核本身的数据结构增加了RAM的用量。
但更主要的是,每个任务要有自己的栈空间,这一块吃起内存来是相当厉害的。
内核本身对CPU的占用时间一般在2到5个百分点之间。
单片机一般不能运行实时内核,因为单片机的RAM很有限。
通过提供必不可缺少的系统服务,诸如信号量管理,邮箱、消息队列、延时等,实时内核使得CPU的利用更为有效。
一旦读者用实时内核做过系统设计,将决不再想返回到前后台系统。
2.7调度(Scheduler
调度(Scheduler,英文还有一词叫dispatcher,也是调度的意思。
这是内核的主要职责之一,就是要决定该轮到哪个任务运行了。
多数实时内核是基于优先级调度法的。
每个任务根据其重要程度的不同被赋予一定的优先级。
基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行。
然而,究竟何时让高优先级任务掌握CPU的使用权,有两种不同的情况,这要看用的是什么类型的内核,是不可剥夺型的还是可剥夺型内核。
2.8不可剥夺型内核(Non-PreemptiveKernel
不可剥夺型内核要求每个任务自我放弃CPU的所有权。
不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个CPU。
异步事件还是由中断服务来处理。
中断服务可以使一个高优先级的任务由挂起状态变以后控制权还是回到原来被中断了的那个任务,直到该任务主动放弃CPU的使用权时,那个高优先级的任务才能获得CPU的使用权。
不可剥夺型内核的一个优点是响应中断快。
在讨论中断响应时会进一步涉及这个问题。
在任务级,不可剥夺型内核允许使用不可重入函数。
函数的可重入性以后会讨论。
每个任务都可以调用非可重入性函数,而不必担心其它任务可能正在使用该函数,从而造成数据的破坏。
因为每个任务要运行到完成时才释放CPU的控制权。
当然该不可重入型函数本身不得有放弃CPU控制权的企图。
使用不可剥夺型内核时,任务级响应时间比前后台系统快得多。
此时的任务级响应时间取决于最长的任务执行时间。
不可剥夺型内核的另一个优点是,几乎不需要使用信号量保护共享数据。
运行着的任务占有CPU,而不必担心被别的任务抢占。
但这也不是绝对的,在某种情况下,信号量还是用得着的。
处理共享I/O设备时仍需要使用互斥型信号量。
例如,在打印机的使用上,仍需要满足互斥条件。
图2.4示意不可剥夺型内核的运行情况,任务在运行过程之中,[L2.4(1]中断来了,如果此时中断是开着的,CPU由中断向量[F2.4(2]进入中断服务子程序,中断服务子程序做事件处理[F2.4(3],使一个有更高级的任务进入就绪态。
中断服务完成以后,中断返回指令[F2.4(4],使CPU回到原来被中断的任务,接着执行该任务的代码[F2.4(5]直到该任务完成,调用一个内核服务函数以释放CPU控制权,由内核将控制权交给那个优先级更高的、并已进入就绪态的任务[F2.4(6],这个优先级更高的任务才开始处理中断服务程序标识的事件[F2.4(7]。
图2.4不可剥夺型内核
不可剥夺型内核的最大缺陷在于其响应时间。
高优先级的任务已经进入就绪态,但还不能运行,要等,也许要等很长时间,直到当前运行着的任务释放CPU。
与前后系统一样,
不可剥夺型内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU的控制权,完全取决于应用程序什么时候释放CPU。
总之,不可剥夺型内核允许每个任务运行,直到该任务自愿放弃CPU的控制权。
中断可以打入运行着的任务。
中断服务完成以后将CPU控制权还给被中断了的任务。
任务级响应时间要大大好于前后系统,但仍是不可知的,商业软件几乎没有不可剥夺型内核。
2.9可剥夺型内核
当系统响应时间很重要时,要使用可剥夺型内核。
因此,μC/OS-Ⅱ以及绝大多数商业上销售的实时内核都是可剥夺型内核。
最高优先级的任务一旦就绪,总能得到CPU的控制权。
当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。
如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。
如图2.5所示。
图2.5可剥夺型内核
使用可剥夺型内核,最高优先级的任务什么时候可以执行,可以得到CPU的控制权是可知的。
使用可剥夺型内核使得任务级响应时间得以最优化。
使用可剥夺型内核时,应用程序不应直接使用不可重入型函数。
调用不可重入型函数时,要满足互斥条件,这一点可以用互斥型信号量来实现。
如果调用不可重入型函数时,低优先级的任务CPU的使用权被高优先级任务剥夺,不可重入型函数中的数据有可能被破坏。
综上所述,可剥夺型内核总是让就绪态的高优先级的任务先运行,中断服务程序可以抢占CPU,到中断服务完成时,内核让此时优先级最高的任务运行(不一定是那个被中断了的任务。
任务级系统响应时间得到了最优化,且是可知的。
μC/OS-Ⅱ属于可剥夺型内核。
2.11关中断和开中断
处理共享数据时保证互斥,最简便快捷的办法是关中断和开中断。
如示意性代码程序2.3所示:
程序清单2.3关中断和开中断
Disableinterrupts;/*关中断*/
Accesstheresource(read/writefrom/tovariables;/*读/写变量*/
Reenableinterrupts;/*重新允许中断*/
μC/OS-Ⅱ在处理内部变量和数据结构时就是使用的这种手段,即使不是全部,也是绝大部分。
实际上μC/OS-Ⅱ提供两个宏调用,允许用户在应用程序的C代码中关中断然后再开中断:
OS_ENTER_CRITICAL(和OS_EXIT_CRITICAL([参见8.03.02OS_ENTER_CRITICAL(和OS_EXIT_CRITICALL(],这两个宏调用的使用法见程序2.4。
程序清单2.4利用μC/OS_Ⅱ宏调用关中断和开中断voidFunction(void{OS_ENTER_CRITICAL(;.../*在这里处理共享数据*/OS_EXIT_CRITICAL(;}可是,必须十分小心,关中断的时间不能太长。
因为它影响整个系统的中断响应时间,即中断延迟时间。
当改变或复制某几个变量的值时,应想到用这种方法来做。
这也是在中断服务子程序中处理共享变量或共享数据结构的唯一方法。
在任何情况下,关中断的时间都要尽量短。
如果使用某种实时内核,一般地说,关中断的最长时间不超过内核本身的关中断时间,就不会影响系统中断延迟。
当然得知道内核里中断关了多久。
凡好的实时内核,厂商都提供这方面的数据。
总而言之,要想出售实时内核,时间特性最重要。
2.12信号量(Semaphores信号量是60年代中期EdgserDijkstra发明的。
信号量实际上是一种约定机制,在多任务内核中普遍使用.信号量用于:
控制共享资源的使用权(满足互斥条件标志某事件的发生使两个任务的行为同步(译者注:
信号与信号量在英文中都叫做Semaphore,并不加以区分,而说它有两种类型,二进制型(binary和计数器型(counting。
本书中的二进制型信号量实际上是只取两个值0和1的信号量。
实际上这个信号量只有一位,这种信号量翻译为信号更为贴切。
而二进制信号量通常指若干位的组合。
而本书中解释为事件标志的置位与清除(见2.21。
信号像是一把钥匙,任务要运行下去,得先拿到这把钥匙。
如果信号已被别的任务占用,该任务只得被挂起,直到信号被当前使用者释放。
换句话说,申请信号的任务是在说:
“把钥匙给我,如果谁正在用着,我只好等!
”信号是只有两个值的变量,信号量是计数式的。
只取两个值的信号是只有两个值0和1的量,因此也称之为信号量。
计数式信号量的值可以是0到255或0到65535,或0到4294967295,取决于信号量规约机制使用的是8位、16位还是32位。
到底是几位,实际上是取决于用的哪种内核。
根据信号量的值,内核跟踪那些等待信号量的任务。
49
一般地说,对信号量只能实施三种操作:
初始化(INITIALIZE,也可称作建立(CREATE;等信号(WAIT也可称作挂起(PEND;给信号(SIGNAL或发信号(POST。
信号量初始化时要给信号量赋初值,等待信号量的任务表(Waitinglist应清为空。
想要得到信号量的任务执行等待(WAIT操作。
如果该信号量有效(即信号量值大于0,则信号量值减1,任务得以继续运行。
如果信号量的值为0,等待信号量的任务就被列入等待信号量任务表。
多数内核允许用户定义等待超时,如果等待时间超过了某一设定值时,该信号量还是无效,则等待信号量的任务进入就绪态准备运行,并返回出错代码(指出发生了等待超时错误。
任务以发信号操作(SIGNAL释放信号量。
如果没有任务在等待信号量,信号量的值仅仅是简单地加1。
如果有任务在等待该信号量,那么就会有一个任务进入就绪态,信号量的值也就不加1。
于是钥匙给了等待信号量的诸任务中的一个任务。
至于给了那个任务,要看内核是如何调度的。
收到信号量的任务可能是以下两者之一。
等待信号量任务中优先级最高的,或者是最早开始等待信号量的那个任务,即按先进先出的原则(FirstInFirstOut,FIFO有的内核有选择项,允许用户在信号量初始化时选定上述两种方法中的一种。
μC/OS-Ⅱ但只支持优先级法。
如果进入就绪态的任务比当前运行的任务优先级高(假设,是当前任务释放的信号量激活了比自己优先级高的任务。
则内核做任务切换(假设,使用的是可剥夺型内核,高优先级的任务开始运行。
当前任务被挂起。
直到又变成就绪态中优先级最高任务。
程序清单2.7示意在μC/OS-Ⅱ中如何用信号量处理共享数据。
要与同一共享数据打交道的任务调用等待信号量函数OSSemPend(。
处理完共享数据以后再调用释放信号量函数OSSemPost(。
这两个函数将在以后的章节中描述。
要注意的是,在使用信号量之前,一定要对该信号量做初始化。
作为互斥条件,信号量初始化为1。
使用信号量处理共享数据不增加中断延迟时间,如果中断服务程序或当前任务激活了一个高优先级的任务,高优先级的任务立即开始执行。
程序清单2.7通过获得信号量处理共享数据OS_EVENT*SharedDataSem;voidFunction(void{INT8Uerr;OSSemPend(SharedDataSem,0,&err;.../*Youcanaccessshareddatainhere(interruptsarerecognized*//*共享数据的处理在此进行,(中断是开着的*/OSSemPost(SharedDataSem;}50
2.13消息邮箱(MessageMailboxes通过内核服务可以给任务发送消息。
典型的消息邮箱也称作交换消息,是用一个指针型变量,通过内核服务,一个任务或一个中断服务程序可以把一则消息(即一个指针放到邮箱里去。
同样,一个或多个任务可以通过内核服务接收这则消息。
发送消息的任务和接收消息的任务约定,该指针指向的内容就是那则消息。
每个邮箱有相应的正在等待消息的任务列表,要得到消息的任务会因为邮箱是空的而被挂起,且被记录到
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验6 uCOSII在ARM上的移植 实验 uCOSII ARM 移植