《uCOOS-II原理与ARM应用程序设计》课件第3章_第1页
《uCOOS-II原理与ARM应用程序设计》课件第3章_第2页
《uCOOS-II原理与ARM应用程序设计》课件第3章_第3页
《uCOOS-II原理与ARM应用程序设计》课件第3章_第4页
《uCOOS-II原理与ARM应用程序设计》课件第3章_第5页
已阅读5页,还剩164页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第三章将µC/OS-Ⅱ移植到UP-Star实验板上

3.1µC/OS-ⅡLED实例

3.2µC/OS-Ⅱ移植

3.3µC/OS-Ⅱ串口通信实例

3.4本章小结

在第二章工程ex2_3的基础上,通过编写和添加μC/OS-Ⅱ移植文件和内核文件,进一步介绍μC/OS-Ⅱ的两个实例。借助这两个实例,深入说明μC/OS-Ⅱ移植到UP-Star实验板上的方法。通常应该在介绍了μC/OS-Ⅱ内核之后再介绍μC/OS-Ⅱ的移植,这样更方便读者理解移植所需要做的工作。本书先介绍μC/OS-Ⅱ移植再介绍μC/OS-Ⅱ内核,是因为移植μC/OS-Ⅱ所做的工作与S3C2410A芯片硬件密切相关,只要对UP-Star实验板和S3C2410A芯片比较熟悉,即使不了解µC/OS-Ⅱ,也可以学习移植;同时,移植完成后,后续各章节实例均可基于这个移植,在UP-Star实验板上运行。本书是以实例带动讲解的方法编写,所以需选择先介绍µC/OS-Ⅱ的移植。读者也可以先去阅读第四章,再返回来阅读本章。本章介绍的移植参考“µC/OS-ⅡandARMProcessorsApplicationNote(AN-1014)”文档,基于µC/OS-ⅡV2.86,如果读者用于其他µC/OS-Ⅱ的版本,需做一定的修改。

下面首先介绍一个基于UP-Star实验板的µC/OS-Ⅱ实例,即LED闪烁实例;然后再介绍µC/OS-Ⅱ移植需要做的工作。在第2.3节工程ex2_3的基础上新建工程ex3_1,存储目录为D:\ZYUCOSII\ex3_1(此时的工程ex3_1与工程ex2_3完全相同,仅是工程名改为ex3_1),如图3-1所示。

尽管是从工程ex2_3过渡到工程ex3_1,但事实上,读者将会发现,工程ex3_1将做很多重大的修改(以至于和图3-1所示的工程ex3_1最初版本相比完全两样),以达到本节的程序设计目的。3.1μC/OS-ⅡLED实例图3-1工程ex3_1的最初版本3.1.1实例ex3_1

为了清晰地说明实例ex3_1的创建过程,下面逐步介绍。

S1.在图3-1的工作区中,移除“user”分组下的所有文件,删除“asm”分组,添加“ports”分组和“ucosii”分组,如图3-2所示。图3-2工程ex3_1的工作区分组从图3-2可以看出,工程ex3_1仅保留了工程ex2_3的设置(即“Project | Options…”菜单项弹出窗口的内容),但实际的程序设计中会用到工程ex2_3已编写的代码。

S2.到目录D:\ZYUCOSII\ex3_1下,删除User子目录下的所有文件,删除子目录ASM,然后新建子目录ports和ucosii,将μC/OS-ⅡV2.86版软件,即第1.2节提到的压缩文件Micrium-μCos-Ⅱ-V286.ZIP解压后的目录Micrium-uCOS-II-V286\Micrium\SOFTWARE\μCOS-Ⅱ\Source下的14个文件拷贝到ucosii子目录下,其中的文件os_cfg_r.h和os_dbg_r.c移动到ports目录下,并把文件名中的“_r”去掉。此时的目录D:\ZYUCOSII\ex3_1如图3-3所示。从网站下载AN-1014.ZIP文件,在网站的“Support|ApplicationNotes”链接中,是关于“µC/OS-ⅡforARMProcessorsWorksinARMorThumbmode”的。将AN-1014.ZIP压缩文件中的IAR子目录下的os_cpu.h、os_cpu_a.asm和os_cpu_c.c三个文件拷贝到图3-3中的ports文件夹中。

现在可以检查一下图3-3中各个文件夹下的内容,在ports文件夹下有5个文件,依次为os_cfg.h、os_cpu.h、os_cpu_a.asm、os_cpu_c.c和os_dbg.c;在ucosii文件夹下有12个文件,依次为os_core.c、os_flag.c、os_mbox.c、ox_mem.c、os_mutex.c、ox_q.c、os_sem.c、os_task.c、os_time.c、os_tmr.c、ucos_ii.c和ucos_ii.h;user文件夹下没有文件,现在,新建一个空文本文件,更名为app_cfg.h。在程序设计过程中,尽可能地不去修改ucosii文件夹下的文件。图3-3目录D:\ZYUCOSII\ex3_1下的文件夹

