ucos2操作系统在51单片机上的移植.docx
- 文档编号:3162089
- 上传时间:2023-05-05
- 格式:DOCX
- 页数:33
- 大小:87.17KB
ucos2操作系统在51单片机上的移植.docx
《ucos2操作系统在51单片机上的移植.docx》由会员分享,可在线阅读,更多相关《ucos2操作系统在51单片机上的移植.docx(33页珍藏版)》请在冰点文库上搜索。
ucos2操作系统在51单片机上的移植
μC/OS-Ⅱ在51单片机上的移植
姓名:
学号:
目录
1绪论2
1.1嵌入式实时操作系统2
1.2μC/OS-Ⅱ嵌入式操作系统2
1.3μC/OS-Ⅱ原理2
2μC/OS-Ⅱ内核结构3
2.1临界区3
2.2任务及任务控制块3
2.3任务状态4
2.4任务调度5
2.5中断处理5
2.6时钟节拍5
2.7μC/OS-Ⅱ初始化与启动6
3μC/OS-Ⅱ在51单片机上的移植6
3.1OS_CPU.H文件的移植6
3.2OS_CPU_A.ASM文件的移植8
3.3OS_CPU_C.C文件的移植20
4移植结果测试23
4.1设计原理23
4.2应用程序设计23
4.3结果分析25
1绪论
1.1嵌入式实时操作系统
大多数的操作系统只注重平均性能,如对于整个系统来说,所有任务的平均响应时间是关键,而不关心单个任务的响应时间。
而嵌入式实时操作系统最主要的特征是性能上的实时性,从这个角度上看,可以把嵌入式实时操作系统定义为“当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的嵌入式操作系统”。
目前大多数嵌入式操作系统提供以下管理功能:
1.任务管理
所有嵌入式操作系统都是多任务的,目前所说的多任务大都是指多线程方式或多进程方式,两者的运行机制不完全一样。
以多进程为例,调度程序的好坏直接影响到系统的性能。
和一般的操作系统一样,嵌入式操作系统的作用也是决定在特定的某一时刻系统应该运行哪一个进程,对嵌入式系统中的运行软件进行描述和管理,并完成处理机资源的分配与调度。
2.存储管理
在嵌入式系统中,一般不采用虚拟内存管理,而采用动态内存管理方式,即当程序的某一部分需要使用内存时,利用操作系统提供的分配函数来处理,一旦使用完,可通过释放函数来释放所占用的内存,这样内存就可以重复使用,这样提高了内存的利用率,方便了用户的使用,并提供了足够的存储空间。
3.周边资源管理
在操作系统中必须提供周边资源的驱动程序,以方便资源管理和应用程序使用。
4.中断管理
嵌入式操作系统和一般操作系统一样,一般都是用中断方式来处理外部事件和I/O请求。
中断管理负责中断的初始化安装、现场的保存和恢复、中断栈的嵌套管理等。
1.2μC/OS-Ⅱ嵌入式操作系统
μC/OS-Ⅱ是一个可裁剪、源码开放、结构小巧、抢先式的实时多任务内核,主要面向中小型嵌入式系统,具有执行效率高、占用空间小、可移植性强、实时性能优良和可扩张性强等特点。
μC/OS-Ⅱ结构小巧,即使包含全部功能如信号量、消息邮箱、消息队列以及相关函数等,编译后的μC/OS-Ⅱ内核也仅有6~10KB,所以它比较适用于小型控制系统,μC/OS-Ⅱ也具有良好的扩展性能。
1.3μC/OS-II原理
μC/OS-II包括任务调度、时间管理、内存管理、资源管理四大部分。
它的移植只与4个文件相关:
汇编文件(OS_CPU_A.ASM)、处理器相关C文件(OS_CPU.H、OS_CPU_C.C)和配置文件(OS_CFG.H)。
有64个优先级,系统占用8个,用户可创建56个任务,不支持时间片轮转。
它的基本思路就是“近似地每时每刻总是让优先级最高的就绪任务处于运行状态”。
为了保证这一点,它在调用系统API函数、中断结束、定时中断结束时总是执行调度算法。
任务的切换是通过模拟一次中断实现的。
μC/OS-II工作核心原理是:
近似地让最高优先级的就绪任务处于运行状态。
2μC/OS-Ⅱ内核结构
2.1临界区
一个任务在某些时候可能会访问共享内存、共享文件或其他共享资源,这些对共享内存进行访问的程序片断称作临界区。
为了防止不同的任务同时处于临界区,必须使用一定互斥的方法来避免这种情况的发生,因此μC/OS-Ⅱ在处理临界区代码时需要关中断,处理完毕后再开中断。
μC/OS-Ⅱ定义两个宏来开关中断,μC/OS-Ⅱ中的这两个宏调用分别是:
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
2.2任务及任务控制块
在μC/OS-Ⅱ中,一个任务看起来像其它C的函数一样,有函数返回类型,有形式参数变量,但是任务是不会返回的,所以返回参数要定义成void类型,下面这个程序就是一个任务函数:
voidTask(void*pdata)
{
for(;;)
{
/*用户代码*/
/*调用μC/OS-Ⅱ的各种服务*/
/*用户代码*/
}
}
当任务完成以后,任务函数可以调用OSTaskDel()来实现自我删除。
任务想要再次进入内核可以调用OSTaskCreat()或者OSTaskCreatExt()。
任务函数的形式参数变量是由用户代码在第一次执行时带入的,将变量定义成void指针是为了允许用户应用程序传递任何类型的数据给任务。
用户也可以建立许多相同的任务,且所有都使用同一个任务函数,但可以向这个任务传入不同的数据,就可以达到不同的任务使用同一个任务函数的目的,大大节省了代码的存储空间.
μC/OS-Ⅱ可以管理多达64个任务,但作者保留了优先级为0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRI0-2,OS_LOWEST_PRI0-1以及OS_LOWEST_PRI-0这8个任务以被将来使用。
必须给每个任务赋以不同的优先级,优先级号越低,任务的优先级越高。
μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。
任务控制块(OS_TCB)是一个数据结构,是用来描述任务的一些属性。
当一个任务建立时,任务控制块将被初始化。
当任务的CPU使用权被剥夺时,μC/OS-Ⅱ用任务控制块来保存改任务的状态,当任务重新得到CPU使用权时,任务控制块能确保任务从中断的那一点继续执行下去。
2.3任务状态
每个任务都处在以下5种状态里:
休眠态、就绪态、运行态、挂起态和被中断态。
在任何时刻,任务的状态一定是这5种状态之一。
休眠态指任务在程序空间之中,还没有交给μC/OS-Ⅱ管理,可以通过调用OSTaskCreate()或OSTaskCreateExt()函数来把任务交给μC/OS-Ⅱ。
当任务一旦建立,这个任务就进入就绪态准备运行。
一个任务可以通过调用OSTaskDel()函数再返回到休眠态,或通过调用该函数让另一个任务进入休眠态。
调用OSStart()可以启动多任务。
OSStart()函数运行进入就绪态的优先级最高的任务。
只有当所有优先级高于它的任务转为等待状态,或者是被删除了,就绪的任务才能进入运行态。
正在运行的任务可以通过调用OSTimeDly()或OSTimeDlyHMSM()这两个函数将自身延迟一段时间,此时这个任务就进入了等待状态,等待这段时间过去,下一个优先级最高的、并进入了就绪态的任务立刻被赋予了CPU的控制权。
等待的时间过去以后,系统服务函数OSTimeTick()使延迟了的任务进入就绪态。
正在运行的任务期待某一事件的发生也要等待,手段是调用以下3个函数之一:
OSSemPend()、OSMboxPend()或OSQPend()。
调用后任务进入了等待状态。
当任务因等待事件被挂起,下一个优先级最高的任务立即得到了CPU的控制权。
当事件发生了,被挂起的任务进入就绪态。
正在运行的任务是可以被中断的,除非该任务或者μC/OS-Ⅱ关中断。
被中断了的任务就进入了中断服务子程序。
响应中断时,正在执行的任务被挂起,中断服务子程序控制了CPU的使用权。
中断服务子程序可能会报告一个或多个事件的发生,而使一个或多个任务进入就绪态。
在这种情况下,从中断服务子程序返回之前,μC/OS-Ⅱ要判定,被中断的任务是否还是就绪态任务中优先级最高的任务。
如果中断服务子程序使一个优先级更高的任务进入了就绪态,则新进入就绪态的这个优先级更高的任务将得以运行,否则原来被中断了的任务还会继续运行。
当所有的任务都处在等待事件发生或等待延迟时间结束的状态时,μC/OS-Ⅱ执行空闲任务。
2.4任务调度
μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那个任务。
每个任务的就绪态标志都放入就绪表中,就绪表中有两个变量OSRdyGrp和OSRdyTbl[]。
通过这两个变量和优先级判定表OSUnMapTbl[256]来确定哪个任务运行。
所谓任务切换,其实要做的就是CPU寄存器内容切换。
当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态,即CPU寄存器中的全部内容。
这些内容保存在任务的当前状况保存区,也就是任务自己的栈区之中。
入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。
任务切换由以下两步完成:
(1)将被挂起任务的微处理器寄存器推入堆栈;
(2)将较高优先级的任务的寄存器值从栈中恢复到寄存器中。
2.5中断处理
μC/OS-II中的中断服务程序最好用汇编语言来写,以减少时间上的开销。
中断服务程序的示意代码如下。
用户中断服务子程序:
保存全部CPU寄存器;
(1)
调用OSIntEnter或OSIntNesting直接加1;
(2)
执行用户代码做中断服务;(3)
调用OSIntExit();(4)
恢复所有CPU寄存器;(5)
执行中断返回指令;(6)
用户代码应该将全部CPU寄存器推入当前任务栈
(1)。
μC/OS-Ⅱ需要了解用户在做中断服务,故用户应该调用OSIntEnter(),或者将全程变量OSIntNesting
(2)直接加1。
直接给OSIntNesting加1比调用OSIntEnter()快得多,可能时,直接加1更好。
上述两步完成以后,用户可以开始服务于叫中断的设备了(3)。
调用脱离中断函数OSIntExit()(4)标志着中断服务子程序的结束,OSIntExit()将中断嵌套层数计数器减1。
当嵌套计数器减到零时,所有中断,包括嵌套的中断就都完成了,此时μC/OS-Ⅱ要判定有没有优先级较高的任务被中断服务子程序唤醒了。
如果有优先级高的任务进入了就绪态,μC/OS-Ⅱ就返回到那个高优先级的任务,OSIntExit()返回到调用点(5)。
保存的寄存器的值是在这时恢复的,然后是执行中断返回指令(6)。
2.6时钟节拍
μC/OS-Ⅱ需要用户提供周期性的信号源,用于实现时间延时和确认超时。
节拍率应在每秒10次到100次之间,也就是每l0ms到l00ms响应一次。
51单片机有两个16位的定时器,可以作为时钟节拍的定时源。
用户必须在多任务系统启动之后再启动时钟节拍源开始计时,也就是在调用OSStart()之后。
2.7μC/OS-Ⅱ初始化与启动
μC/OS-Ⅱ的初始化是通过调用系统初始化函数OSInit()实现的。
OSInit()初始化内核所需要的所有变量和数据结构。
OSInit()还建立了空闲任务,使这个任务始终处于就绪态。
空闲任务OSTaskIdle()的优先级总是被设置成所有任务中最低的。
OSInit()初始化变量主要是一些系统的全局变量,还初始化了4个空的数据结构空间。
这4个结构都是单向链表,允许μC/OS-Ⅱ从缓冲区中迅速得到或释放一个其中的元素。
在空缓冲区中空任务控制块的数目取决于最多任务OS_MAX_TASKS,最多任务数在OS_CFG.H文件中定义的。
然后OSInit()将会建立两个任务,并将这两个任务的任务控制块OS_TCB用双向链表链接在一起。
OSTCBList指向这个链表的起始处。
当建立一个任务时,这个任务总是被放在这个链表的起始处。
换句话说,OSTCBList总是指向最后建立的那个任务。
因为两个任务都处于就绪态,在就绪任务表OSRdyTbl[]中的相应位也要设为1。
μC/OS-Ⅱ多任务的启动是用户通过调用OSStart()实现的,在启动之前,用户至少要建立一个应用任务。
OSStart()是从任务就绪表中构造出优先级最高的任务,然后调用高优先级就绪任务启动函数OSStartHighRdy()。
3μC/OS-Ⅱ在51单片机上的移植
所谓移植,就是使一个实时内核能在某个微处理器或微控制器上运行。
要使μC/OS-Ⅱ正常运行,处理器必须满足以下要求:
1.处理器的C编译器能产生可重入代码。
2.用C语言就可以打开和关闭中断。
3.处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。
4.处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。
5.处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。
移植工作主要包括以下几个内容:
●用#define设置一个常量的值(OS_CPU.H)
●声明10个数据类型(OS_CPU.H)
●用#define声明三个宏(OS_CPU.H)
●用C语言编写六个简单的函数(OS_CPU_C.C)
●编写四个汇编语言函数(OS_CPU_A.ASM)
下面分别从这几个方面谈一下。
3.1OS_CPU.H文件的移植
OS_CPU.H包括了用#defines定义的与处理器相关的常量、宏和类型定义。
OS_CPU.H的大体结构如下所示:
#ifdefOS_CPU_GLOBALS
#defineOS_CPU_EXT
#else
#defineOS_CPU_EXTextern
#endif
typedefunsignedcharBOOLEAN;
(1)
typedefunsignedcharINT8U;/*无符号8位整数*/
typedefsignedcharINT8S;/*有符号8位整数*/
typedefunsignedintINT16U;/*无符号16位整数*/
typedefsignedintINT16S;/*有符号16位整数*/
typedefunsignedlongINT32U;/*无符号32位整数*/
typedefsignedlongINT32S;/*有符号32位整数*/
typedeffloatFP32;/*单精度浮点数*/
(2)
typedefdoubleFP64;/*双精度浮点数*/
typedefunsignedcharOS_STK;/*堆栈入口宽度为8位*/(3)
#defineOS_ENTER_CRITICAL()EA=0/*禁止中断*/(4)
#defineOS_EXIT_CRITICAL()EA=1/*允许中断*/
#defineOS_STK_GROWTH0/*定义堆栈的增长方向*/(5)
#defineOS_TASK_SW()OSCtxSw()(6)
3.1.1与编译器相关的数据类型
因为不同的微处理器有不同的字长,所以μC/OS-Ⅱ的移植包括了一系列的类型定义以确保其可移植性。
μC/OS-Ⅱ代码从不使用C的short,int和long等数据类型,因为它们是与编译器相关的,不可移植,因此将其定义成整型数据结构
(1)。
μC/OS-Ⅱ还定义了浮点数据类型
(2)。
用户必须将任务堆栈的数据类型告知μC/OS-Ⅱ,这个过程是通过为OS_STK声明正确的C数据类型来完成的。
51单片机的堆栈成员是8位的,并且用户的编译文件指定字符型为8位数,所以应将OS_STK声明为无符号字符型数据类型(3)。
所有的任务堆栈都必须用OS_STK来声明数据类型。
3.1.2OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
与所有的实时内核一样,μC/OS-Ⅱ需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断,这就使得μC/OS-Ⅱ能够保护临界段代码免受多任务或中断服务例程的破坏。
μC/OS-Ⅱ定义了两个宏来禁止和允许中断:
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()(4)。
OS_ENTER_CRITICAL()用来关中断,OS_EXIT_CRITICAL()用来开中断。
执行这两个宏的最简单的方法是在OS_ENTER_CRITICAL()中调用处理器指令来禁止中断,以及在OS_EXIT_CRITICAL()中调用允许中断指令。
本移植过程就是通过这种方法来禁止允许中断的,在51单片机中是通过中断允许寄存器来控制中断的,当EA=0时为关中断,当EA=1时为开中断。
3.1.3OS_STK_GROWTH
只要在结构常量OS_STK_GROWTH中指定堆栈的生长方式就可以了。
●置OS_STK_GROWTH为0表示堆栈从低地址向高地址增长。
●置OS_STK_GROWTH为1表示堆栈从高地址向低地址增长。
由于51单片机的堆栈是从低地址向高地址增长的,因此将OS_STK_GROWTH设置成0(5)。
3.1.4OS_TASK_SW()
OS_TASK_SW()(6)是一个宏,它是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的。
OS_TASK_SW()总是在任务级代码中被调用的。
另一个函数OSIntExit()被用来在ISR使得更高优先级任务处于就绪状态时,执行任务切换功能。
任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。
μC/OS-Ⅱ要运行处于就绪状态的任务必须要做的事就是将所有处理器寄存器从任务堆栈中恢复出来,并且执行中断返回。
为了切换任务可以通过执行OS_TASK_SW()来产生中断。
大部分的处理器会提供软中断或是陷阱指令来完成这个功能。
ISR或是陷阱处理函数的向量地址必须指向汇编语言函数OSCtxSw()。
然而51单片机并不提供软中断机制,因此需要尽自己的所能将堆栈结构设置成与中断堆栈结构一样。
OS_TASK_SW()只会简单的调用OSCtxSw()而不是将某个向量指向OSCtxSw()。
而?
C_XBP指针能做到这点。
?
C_XBP是外部可重入栈的堆栈指针。
51由于内部RAM容量的限制,可重入栈没有使用硬件堆栈(堆栈指针SP),而是在外部RAM中模拟的可重入栈,其堆栈指针即为?
C_XBP,不过它是用软件模拟的。
3.2OS_CPU_A.ASM文件的移植
OS_CPU_A.ASM的移植需要编写四个简单的汇编语言函数:
●OSStartHighRdy()
●OSCtxSw()
●OSIntCtxSw()
●OSTickISR()
3.2.1准备工作
要对所要移植的函数定义重定位段,声明引用的变量,分配堆栈空间,定义必要的宏等,如下面的程序所示:
;定义重定位段
?
PR?
OSStartHighRdy?
OS_CPU_ASEGMENTCODE
(1)
?
PR?
OSCtxSw?
OS_CPU_ASEGMENTCODE
?
PR?
OSIntCtxSw?
OS_CPU_ASEGMENTCODE
?
PR?
OSTickISR?
OS_CPU_ASEGMENTCODE
;声明引用全局变量和外部子程序
EXTRNIDATA(?
C_XBP);仿真堆栈指针用于重入局部变量保存
(2)
EXTRNIDATA(OSTCBCur)
EXTRNIDATA(OSTCBHighRdy)
EXTRNIDATA(OSRunning)
EXTRNIDATA(OSPrioCur)
EXTRNIDATA(OSPrioHighRdy)
EXTRNCODE(_?
OSTaskSwHook)
EXTRNCODE(_?
OSIntEnter)
EXTRNCODE(_?
OSIntExit)
EXTRNCODE(_?
OSTimeTick)
;对外声明4个不可重入函数
PUBLICOSStartHighRdy(3)
PUBLICOSCtxSw
PUBLICOSIntCtxSw
PUBLICOSTickISR
;分配堆栈空间。
?
STACKSEGMENTIDATA(4)
RSEG?
STACK
OSStack:
DS40H
OSStkStartIDATAOSStack-1
;定义压栈宏
PUSHALLMACRO(5)
PUSHPSW
PUSHACC
PUSHB
PUSHDPL
PUSHDPH
MOVA,R0;R0-R7入栈
PUSHACC
MOVA,R1
PUSHACC
MOVA,R2
PUSHACC
MOVA,R3
PUSHACC
MOVA,R4
PUSHACC
MOVA,R5
PUSHACC
MOVA,R6
PUSHACC
MOVA,R7
PUSHACC
;PUSHSP;不必保存SP,任务切换时由相应程序调整
ENDM
;定义压栈宏
POPALLMACRO
;POPSP;不必保存SP,任务切换时由相应程序调整
POPACC;R0-R7出栈
MOVR7,A
POPACC
MOVR6,A
POPACC
MOVR5,A
POPACC
MOVR4,A
POPACC
MOVR3,A
POPACC
MOVR2,A
POPACC
MOVR1,A
POPACC
MOVR0,A
POPDPH
POPDPL
POPB
POPACC
POPPSW
ENDM
3.2.2OSStartHighRdy()函数的移植
使就绪状态的任务开始运行的函数叫做OSStart()。
在用户调用OSStart()之前,用户必须通过调用OSTaskCreate()或OSTaskCteateExt()建立至少一个以上自己的任务。
OSStartHighRdy()假设OSTCBHighRdy指向的是优先级最高任务的任务控制块。
在μC/OS-Ⅱ中处于就绪状态的任务的堆栈结构看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。
要想运行最高优先级任务,用户所要做的是将所有处理器寄存器按顺序从任务堆栈中恢复出来,并且执行中断返回。
为了简单一点,堆栈指针总是储存在任务控制块(即它的OS_TCB)的开头。
换句话说,也就是要想恢复的任务堆栈指针总是储存在OS_TCB的0偏址内存单元中。
OSStartHighRdy()程序流程图如图3-1所示。
图3-1OSStartHighRdy()程序流程图
移植的程序如下所示:
RSEG?
PR?
OSStartHighRdy?
OS_CPU_A
OSStartHighRdy:
USING0;使用寄存器第0组.上电后51自动关中断,此处不必用CLREA指令,因为到此处还未开中断,本程序退出后,开中断。
LCALL_?
OSTaskSwHook
(1)
OSCtxSw_in:
;OSTCBCur===>DPTR获得当前TCB指针
MOVR0,#LOW(OSTCBCur);获得OSTCBCur指针低地址,指针占3字节。
+0类型+1高8位数据+2低8位数据
INCR0
(2)
MOVDPH,@R0;全局
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ucos2 操作系统 51 单片机 移植