uC/OS-II在ARM微处理器上的移植
引言:在开发嵌入式系统时,一般选择基于ARM 和uC/OS-II 的嵌入式开发平台,因为ARM 微处理器具有处理速度快、超低功耗、价格低廉、应用前景广泛等优点。uC/OS - II 是由Jean J . Labrosse 先生编写的完整的可移植、固化、裁剪的占先式实时多任务内核。uC/OS - II 结构简单,容易移植,适于学习。
1.硬件平台要求
要使uC/OS – II可以正常工作,处理器必须满足如下工作要求:
1. 处理器的C编译器能产生可重入代码。
2. 在程序中可以打开或者关闭中断。
3. 处理器支持中断,并且能产生中断(通常10HZ~100HZ之间)。
4. 处理器支持能够容纳一定数据的硬件堆栈。
5. 处理器有将堆栈指针和其他CPU寄存器存储和读出到堆栈的指令。
ARM的大多数处理器完全满足上述要求。
下面是uC/OS的文件结构图:
应用软件 |
核心代码 Os_core.c OS_FLAG.C Os_task.c OS_MUTEX.C Os_mem.c Os_q.c Os_sem.c Os_time.c Os_ii.c Os_ii.h Os_mbox.c
|
设置代码 (应用相关) Os_cfg.h Includes.h |
移植代码(处理器相关) OS_CPU.H , OS_CPU_A.ASM , OS_CPU.C |
2. 移植需要修改的部分
数据类型:BOOLEA, INT8U, INT8S, INT16U, INT16S, INT32U, INT32S, FP32, FP64, OS_STK, OS_CPU_SR, 这些都在OS_CPU.H中。
宏与宏定义:OS_STK_GROWTH,OS_ENTER_CRITICAL() 与OS_EXIT_CRITICAL() 它们都在OS_CPU.H中。
汇编函数:OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR(), 这三个在OS_CPU_A.ASM中,它们的修改相对比较复杂。
OS_CPU_C.C 中要修改的C语言函数:OSTaskStkInit(), OSInitHookBegin (void), OSInitHookEnd (void), OSTaskCreateHook (OS_TCB *ptcb), OSTaskDelHook (OS_TCB *ptcb), OSTaskStatHook (void), OSTaskSwHook (void), OSTCBInitHook (OS_TCB *ptcb), OSTimeTickHook (void), OSTaskIdleHook (void)。这10个函数都修改只有OSTaskStkInit()的修改比较复杂,其他的函数很多都是空的。
3. 与处理器和编译器相关的代码
1)与编译器相关的数据类型,放在OS_CPU.H。
比如typedef unsigned char INT8U; // Unsigned 8 bit quantity
2) OS_ENTER_CRITICAL() 与OS_EXIT_CRITICAL()
#define OS_CRITICAL_METHOD 1//修改这一句就可以修改两个以上两个函数的定义?
3)OS_STK_GROWTH
定义堆栈的增长方向:#define OS_STK_GROWTH 1 //从下往上
同理#define OS_STK_GROWTH 0 //从上往下
4. OS_CPU_C. C及其6个用 C语言编写的函数
1)OSTaskStkInint()用于任务栈的初始化,在创建任务和异常的时候都会被调用到,
2)OSTaskCreateHook (OS_TCB *ptcb)在创建任务和异常的时候都会被调用到,该函数调用时中断是被禁止的,所以要缩短代码,以缩短中断的相应时间。当它被调用时,回收到指向已建立任务的OS_TCB指针。
3)OSTaskDelHook (OS_TCB *ptcb)收到被删除任务的OS_TCB指针。
4)OSTaskSwHook ()任务切换是调用到,这个函数也要注意缩短响应时间。
5)OSTaskStatHook (void)每秒钟都会被OSTasKStat()调用一次。用户可用它来扩展统计功能。
6)OSTimeTickHook()在每个时钟节拍都会被OSTaskTick()调用。实际上是在节拍被uC/OS-II真正处理,并通知用户的移植实例或应用程序之前别调用的。
后5个可以不用加代码只有OS_CFG.H中的OS_CPU_HOOKS_EN被置为1时才会产生这些函数的代码。
5. 用汇编语言编写的4个处理器相关的函数(OS_CPU_A.ASM)
OS_CPU_A. S 文件的移植需要对处理器的寄存器进行操作,所以必须用汇编语言来编写。这个文件的实现集中体现了所要移植到处理器的体系结构和uC/OS-II 的移植原理。它包括4 个子函数:OSStartHighRdy() ,OSCtxSw() ,OSIntCtxSw() ,OSTick2ISR()。 其中难点在于OSIntCtxSw() 和OSTickISR() 函数的实现,因为这两个函数的实现与移植者的移植思路以及相关硬件定时器、中断寄存器的设置有关。在实际的移植工作中,这两处也是比较容易出错的地方。
OSIntCtxSw( ) 函数由OSIntExit ( ) 函数调用,而OSIntExit () 函数又由OSTickISR() 调用。OSIntCtxSw()函数最重要的作用就是它完成在中断ISR 中直接进行任务切换,从而提高了实时响应的速度。它发生的时机是在ISR 执行到OSIntExit ( ) 时,如果发现有高优先级的任务因为等待time_tick 的到来获得了执行。uC/OS-II 在ARM系统上的移植与实现的条件,就可以马上被调度执行,而不用返回被中断的那个任务之后再进行任务切换。实现OSIntCtxSw() 的方法大致也有两种情况:一是通过调整SP 堆栈指针的方法,根据所用的编译器对于函数嵌套的处理,通过精确计算出所需要调整的SP 位置来使得进入中断时所作的保护现场的工作可以被重用。 二是设置需要切换标志位的方法,在OSIntCtxSw( ) 里面不发生切换,而是设置一个需要切换的标志,等函数嵌套从进入OSIntExit ( ) = > OS ENTER CRITI2CAL() = > OSIntCtxSw( ) = > OS EXIT CRITICAL() = > OSIntExit ( ) 退出后,再根据标志位来判断是否需要进行中断级的任务切换。
其次是对OSTickISR() 修改,OSTickISR() 首先在被中断任务堆栈中保存CPU 寄存器的值,然后调用OSIntEnter () 。随后调用OSTimeTick() ,检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。最后调用OSIntExit ( ) 。如果在中断中(或其他嵌套的中断) 有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,OSIntExit ( ) 将进行任务调度。如果进行了任务调度,OSIntExit () 将不再返回调用者,而是用新任务的堆栈中的寄存器数值恢复CPU 现场,然后实现任务切换。如果当前中断不是中断嵌套的最后一层,或中断中没有改变任务的就绪状态,,OSIntExit ( ) 将返回调用者OSTickISR ( ) ,OSTickISR() 返回被中断的任务。 最后就是退出临界区和进入临界区函数。 进入临界区时,必须关闭中断,用ARMDisableInt () 函数实现。在退出临界区的时候恢复原来的中断状态,通过ARMEnableInt ( ) 函数来实现。 至于进行任务级上下文切换,则是由汇编子程序OSCtxSw 实现。
应用软件 |
核心代码 Os_core.c OS_FLAG.C Os_task.c OS_MUTEX.C Os_mem.c Os_q.c Os_sem.c Os_time.c Os_ii.c Os_ii.h Os_mbox.c
|
设置代码 (应用相关) Os_cfg.h Includes.h |
移植代码(处理器相关) OS_CPU.H , OS_CPU_A.ASM , OS_CPU.C |
[参考文章]
《嵌入式实时操作系统uC/OS-II》(第二版) Jean J.Labrosse著 邵贝贝 等译
《uC/OS-II在ARM系统上的移植与实现》作者:李学桥、刘放美
《嵌入式系统设计与实例开发——基于ARM微处理器与uC/OS-II实时操作系统》(第二版) 王田苗 主编