S3.回到图3-2,向工作区中添加文件,即向ucosii分组中添加D:\ZYUCOSII\ex3_1\ucosii\ucos_ii.c文件,向ports分组中添加D:\ZYUCOSII\ex3_1\ports目录下的os_cpu_a.s、os_cpu_c.c和os_dbg.c3个文件。新建3个文件,即includes.h、app.h和app.c文件,这3个文件均保存到D:\ZYUCOSII\ex3_1\User目录下,并把app.c文件添加到工作区的user分组下。其中app.h文件内容为空,includes.h文件和app.c文件的内容如下:includes.h文件的内容:

1/*FileName:includes.h

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#include"stdio.h"

8#include"string.h"

9

10#include"..\\ucosii\\ucos_ii.h"

11

12#include"app.h"app.c文件的内容:

1/*FileName:app.c

2**Byzhnyong@21

3**@2009-4-4

4**MainRoutine

5**CopyrightReserved

6*/7

8#include“includes.h”

9

10voidmain(void)

11{

12OSInit();

13

14OSStart();

15}

这里主程序文件即为app.c文件,相当于工程ex2_3中的zyMain.c的作用。至此,D:\ZYUCOSII\ex3_1\User目录下有4个文件,即app.c、app.h、app_cfg.h和includes.h。其中app_cfg.h文件添加如下代码:

S4.将目录D:\ZYUCOSII\ex3_1\下所有文件的属性修改为可读可写,即去掉“只读”属性。

S5.这时编译工程ex3_1,即点击菜单“Project|RebuildAll”,会出现多处“不能打开文件错误”,依次修改以下代码:

(1) ucos_ii.h第44行,由“#include<app_cfg.h>”改为“#include".\\user\\app_cfg.h"”;

(2) ucos_ii.h第45行,由“#include<os_cfg.h>”改为“#include"..\\ports\\os_cfg.h"”;

(3) ucos_ii.h第46行,由“#include<os_cpu.h>”改为“#include"..\\ports\\os_cpu.h"”;

(4) os_cpu_c.c第24行,由“#include<ucos_ii.h>”改为“#include"..\\ucosii\\ucos_ii.h"”;

(5) os_dbg.c第24行,由“#include<ucos_ii.h>”改为“#include"..\\ucosii\\ucos_ii.h"”;

(6) ucos_ii.c第24行,由“#include<ucos_ii.h>”改为“#include"ucos_ii.h"”;

(7) ucos_ii.c第28行,由“#include<os_core.c>”改为“#include"os_core.c"”;

(8) ucos_ii.c第28行,由“#include<os_flag.c>”改为“#include"os_flag.c"”;

(9) ucos_ii.c第28行,由“#include<os_mbox.c>”改为“#include"os_mbox.c"”;

(10) ucos_ii.c第28行,由“#include<os_mem.c>”改为“#include"os_mem.c"”;

(11) ucos_ii.c第28行,由“#include<os_mutex.c>”改为“#include"os_mutex.c"”;

(12) ucos_ii.c第28行,由“#include<os_q.c>”改为“#include"os_q.c"”;

(13) ucos_ii.c第28行,由“#include<os_sem.c>”改为“#include"os_sem.c"”;

(14) ucos_ii.c第28行,由“#include<os_task.c>”改为“#include"os_task.c"”;

(15) ucos_ii.c第28行,由“#include<os_time.c>”改为“#include"os_time.c"”;

(16) ucos_ii.c第28行,由“#include<os_tmr.c>”改为“#include"os_tmr.c"”;

(17) os_tmr.c第25行,由“#include<ucos_ii.h>”改为“#include"ucos_ii.h"”。

S6.新建文件bsp.h和bsp.c,保存在D:\ZYUCOSII\ex3_1\

ports目录下,将bsp.c文件添加到工作区ports分组下,在includes.h文件末尾添加一条语句“#include"..\\ports\\bsp.h"”。其中,bsp.h文件内容为空,bsp.c文件的内容如下:1/*FileName:bsp.c

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#include"..\\user\\includes.h"

8

9voidOS_CPU_ExceptHndlr(INT32Uexcept_type){}

10

11voidApp_TaskSwHook(void){}

1213voidApp_TaskCreateHook(OS_TCB*ptcb){}

14

15voidApp_TCBInitHook(OS_TCB*ptcb){}

16

17voidApp_TaskStatHook(void){}

18

19voidApp_TaskIdleHook(void){}

20

21voidApp_TaskDelHook(OS_TCB*ptcb){}

22

