推广 热搜: 电机  PLC  变频器  服务机器人  培训  变送器  危化品安全,爆炸  西门子PLC  触摸屏  阀门 

μC/OS-II实时嵌入式操作系统在AVR mega系列MCU上的移植

   日期:2013-03-23     来源:工控之家网    作者:工控之家    浏览:69    评论:0    
摘  要:本文以AVR mega系列单片机为平台详细介绍了源代码公开的实时嵌入式操作系统μC/OS-II的内核代码及移植方法,并对系统的相关性能进行了测试,为在8位单片机上进行嵌入式系统开发提供了参考。

关键词:μC/OS-II; AVR mega; 移植; 系统测试

1 简介

  随着技术的发展,在近几年中,嵌入式系统的设计及应用对人们生活产生了很大的影响,并将逐渐改变人们未来的生活方式。在特定的操作系统之上开发应用程序,可以使得开发人员忽略掉很多底层硬件细节,使得应用程序调试更方便,易于维护,同时开发周期也缩短,降低了开发成本,因而嵌入式实时操作系统深得开发人员的青睐。

  μC/OS-II是一种专门为微处理器设计的抢占式实时多任务操作系统,具有源代码公开、可移植性、可裁减、稳定性和可靠性高等特点。其内核主要提供进程管理、时间管理、内存管理等服务,系统最多支持56个任务,每个任务均有自己单独的优先级。由于其内核为抢占式,所以总是运行优先级最高的任务。系统提供了丰富的API函数,便于实现进程间的通信及进程状态的转化。由于μC/OS-II是为嵌入式应用编写的通用软件,故在具体应用时需根据不同单片机的特点进行移植,其大部分代码是用标准C语言所写,只有与处理器相关的一部分代码用汇编语言写成,因而具有很强的移植性,能在多数8位、16位、32位单片机及数字信号处理器上实现运行。

  AVR mega系列单片机是基于AVR RISC的,低功耗的8位单片机,内部有32个通用寄存器。通过在一个时钟周期内执行一条指令,运行速度可以达到1MIPS/MHz的性能。AVR单片机内核有丰富的指令集,通过32个通用寄存器直接与逻辑运算单元相连接,允许在一个时钟周期内一条单一的指令访问两个独立的寄存器。这样的结构使得代码的执行效率比传统的复杂指令集的微处理器快近10倍。AVR mega128是mega系列里功能最强大、资源最丰富的一款单片机,有128k的在系统可编程flash,4k字节的SRAM和EEPROM,为系统的移植提供了一个良好的平台。要实现μC/OS-II在AVR mega 系列MCU上的移植,需要有AVR系列单片机的编译器,这里采用的是GNU推出的AVR-GCC编译器。

2 移植工作概要

  移植工作主要包括以下几个内容:

  ● 用#define设置一个常数的值(OS_CPU.H);

  ● 声明9个数据类型(OS_CPU.H);

  ● 用#define声明三个宏(OS_CPU.H);

  ● 用C语言编写六个函数(OS_CPU_C.C);

  ● 编写四个汇编语言函数(OS_CPU_A.ASM).

  要移植μC/OS-II,必须编写OS_CPU.H、OS_CPU_A.ASM、OS_CPU_C.C三个文件。这三个文件与芯片的硬件特性有关,它们主要提供任务切换与时钟的功能。μC/OS-II其他内核代码均用C语言写成,它们为系统提供任务管理、进程间通信、时间管理以及内存管理等功能。μC/OS-II软、硬件体系结构如下图所示:


图1 μC/OS-II软件、硬件体系结构

  INCLUDES.H是一个头文件,在所有后缀为.C的文件开始都包含INCLUDES.H文件,其主要包含CFG.H、OS_CPU.H、UCOS_II.H三个文件。对于不同类型的处理器,还需要改写INCLUDES.H文件,增加自己的头文件,但必须加在文件末尾。OS_CFG.H主要包含的是一些二值常量,通过对这些常量置1或0,可以方便的对内核进行裁减,这是μC/OS-II较为突出的一个优点。