23voidApp_TimeTickHook(void){}现在编译链接工程ex3_1,将正常编译通过,没有任何警告和错误,但实际的移植工作才刚刚开始。

S7.新建文件startup.s,保存在目录D:\ZYUCOSII\ex3_1\

ports下,并将该文件添加到工作区ports分组下,该文件实现S3C2410上电复位后的芯片初始化工作,其代码如下:

S8.进入到os_cpu_c.c文件的OSInitHookBegin函数中,即文件代码的第136行,原始语句为“*pstk=(OS_STK)0;”,估计此处是软件作者J.J.Labrosse先生的失误,此处应改为“*pstk++=(OS_STK)0;”。然后,在第147行和148行间插入一个函数调用语句,即在第147行处回车,添加语句“myInitHookBegin()”,这个函数是作者自定义的系统初始化函数。接着,在app_cfg.h的末尾添加语句“voidmyInitHookBegin(void);//MyInitCodefuncprototype”。最后,在bsp.c文件的末尾添加如下代码:注意:上述代码在bsp.c中的实际行号是从第25行开始的。myInitLED函数用于初始化LED灯,myInitTimer4用于初始化定时器4,定时器4用作μC/OS-Ⅱ时钟节拍,定时中断产生频率为100Hz。

编写bsp.h文件的代码如下:

S9.编写定时器4中断入口和处理函数,修改bsp.c文件中的OS_CPU_ExceptHndlr函数内容如下:

1voidOS_CPU_ExceptHndlr(INT32Uexcept_type)