3 移植代码分析

  3.1 OS_CPU.H文件

  OS_CUP.H包括用#define定义的与处理器有关的常量,宏和类型定义。大体结构如下:

  typedef unsigned char BOOLEAN;

  typedef unsigned char INT8U; /* 无符号8位数 */

  typedef signed char INT8S; /* 带符号8位数 */

  typedef unsigned int INT16U; /* 无符号16位数 */

  typedef signed int INT16S; /* 带符号16位数 */

  typedef unsigned long INT32U; /* 无符号32位数 */

  typedef signed long INT32S; /* 带符号32位数 */

  typedef float FP32; /* 单精度浮点数 */

  typedef unsigned char OS_STK; /* 堆栈入口宽度为8位 */

  #define OS_STK_GROWTH 1 /* 堆栈由高地址向低地址增长 */

  #define OS_ENTER_CRITICAL() asm volatile ("cli")/* 禁止中断 */

  #define OS_EXIT_CRITICAL() asm volatile ("sei")/* 允许中断 */

  #define OS_TASK_SW() OSCtxSw() /* 任务级的切换函数 */

  (1)数据类型

  由于不同的处理器有不同的字长,μC/OS-II的移植需要重新定义一系列的数据结构以确保其可移植性。用户还需通过OS_STK声明正确的C数据类型将任务堆栈的数据类型通知μC/OS-II。在AVR mega系列中堆栈是按8位字长进行操作的,所以堆栈数据类型OS_STK被声明为8位。所有的任务堆栈都必须用OS_STK来声明数据类型。

  (2)代码临界区

  μC/OS-II在进入系统临界代码区之前需关中断,退出临界区后再开中断,则μC/OS-II能够保护临界区代码免受多任务或中断服务例程的破坏。在AVR mega系列单片机中,通过设置状态寄存器SREG中的中断屏蔽位来实现。μC/OS-II中的宏OS_ENTER_CRITICAL()定义将状态寄存器中的中断屏蔽位清除,以屏蔽所有的中断;OS_EXIT_CRITICAL()定义将状态寄存器的中断屏蔽位置位以允许所有的中断。

  (3)堆栈方向

  AVR mega系列微处理器的堆栈是由高地址向低地址递减的,所以OS_STK_GROWTH必须设置为1。

  (4) OS_TASK_SW()函数的定义

  在μC/OS-II中,OS_TASK_SW()用来实现任务切换。在进行宏定义时直接定义为OSCtxSw()函数,即任务级切换函数。

  3.2 OS_CPU_A.ASM文件

  3.2.1 相关函数

  移植μC/OS-II时需要改写OS_CPU_A.ASM中的四个汇编语言函数:

  ● OSStartHighRdy()

  ● OSCtxSw()

  ● OSIntCtxSw()

  ● OSTickISR()

  3.2.2 函数介绍

  (1) OSStartHighRdy()函数

  该函数由OSStart()函数调用,功能是运行优先级最高的就绪态任务。在调用OSStart()函数之前,用户必须调用OSInit(),并且至少已经创建了一个任务。当调用OSStart()函数时,获得了指向就绪任务中最高优先级任务的OS_TCB的地址指针OSTCBHighRdy,并把这个指针赋值给OSTCBCur。OSStartHighRdy()通过OSTCBHighRdy,就可以从任务的控制块中获得堆栈指针。由于OSTaskCreate()函数创建任务时堆栈初始化为模拟一次中断发生的结构,于是当弹出所有保存的寄存器后,运行RET命令就可以切换到了新任务。

  OSStartHighRdy()函数的汇编代码及相关介绍如下:

  CALL OSTaskSwHook /* 调用用户自己定义的函数 */

  LDS R16,OSRunning /* 设置标识系统开始运行的变量 */

  INC R16

  STS OSRunning,R16

  LDS R30,OSTCBHighRdy /* Z指针指向最高优先级任务的OS_TCB */

  LDS R31,OSTCBHighRdy+1

  LD R28,Z+

  out SPL,R28

  LD R29,Z+

  out SPH,R29 /* 从OS_TCB中获取堆栈指针 */

  POPRS /* 恢复所有的寄存器 */

  RET /* 任务开始执行 */

  (2) OSCtxSw()函数

  OSCtxSw()是一个任务级任务切换函数,其只在任务中被调用,区别于中断程序中调用的切换函数OSIntCtxSw();在μC/OS-II中,如果任务调用某个函数,而该函数的执行结果可能会造成系统任务重新调度,例如试图唤醒了一个优先级更高的任务,则需进行任务切换。由于AVR mega128不支持软中断指令,移植时于OS_CPU.H文件直接定义了OSCtxSw()函数。

  OSCtxSw()函数的汇编代码及相关介绍如下:

  PUSHRS /* 保存当前环境 */

  LDS R30,OSTCBCur

  LDS R31,OSTCBCur+1 /* Z指针指向当前任务的OS_TCB */

  in r28,SPL

  ST Z+,R28

  in r29,SPH

  ST Z+,R29 /* 保存堆栈指针到OS_TCB中 */

  CALL OSTaskSwHook /* 调用用户自己定义的函数 */

  LDS R16,OSPrioHighRdy /* OSPrioCur = OSPrioHighRdy */

  STS OSPrioCur,R16

  LDS R30,OSTCBHighRdy

  LDS R31,OSTCBHighRdy /* z指针指向高优先级任务的OS_TCB*/

  STS OSTCBCur,R30

  STS OSTCBCur+1,R31 /* OSTCBCur = OSTCBHighRdy */

  LD R28,Z+

  out SPL,R28

  LD R29,Z+

  out SPH,R29 /* 获得堆栈指针 */

  POPRS /* 更新寄存器内容 */

  RET /* 任务切换 */

  (3) OSIntCtxSw()函数

  在μC/OS-II中,由于中断的发生可能引起任务切换,故在中断服务程序的最后会调用OSIntExit()函数检查任务就绪状态。如果要进行任务切换,将调用OSIntCtxSw()函数,因而OSIntCtxSw()又称为中断级的任务切换函数。由于在调用OSIntCtxSw()之前已经发生了中断,OSIntCtxSw()默认寄存器已经保存在被中断任务的堆栈中了。OSIntCtxSw()的代码大部分和OSCtxSw()函数相同,不同之处在于:

  ● 由于中断已经发生,寄存器入栈的工作已经完成,在此不需要再保存寄存器的内容;

  ● 在中断服务子程序中调用OSIntExit()时,将返回地址推入了堆栈。OSIntExit()中的进入临界函数OS_ENTER_CRITICAL()可能会将CPU的状态字也推入了堆栈,这取决于中断是怎么关掉的。同时OSIntCtxSw()的返回地址也会被推入堆栈中。

  当任务挂起时,栈结构应该与μC/OS-II所规定的完全一致。所以当任务挂起时,OSIntCtxSw()需要对栈指针进行调整,即调整的栈结构要保证所有挂起的任务的栈结构看起来是一样的。调整的方法很简单,只要将堆栈指针加一个固定的值就可以了。

  (4) OSTickISR()函数

  在μC/OS-II中,当调用OSStart()启动多任务环境后,时钟中断非常重要。在时钟中断中处理所有与定时器相关的工作,如任务的延时、等待操作等。在时钟中断中将查询处于等待状态的任务,判断是否延时结束,以重新进行任务调度。OSTickISR()采用μC/OS-II中的其他中断服务程序原型,移植时采用了AVR mega128的8位定时器0的溢出中断来产生时钟节拍,溢出时间为10ms,并在OSTickISR()中重新装载定时器的起始值。

  OSTickISR()函数汇编代码及相关介绍如下:

  OSTickISR:

  SEI /* 重新开中断 */

  PUSHRS /* 保存所有寄存器 */

  LDS R16,OSIntNesting /* μC/OS-II中断服务程序原型 */

  INC R16

  STS OSIntNesting,R16

  CALL OSTimeTick /* 调用时钟节拍处理函数 */

  CALL OSIntExit /* μC/OS-II中断服务程序原型 */

  LDI R16,256-(11059200/50/1024)

  OUT TCNT0,R16 /* 重载定时器,定时10ms */

  POPRS /* 恢复寄存器的值 */

  RET /* 中断返回 */

  3.3 OS_CPU_C.C文件

  μC/OS-II的移植需要用户在OS_CPU_C.C中定义6个函数,而实际上需要定义的只有OSStkInit()一个函数,其他5个函数需要声明,但不一定有实际内容。这五个函数是用户自己定义的。使用时需要将OS_CFG.H里的OS_CPU_HOOKS_EN定义为1,设置为零表示不使用这些函数。在移植代码中并不要求使用这几个函数,故只定义其位空函数。这5个函数分别为:

  ● OSTaskCreateHook()函数

  ● OSTaskDelHook ()函数

  ● OSTaskSwHook ()函数

  ● OSTaskStatHook ()函数

  ● OSTimeTickHook ()函数

  OSTaskStkInit()函数由任务创建函数OSTaskCreate()或OSTaskCreateExt()调用,用来初始化任务的堆栈。初始状态的堆栈模拟发生一次中断后的堆栈结构。按照中断后的进栈次序预留各个寄存器的存储空间,而中断返回地址指向任务代码的起始地址。由于AVR mega128的堆栈是8位宽的,OSTaskStkInit()将创建一个指向以字节为单位的内存区域的指针,同时要求堆栈指针指向空堆栈的顶端。堆栈初始化工作结束后,OSTaskStkInit()返回新的堆栈指针,OSTaskCreate()或OSTaskCreateExt()将这个堆栈指针保存到任务的OS_TCB上。这里要注意的是,堆栈中的SREG初始化为0x80,即使得任务启动后允许中断的发生;如果设置为0x00,则任务启动后将禁止中断。如果选择任务启动后允许中断发生,则所有的任务运行期间中断都允许;同样,如果选择任务启动后禁止中断,则所有的任务都禁止中断发生,而不能有所选择。如果某个任务选择启动后禁止中断,那么其他的任务在运行的时候需要重新开启中断。同时还需要修改OSTaskIdle()和OSTaskStat()函数,在运行时开启中断。如果以上任何环节出现问题,系统就会崩溃。所以在初始化任务堆栈时设置状态寄存器SREG为0x80,即允许任务启动后中断。

4 系统测试

  随着嵌入式产品的广泛应用,嵌入式系统的研究也在不断发展,出现了很多的实时系统。比如实时系统VxWorks,RT-Linux等。但这些操作系统主要在32位处理器上使用,并且需要较高的成本。而由于8位单片机本身资源的限制,以前很少有在8位机上运行的操作系统,随着8位机功能和资源的增加,渐渐出现了不少使用在8位单片机上的实时系统,比如在AVR单片机上的AVRX,NUT/OS等。这些操作系统主要用汇编语言写成,相对来讲代码效率较高,但也有他本身的限制,即作为一种专用的操作系统而言,它们很难移植到其他平台上和在其他场合使用。μC/OS-II作为一个用C语言编写成的操作系统,已被广泛的移植到各种平台上。同时因为其用C语言所写,性能比汇编语言编写的操作系统稍差,故在移植工作完成后,对系统进行了测试。

  一个实时进程在系统中变成可运行到进程实际开始它的执行,是有一定的延迟时间的,即分配时间。这段时间与内核密切相关。为了测试这段时间,这里只建立了一个任务,该任务在等待一个信号量,收到信号量后对一个计数值进行简单的加1,而给任务发信号量的过程采用定时器2的比较匹配中断完成。AVR单片机的定时器有比较匹配中断模式,当定时器计数与预设值相等时,可以再次从0开始计数。比较匹配中断的程序采用μC/OS-II的中断服务程序原型,并且在中断程序中给任务发信号量,而且定义一个变量记录中断发生的次数。如果在所设定的比较时间里,任务程序中的计数值和中断程序中的计数值相等,表明任务在该时间内能满足分配时间。通过不断缩小比较匹配的时间发现,当时间减小到24微秒时,任务已经没有运行了,任务中的计数值为0,表明任务没来得及运行又被比较匹配中断程序占用了处理器。而当大于24微秒时,运行了六千次任务调度,可以完成。对于处理速度相对高档CPU而言已是较慢的8位单片机而言,24微秒能满足应用的需要。

  在实时系统中,实时系统的实时性表现在系统对外部事件的响应能力上。系统通过中断来响应外部事件的发生,并且用户的中断服务中做的事要尽量的少,把大部分工作留给任务去做,只是通过信号量或者消息队列机制来通知任务运行。因而任务对外部事件的响应时间就是一个重要的性能指标,这里对这段时间进行了测试。程序中建立了一个任务,等待在一个信号量上。mega128的定时器2设置为比较匹配输出模式,在匹配时间到了之后产生一定周期的脉冲输出并产生中断。设置定时器1为计数模式来计数产生的脉冲输出。通过定时器2的比较匹配中断服务子程序来发信号量通知任务运行时,并且在中断子程序中不开中断,而在任务收到信号量得到运行后才开中断,以实现中断处理与任务运行的同步。并且任务中对一个全局变量计数器加1,记录任务运行的次数。如果运行一段时间后在设置的比较匹配时间里任务的计数器和定时器1计数值相等,则系统在这段时间里是能完全响应外部事件。当定时器2的比较匹配时间大于39微秒时,任务运行六千次后退出任务循环并停止定时器1的计数,这两个计数器的值相等,都为六千次。当匹配时间小于39微秒时,定时器1的计数值大于任务的计数值六千,任务的处理时间不能完全响应外部事件的发生。即系统对外部事件的响应和处理时间为39微秒,而只有外部事件的发生大于这段时间才能保证系统的实时性。而当匹配时间减小到24微秒即系统的分配时间时,任务计数器的值为零,而定时器1不断在计数,这表明任务已得不到运行,即与前述结论相符。

7 结束语

  μC/OS-II作为通用的嵌入式实时操作系统,已广泛使用在各种场合。通过在AVR Mage 系列单片机上的移植和测试,说明了其在8位单片机上的可能性。并且由于其公开了源代码,结合8位机的特性,可以在此基础上进行系统的裁减和扩展,使之能达到更好的效果,本文为嵌入式系统应用基础提供了借鉴。

参考文献

  [1] 耿德根,宋建国,马潮,叶勇建. AVR高速嵌入式单片机原理与应用.北京:北京航空航天大学出版社,2001.

  [2] Jean J.Labrosse著.邵贝贝译. μC/OS-II – 源码公开的实时嵌入式操作系统.北京:中国电力出版社,2001.

  [3] 屠祁,屠立德. 操作系统基础.北京:清华大学出版社,2000.

 
打赏
 
更多>同类环保知识
0相关评论

推荐图文
推荐环保知识
点击排行
网站首页  |  免责声明  |  联系我们  |  关于我们  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  RSS订阅  |  违规举报  |  鲁ICP备12015736号-1
Powered By DESTOON