2{

3switch(except_type)

4{

5case6: //IRQInterrupt

6switch(INTOFFSET) //INTOFFSETissub-intentryNo.

7{18break;

19default:

20break;

21}

22}同时,在bsp.h中添加如下代码:

1//InterruptController

2//---------------------------------------------------------

3//InterruptRequestStatus

4//InterruptModeControl

5//InterruptMaskControl

6//IRQPriorityControl

7//InterruptRequestStatus

8//InterruotRequestSourceOffset

9//SubSourcePending上述代码可位于bsp.h的末尾或“//FunctionPrototype”注释行前面(实际上,这些代码可位于文件中的任意位置)。

S10.新建文件appfun.c,保存在目录D:\ZYUCOSII\

ex3_1\User下,并将该文件添加到工作区user分组下,该文件用于存放用户编写的各种功能函数,目前主要是关于LED灯点亮和熄灭的函数,编写该文件内容如下:编写app.h文件的代码如下:

S11.修改app.c文件的内容,创建一个任务,用于控制LED灯的闪烁,其完整代码如下:

1/*FileName:app.c

2**Byzhnyong@21

3**@2009-4-4

4**MainRoutine

5**CopyrightReserved

6*/

7

8#include“includes.h”

9

10voidmain(void)

11{

12OSInit();

13OSTaskCreate(AppTaskStart,(void*)0,&AppTaskStartStk

[TASK_STK_SIZE-1],0);

14OSStart();

15}目前,app.h的完整代码如下:

1/*FileName:app.h

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#ifdefMY_APP_GLOBALS

8#defineMY_APP文件appfun.c的完整代码如下:

1/*FileName:appfun.c

2**Byzhnyong@21

3**@2009-4-4

4**CopyrightReserved

5*/

6

7#defineMY_APP_GLOBALS

8#include"includes.h"

9

S12.编译链接工程ex3_1,在线仿真可以看到LED1和LED2间隔1秒交替点亮闪烁。最后的工程ex3_1如图3-4所示。图3-4移植完成后的工程ex3_1至此,工程ex3_1完成了,同时也完成了µC/OS-Ⅱ的移植,此时,目录D:\ZYUCOSII\ex3_1下的子目录及其文件和图3-3一样,子目录user下有文件app.c、app.h、app_cfg.h、appfun.c和includes.h,这些文件均需要自己编写;子目录ports下有文件bsp.c、bsp.h、os_cfg.h、os_cpu.h、os_cpu_

a.asm、os_cpu_c.c、os_dbg.c和startup.s,其中,只有bsp.c、bsp.h和startup.s文件需自己编写;子目录ucosii下为12个µC/OS-Ⅱ内核文件,即os_core.c、os_flag.c、os_mbox.c、ox_mem.c、os_mutex.c、ox_q.c、os_sem.c、os_task.c、os_time.c、os_tmr.c、ucos_ii.c和ucos_ii.h,这些文件均不需要自己编写。通过对比工程ex3_1和工程ex2_3可知,两个工程中需要自己编写的代码数量是差不多的,但是工程ex3_1上加载了µC/OS-Ⅱ操作系统。

µC/OS-Ⅱ原作者J.J.Labrosse指出移植µC/OS-Ⅱ是件轻松的事情,主要是因为µC/OS-Ⅱ本身的设计就考虑了移植,即移植的大部分工作原作者都给出来了。

Labrosse认为移植µC/OS-Ⅱ需要花费几个小时至一周的时间不等,视编程人员对ARM芯片结构的熟悉程度。通过上述关于µC/OS-ⅡV2.86的移植,笔者也有同样的观点,如果读者自己研究移植代码,将会发现,移植µC/OS-Ⅱ工作的大部分时间都花在了阅读“µC/OS-ⅡandARMProcessorsApplicationNote(AN-1014)”英文文档上,一定意义上,编程人员的英文水平决定了他的移植水平。从现在开始,我们将开始基于µC/OS-Ⅱ嵌入式操作系统的面向任务程序设计工作,在调试这类程序时,借助µC/OS-Ⅱ的调试插件C-SPY更为直观一些。在图3-4中,点击“Project|Options”菜单,进入如图3-5所示的界面,选中“Debugger|Plugins”下的“µC/OS-Ⅱ”一项,点击“OK”按钮保存。然后回到图3-4中,点击“Project|DownloadandDebug”菜单进入如图3-6所示的调试环境界面,这时菜单栏中有一项为“µC/OS-Ⅱ”,点击该菜单项下的“TaskList”子菜单,将弹出任务一栏,如图3-6所示。图3-5设置µC/OS-Ⅱ调试插件图3-6工程ex3_1显示任务信息的调试窗口从图3-6中可以看出,工程ex3_1中包含4个任务,其中优先级为0的任务为我们自己创建的任务,因为没有命名,所以“Name”一栏为“?”;还有3个系统任务,即µC/OS-ⅡTmr、µC/OS-ⅡStat和µC/OS-ⅡIdle,优先级分别为10、62和63。

下面首先从整体上解释一下实例ex3_1,重点介绍为了移植µC/OS-Ⅱ我们所做的工作,然后,第3.2节将进一步介绍移植µC/OS-Ⅱ所需要做的工作,即摘录“µC/OS-ⅡandARMProcessorsApplicationNote(AN-1014)”文档中的一些内容。3.1.2实例ex3_1注解

第3.1.1节实现了实例ex3_1及其功能,这里介绍这个实例是如何工作的。仿真(或系统上电之后)时,程序首先从startup.s开始执行,实际上是从0x0地址开始执行的(这里也暴露出了IAREWARM软件的一个小缺点,好像快速中断与普通中断标号在反汇编窗口中没有区分,但是这点对程序执行毫无影响)。startup.s文件完成S3C2410A芯片的一些初始化工

作,例如,关闭看门狗的复位功能和中断功能(这里暂时关闭了,以后会开启这个功能,喂狗的时间周期小于10秒。注意看门狗定时器的定时功能依然工作);设置CPU时钟工作频率为192MHz(即FCLK的频率为192MHz,PCLK为48MHz);设置存储器管理器,将外部32MB的SDRAM配置在区块6上;设置各种工作模式的堆栈指针;最后,将程序跳转到C语言的主程序入口main。

startup.s的代码均为汇编语言,不过这个代码相对于《ARM原理与C程序设计》附录四的启动代码来说,是非常简单的。读者还可以去阅读“ARMIARAssemblerReferenceGuide”英文文档来进一步学习EWARM下的汇编指示符表示方法,这个文档位于EWARM安装目录的一个doc子目录下。接着,程序指针进入到app.c文件,开始执行OSInit()函数,通过单步执行将发现,OSInit将首先调用函数OSInitHookBegin,再单步进入到OSInitHookBegin,可见我们自己定义的初始化函数myInitHookBegin就位于其中。即通过执行myInitHookBegin函数就实现了系统的初始化,单步进入myInitHookBegin函数,里面的代码用于初始化系统异常向量表(这是µC/OS-Ⅱ移植要求必须做的),初始化LED显示和定时器4,这里将定时器4设置减计数到0的频率为100Hz,也就是说,如果定时器4开中断后,其产生中断信号的频率为100Hz,就是用作µC/OS-Ⅱ的时钟节拍频率。然后,主程序文件app.c执行完OSInit后,执行OSTaskCreate创建一个任务,这个任务的代码位于appfun.c中,用于LED闪烁;接着,执行OSStart函数,单步进入OSStart函数后,可见该函数的内容如下:第一次执行OSStart时,变量OSRunning为OS_FALSE

(µC/OS-ⅡV2.86中真用OS_TRUE表示,假用OS_FALSE表示),所以会执行第4~8行代码,进行任务调度,并执行优先级最高的任务。在执行完OSStartHighRdy函数后,将开放所有中断,并且工作在管理(SVC)模式下(事实上,工程ex3_1几乎总是工作在SVC模式下,除了极少数语句会工作在常规中断(IRQ)模式下外)。现在,IRQ中断开放了,同时,µC/OS-Ⅱ会发现我们定义的任务AppTaskStart,并进入其代码中运行(位于appfun.c中),函数AppTaskStart将首先打开定时器4中断,然后再进入死循环中(所有任务都是死循环执行的,不会返回)。为了清楚地说明具体的执行路线,下面列出AppTaskStart任务中的死循环代码:

1while(1)

2{

3LEDon(1);

4OSTimeDlyHMSM(0,0,1,0);

5LEDon(2);

6OSTimeDly(100);

7}任务AppTaskStart进入死循环后,首先执行LEDon(1)(此处不妨假设定时器4中断还没有来到),使LED1号灯点亮;然后,执行OSTimeDlyHMSM(0,0,1,0),这是一个µC/OS-Ⅱ延时函数,进入这个函数后,实际上µC/OS-Ⅱ将切换到一个名为“µC/OS-ⅡIdle”的空闲任务;空闲任务每隔0.01秒会收到定时器4中断,进行任务调度;当OSTimeDlyHMSM(0,0,1,0)延时满1秒(实际上任务AppTaskStart的延时计数达到1秒)后,任务会由空闲任务调度到AppTaskStart任务,程序指针回到LEDon(2)语句执行,关闭LED1号灯,点亮LED2号灯;然后,又进入到OSTimeDly(100)语句执行,循环以上过程。函数OSTimeDlyHMSM(0,0,1,0)为延时函数,四个参数的含义为时、分、秒、毫秒,由于定时器4的中断频率为100Hz,所以,最小的延时为10ms(它也有最大延时限制,但一般程序中不会越过这个最大限制)。而函数OSTimeDly用得更多一些,只有一个参量,表示延时时钟节拍数,如果时钟节拍频率为100Hz,则OSTimeDly(100)刚好延时1秒,与函数OSTimeDlyHMSM(0,0,1,0)的功能完全相同。

读者可能会对上述介绍有些迷茫,相信在学习完第四章后,将会完全理解上述过程。毕竟,本章的主要目的在于移植µC/OS-ⅡV2.86,而不是详细介绍其任务调度过程。

J.J.Labrosse先生编写的“µC/OS-ⅡandARMProcessorsApplicationNote,AN-1014Rev.F”移植文档有56页,关于µC/OS-Ⅱ的移植工作介绍得非常细致全面,是移植µC/OS-Ⅱ必读的参考资料。本节除摘录了其中一些主要内容外,还结合第3.1节进行了必要的补充。3.2µC/OS-Ⅱ移植由第3.1节知,工程ex3_1的文件结构如图3-7所示。除了底层硬件UP-Star外,软件部分可以分为四部分,即用户编写的应用程序、µC/OS-Ⅱ内核文件、µC/OS-Ⅱ移植文件和BSP(BoardSupportPackage)板级支持包文件。图3-7显示了各个部分之间的关系:移植文件屏蔽了硬件的具体操作细节,为µC/OS-Ⅱ内核文件服务;µC/OS-Ⅱ内核文件管理着系统的所有资源,为应用程序服务;当应用程序需要直接访问底层硬件时,可借助于BSP文件。图3-7工程ex3_1文件结构工程ex3_1中移植相关的文件保存在D:\ZYUCOSII\ex3_1\

ports中(其中也包含了BSP板级支持包文件),这个目录下有8个文件,如图3-8所示。习惯上也可把BSP文件bsp.h和bsp.c归类为移植文件。图3-8中,bsp.h文件主要是宏定义一些S3C2410A芯片外设寄存器地址和自定义函数声明;startup.s文件为启动代码文件,用于初始化CPU时钟和存储器配置等,并跳转到C语言入口地址;os_dbg.c文件主要为µC/OS-Ⅱ及其配置提供在线调试信息;os_cfg.h文件为µC/OS-Ⅱ定义配置常量。其中,bsp.h和startup.s文件内容的说明可参阅第3.1节。os_cfg.h文件内容将在第五章的5.7节介绍。下面重点介绍其余四个文件的内容,即os_cpu.h、os_cpu_c.c、os_cpu_a.asm和bsp.c文件。图3-8工程ex3_1中的移植文件3.2.1os_cpu.h文件

os_cpu.h文件有241行代码,限于篇幅,这里不给出完整的代码了,第3.1节介绍了获得该文件代码的方法。移植µC/OS-Ⅱ时,需要考虑不同的编译器可能对数据类型的定义不尽相同。例如,在EWARM中,short为16位有符号整型,而int为32位有符号整型。为了适应不同的编译系统,移植人员需要查看编译器关于基本数据类型的定义,在EWARM中,可以作如下的自定义数据类型:这样,如果定义无符号16位整型数,使用类型INT16U即可。注意上述代码的第11行和12行,第11行定义了类型OS_STK,专门用于定义堆栈;第12行定义了类型OS_CPU_SR,专门用于定义CPSR寄存器变量,用于临时保存CPSR的值。这两行自定义类型的主要目的在于增强程序的可读性。程序中那些执行过程中不能被中断的代码,称为Critical代码段,常被译为临界(段)代码,本书采用这一译法。因为这类代码往往用于设置硬件寄存器或初始化外设,实际上是表示硬件状态切换的代码,有一种状态“边界”临时转换的含义,故译为“临界段”比较合适。临界段代码中中断是关闭的,所以临界段代码一般比较短小,进入临界段前需要关闭中断;离开临界段后要恢复CPSR寄存器的状态。µC/OS-Ⅱ在os_cpu.h中宏定义了两个函数,分别用于关闭中断和恢复CPSR寄存器状态,其代码如下:

µC/OS-Ⅱ有三种中断管理的方法,一般使用方法3,即上述第1行代码中宏定义OS_CRITICAL_METHOD为3;第15~16行代码为宏函数,即调用OS_CPU_SR_Save()函数保存当前CPSR寄存器的值,并关闭中断;调用OS_CPU_SR_

Restore(cpu_sr)函数恢复CPSR的值,注意这里是“恢复”,如果原来的中断是关闭的,则调用OS_CPU_SR_Restore后,中断仍然是关闭的,即不能用OS_EXIT_CRITICAL来开中断!如果OS_CPU_INT_DIS_MEAS_EN大于0,还将调用第9行和第11行的两个函数,对中断关闭期间进行计数,用于统计中断关闭的时长。第8行和第10行末尾的“\”是续行符,表示其下的一行代码和当前行代码是同一行代码。

在os_cpu_a.asm中可以找到如下代码:上述代码的解释为:第1行定义标号OS_CPU_SR_Save,即函数名;第2行将CPSR的值保存到R0寄存器,R0的值为函数的返回值;第3行,R0的值与OS_CPU_ARM_CONTROL

_INT_DIS常量(值为0xC0)取或,送入R1寄存器,此时R1的第7位和第6位为1;第4行将R1的值写入CPSR的控制域,即CPSR的第7位和第6位写为1;第5行跳转。同理,第8行定义标号OS_CPU_SR_Restore,即函数名;第9行将R0的值写入CPSR寄存器的控制位域,此处R0为全局参数,即函数OS_CPU_SR_Restore的参数;第10行跳转。

S3C2410A芯片封装ARM920T核,其堆栈增长方向为由内存高地址向低地址,故在os_cpu.h中定义:

#defineOS_STK_GROWTH1/*StackgrowsfromHIGHtoLOWmemoryonARM*/

任务级的切换调用宏函数OS_TASK_SW(),这个宏函数位于os_cpu.h中:

#defineOS_TASK_SW()OSCtxSw()

用户需要编写的异常处理函数也位于os_cpu.h中,即

voidOS_CPU_ExceptHndlr(INT32Uexcept_type);

os_cpu.h中的其他代码请读者自己阅读,这里不再说明。这个文件在移植时不需要做任何修改!3.2.2os_cpu_c.c文件

os_cpu_c.c文件第24行改为“#include"..\\ucosii\\ucos_ii.h"”,即添加包括文件路径。该文件主要用于实现一些“钩子”函数,即OSInitHookBegin()、OSInitHookEnd()、OSTaskCreateHook()、OSTaskDelHook()、OSTaskIdleHook()、OSTaskStatHook()、OSTaskStkInit()、OSTaskSwHook()、OSTCBInitHook()和OSTimeTickHook()函数。之所以称之为钩子函数,是由于这个函数放置在相应的函数中调用,用于扩充调用它们的函数的功能,就像一个钩子一样挂在那里,这些函数大部分可以为空函数。例如,OSInitHookBegin函数位于OSInit函数开头,即进入OSInit函数后,首先执行OSInitHookBegin函数,而我们自定义的系统初始化函数就位于OSInitHookBegin函数中,如下:上述代码的第24行为我们自定义的系统初始化函数。其中第12行代码原文中没有“++”,是笔者添上去的,估计是J.J.Labrosse先生的失误,尽管这里“++”意义不是很大。第15行至19行代码为定义异常堆栈首地址。在µC/OS-ⅡV2.86中要求所有异常模式下的堆栈共用一个,其大小为OS_CPU_

EXCEPT_STK_SIZE,定义在文件os_cpu.h或app_cfg.h中。全部的10个钩子函数中,只有OSTaskStkInit函数是必须编写的。每个任务都是一个死循环,任务创建后就保存在内存中了,任务被“调用”的含义严格意义上讲是返回到某个任务去执行!这个概念很重要,这是µC/OS-Ⅱ嵌入式操作系统下函数调用与普通芯片级程序下函数调用的不同之处。因此任务被“调用”执行时,即程序指针从某个任务返回到该任务时,要做一系列的出栈操作,即恢复该任务的执行环境。但事实上,任务被创建时并没有执行任务,当然也不会有入栈操作,所以,必须在OSTaskStkInit函数中进行模拟的入栈操作,即让任务创建时看起来有一个入栈的过程。在任务创建时首先会调用OSTaskStkInit函数,目的就是完成任务的入栈操作,并且这个入栈操作与OSStartHighRdy(位于os_cpu_a.asm中)出栈操作必须对应!这里OSTaskStkInit函数的代码如下所示:再强调一下,每个任务都是一个死循环函数,一个任务被“调用”执行时,实际上是返回到那个任务去执行,而不是调用那个任务!返回一个任务去执行,应首先恢复这个任务的执行环境,即执行一个出栈操作。任务在创建时必须调用OSTaskStkInit函数“制造”一个入栈操作,即与返回到任务时的出栈操作完全相反的操作。而这个入栈操作意义不大,只有任务的入口地址和参数有效,即上述代码的第10行和第24行,其余行的入栈操作只是对应出栈过程而已,入栈的数值可以随意选取。第26或27行很重要,根据ARM工作模式或Thumb工作模式设定入栈的CPSR的值(当出栈时这个值用于恢复CPSR的值,从而开放中断)。

os_cpu_c.c文件在移植时,除了前述在OSInitHookBegin中添加自定义的初始化函数myInitHookBegin外,其余内容不需要修改。用户添加钩子功能时,不需要直接在os_cpu_c.c的各个钩子函数中添加代码,而只需要在各钩子函数中调用的“应用钩子函数”中添加代码即可。例如:程序设计人员不需要直接在OSTaskCreateHook中添加代码,而只需要在App_TaskCreateHook(上述代码的第21行)函数中添加代码,而这个App_TaskCreateHook函数一般放在bsp.c或其他用户编写的程序文件中。这样做的好处在于保持µC/OS-Ⅱ内核代码和移植代码的完整性,不同的用户使用时,可清楚地划分哪些是自己添加的代码,哪些是µC/OS-Ⅱ的代码,当程序出现调试或运行错误时,可以集中精力从自己添加的代码中寻找错误。各个钩子函数中调用的“应用钩子函数”为:OSTaskCreateHook调用App_TaskCreateHook,在µC/OS-Ⅱ调用OSTaskCreate或OSTaskCreateExt创建一个任务时调用该函数,用户可以在此添加特殊功能的移植代码;OSTaskDelHook调用App_TaskDelHook,在删除一个任务(使任务处于休眠态)时调用该函数;OSTaskIdleHook调用App_TaskIdleHook,µC/OS-Ⅱ进入空闲状态时调用该函数,一般地,可在该函数中添加使CPU工作于低功耗状态的代码;

OSTaskStatHook调用App_TaskStatHook,统计任务中调用该函数;OSTaskSwHook调用App_TaskSwHook,任务级切换时调用该函数;OSTCBInitHook调用App_TCBInitHook,在OS_TCBInit函数创建了TCB后,调用该函数;OSTimeTickHook调用App_TimeTickHook,在每个时钟节拍都会调用该函数。每个钩子函数的调用位置很重要,后续章节中还会介绍。此外,读者可去查看µC/OS-Ⅱ源代码,以便了解到钩子函数更准确详细的调用位置。3.2.3os_cpu_a.asm文件

os_cpu_a.asm文件有843行代码,在学习《ARM原理与C程序设计》一书后,几乎可以读懂全部代码。我们认为,读懂这个文件的全部代码是有必要的,这些代码中包含了对异常处理的一个精湛的方法,可以让读者收获很大;而且,这些代码写得相当出色和专业,是学习汇编语言的典范例子。

这里只讲述OSStartHighRdy函数,其余内容读者在学习完第四章后,再去自行研究。

为了介绍的方便,把OSStartHighRdy函数的内容罗列如下:上述代码中,第1行为标号,也用作C函数名;第3行设置CPSR的值,进入SVC模式,关闭普通中断和快速中断;第5~7行,调用函数OSTaskSwHook,调用的方法为首先将保存了OSTaskSwHook跳转地址的地址存入R0寄存器,将PC的当前值保存到LR中,跳转到R0地址处,此时的R0处为一个跳转地址,于是进入到OSTaskSwHook执行;第9~11行依次为_OS_Running的值保存到R0中,R1的值赋为1,将R1的值即1写入到_OS_Running地址处,而_OS_Runing地址处的值定义为OSRunning,所以,这几句执行的结果为OSRunning赋为1(即真);在os_cpu_a.asm的第822行,有语句“_OS_TCBHighRdy:DC32OSTCBHighRdy”,第14~16行依次为将_OS_

TCBHighRdy的值写入R0中,再将_OS_TCBHighRdy地址处的值写入R0中,最后,以_OS_TCBHighRdy地址处的值为地址,将该地址里存储的值写入SP中,即堆栈指针指向新的TCB堆栈(参考第四章);第18行将CPSR出栈,出栈的值赋给SPSR,第18行与第21行联合完成了对第3.2.2节OSTaskStkInit中入栈后堆栈的出栈操作(假设属于同一个任务的调用)。上述内容在学习了第四章后再去理解将更加容易一些。此外,os_cpu_a.asm中的OSCtxSw和OSIntCtxSw也是极为重要的代码段,需要认真学习。3.2.4bsp.c文件

bsp.c文件属于BSP(板级支持包)文件,广义上,也可以视为移植文件,因为µC/OS-Ⅱ内核文件的正常运作也需要这个文件中的部分函数。从第3.1.1节中可以找到这个文件完整的代码,这里仅需要介绍第3.1.1节第S9步中提到的一些代码,为方便起见,将这些代码罗列如下:14

15break;

16default:

17break;

18}

19break;

20default:

21break;

22}

23}上述函数OS_CPU_ExceptHndlr是用户需要编写的中断处理函数。第3行判断参数except_type的值,如果为6,表示为IRQ普通中断。这时在第6行进一步判断INTOFFSET的值,如果为14,表示为定时器4的中断,此时在第9行调用OSTimeTick函数进入系统时钟节拍处理,这个函数是非常重要的,如果节拍太慢,系统的实时性会变差;如果节拍太快,系统的负担加重,一般设为10~100Hz。第11~13行与定时器4的中断处理相关,参见《ARM原理与C程序设计》第170页或“S3C2410AUser’sManual”第14章。当定时器4中断发生后,os_cup_a.asm中的异常处理函数将会触发OS_CPU_ExceptHndlr函数,进一步调用OSTimeTick函数,进入系统节拍处理。

至此,读者应回到第3.1节反复运行工程ex3_1,多设置一些断点,或把时钟节拍加长到10秒以上单步运行(笔者在初次学习µC/OS-Ⅱ移植时,曾把时钟节拍延长到19.08845秒),以体会移植代码是如何工作的。本节在第3.1节工程ex3_1的基础上,添加串口通信功能,生成工程ex3_2。在工程ex3_2中的AppTaskStart任务中,再新建3个任务,即AppTaskOne、AppTaskTwo和AppTaskThree。其中,AppTaskOne每隔一秒点亮LED1灯一次;AppTaskTwo每隔2秒点亮LED2灯一次;AppTaskThree每隔4秒点亮LED3灯一次。当灯点亮时,串口调试助手同步显示被点亮的LED灯号。

3.3µC/OS-Ⅱ串口通信实例3.3.1实例ex3_2

按第3.1节的方法新建工程ex3_2,保存目录为D:\ZYUCOSII\ex3_2,工程ex3_2的工作区如图3-9所示(这时的工程ex3_2与工程ex3_1功能完全相同,只是工程名和存储目录不同而已)。图3-9工程ex3_2的最初版本为了清楚地介绍工程ex3_2的创建过程,下面分步进行:

S1.在app.c文件中创建任务AppTaskStart时,将其优先级指定为5,即OSTaskCreate函数的最后一个参数为5,完整的app.c文件内容如下:

1/*FileName:app.c

2**Byzhnyong@21

3**@2009-4-4

4**MainRoutine

5**CopyrightReserved

6*/

7

本书后面章节的几乎所有实例的主程序文件与上述代码完全相同,即有意义的代码只有第8行和第10~15行等7行,程序的功能主要在appfun.c文件中实现。

S2.在bsp.h文件的末尾,添加一行串口0初始化函数声明语句,即

voidmyInitUART0(void);//InitializeUART0

在bsp.h文件中添加对串口0寄存器地址的宏定义语句,如下:

1//---------------------------------------------------------

2//UART0

3//---------------------------------------------------------上述代码可以放置在bsp.h的任何位置。

在bsp.c文件的末尾,添加上述函数myInitUART0的函数体,即

1//InitializeUART0

2voidmyInitUART0(void)

3{

4//SetGPH3:2asRXD0:TXD0

5GPHCON=0xA0;

6

7//SetUART0Baudrate:4800bps,8-bit,1-bitstop

8UFCON0=0x0;

9UMCON0=0x0;

10ULCON0=0x03;

11UCON0=0x05;

12UBRDIV0=0x270;

13}在bsp.c文件的myInitHookBegin函数内添加一条语句,即“my

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论