uC/OS II是一个完整的、可移植、可裁减、源码公开的抢占式实时多任务操作系统。因此程序开发人员可以在嵌入式系统的开发过程中.灵活地改写其源代码.以满足用户特定的需求。PIC18F452是Microchip公司生产的单片机PICmicro家族中的中档微处理器产品.是一款含有丰富片上资源的8位MCU.广泛应用在家用电器、医疗设备、工业控制等领域。因此uC/OS II在该处理器上的成功移植.将大大提高复杂应用系统的开发效率.增强系统的可靠性,降低开发成本,提高经济效益。

1 uC/OS II移植方法
1.1 uC/OS II成功移植的条件
要把uC/OS II成功地移植到某一处理器上.该处理器必须满足以下要求:

  1. 处理器的C编译器能产生可重入代码。

  2. 用C语言就可以打开和关闭中断。

  3. 处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。

  4. 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。

  5. 处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。

   而Microchip PIC18F452的体系结构可以满足移植uC/OS II的硬件需求.同时Microchip提供的C18编译器也能满足移植的需要。

1.2 uC/OS II移植的相关工作
uC/OS II的移植工作主要涉及与处理器相关的以下内容:

  1. 与编译器相关的数据类型声明(OS_CPU.H)不同的处理器有不同的字长.所以必须定义一系列数据类型以确保移植的正确性。文件OS_CPU.H中声明了10个相关数据类型。

  2. 改写与任务管理相关的函数(OS_CPU_C.C)uC/OS II移植需要改写6个与任务管理相关的函数.它们是:OSTaskStkInit()、OSTaskCreatHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskStatHook()、OSTaskTickHook()
       其中只需对OSTaskStkInit()编写代码,后5个函数必须声明,但是内部并没有代码。OSTaskCreate()和OsTaskCreateExt()通过调用OSTaskStkInit()来初始化任务的堆栈结构。

  3. 编写与任务切换相关的函数(OS_CPU_A.ASM)
       uC/OS II的移植要求用户编写四个与处理器相关的汇编语言函数:OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()、OSTickISR()。
       如果用户的编译器支持插入汇编语言代码,可将所有与处理器相关的代码放到OS_CPU_C.C文件中,该文件便不再需要。

  4. 编写中断服务程序CPUhighInterruptHook() 数和CPUlwoInterruptHook()函数

2 uC/OS II在PIC18F452上的移植实现
2.1数据类型定义
   在uC/OS II中,不使用c的short、int和long等数据类型。下面就是uC/OS II定义的一部分数据类型。
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;/*无符号8位整数 */
typedef signed char INT8S; /*有符号8位整数 */
typedef unsigned int INT16U; /*无符号16位整数 */
typedef signed int INT16S; /*有符号16位整数 */
……

2.2宏定义
   包括开关中断的宏定义,以及进行任务切换的宏定义。
#define OS_CRITICAL_METHOD 3
……
#if OS_CRITICAL_METHOD==3 /*关开中断方式*/
#define OS_ENTER_CRITICAL() \
cpu_sr=INTCON & 0b11000000;\
INTCON &=(0b00111111|(RCON&0b10000000))
#define OS_ENTER_CRITICAL_HIGH() \
cpu_sr=INTCON & 0b11000000;\
INTCON &=0b00111111
#define OS_EXIT_CRITICAL() INTCON |=cpu_sr
#endif
……
void OSCtxSw(void);
#define OS_TASK_SW() OSCtxSw()
(1)开关中断宏
   与其他实时系统一样,uC/OS II在进入系统临界代码区之前要关闭中断,等到退出临界区后再打开,从而保护核心数据不被多任务环境下的其他任务或中断破坏。uC/OS II定义了两个宏用来关闭/打开中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。uC/OS II中提供三种开关中断的方法。本移植中实现OS_ENTER_CRITICAL()用的是第3种方法,将CPU的状态字寄存器保存到局部变量中。而OS_EXIT_CRITICAL()从这个局部变量中恢复状态寄存器。该移植中使用的局部变量为OS_CPU_SR类型的cpu_sr。此方法还定义了一个关中断的宏OS_ENTER_CRITICAL_HIGH(),与OS_ENTER_CRITICAL()不同的是它不仅可以禁止低优先级中断,而且可以禁止高优先级中断。后者只能禁止低优先级中断。
(2)任务切换宏OS_TASK_SW()
   上下文切换时可直接调用该宏,因为在PIC18中是没有软中断的。

2.3堆栈
   uC/OS II是一个多任务的调度器,每个任务都有自己的堆栈。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。OS_STK数据类型和处理器的寄存器长度是一致的,PIC18F452的寄存器长度为8位,并定义堆栈由低地址向高地址增长,以便在调用任务建立函数OSTaskCreate()或OSTaskCreateExt()时,告知堆栈是增长方向:
typedef INT8U OS_STK;/*栈类型(8位宽)*/
#define OS_STK_GROWTH 0 /*定义栈增长方向为由低到高*/

2.4时钟
   PIC18F452有8种振荡器模式。设计中选用的是XT模式,故决定了时钟频率为4Mhz。

2.5任务切换
   由于uC/OS II是一个抢占式的内核,所以它总是执行处于就绪态的优先级最高的任务。任务在执行时会调用uC/OS II提供的一个服务来等待时间过期,或者信号量的到来,或者另一个任务或中断服务例程的消息的到来。当内核发现有优先级更高的任务处于就绪态时,便进行任务间切换。其处理过程如下:

  1. 任务调用uC/OS II提供的OSTimeDly()服务;

  2. OSTimeDly()将任务放到正在等待任务过期的一串任务里面;

  3. 因为任务不可能再被执行,系统会调用调度器(OSSched())找出下一个优先级最高的任务来运行;

  4. 上下文切换是通过调用OSCtxSw()来完成的。
    OSCtxSw()是用内嵌汇编语言写的,因为它要直接操作PIC中的寄存器。

2.6中断服务例程
   根据uC/OS II的要求,用户要编写自己的中断服务例程。所有的中断是在CPUhighInterruptHook()函数和CPUlowInterruptHook()函数中处理的.用户只需要提供处理中断的代码。时钟中断中的必须是一个低优先级的中断.并且需要定时调用OSTimeTick()。
   在CPUlowInterruptHook()函数处理完中断服务子程序后,CPU相关代码开始退出中断。调用OSIntExit()使中断嵌套层数递减,当嵌套层数为0时,所有的中断嵌套结束,系统通过调用OSSched()判断CPU应该回到先前被中断的任务,还是运行高优先级的任务。如果有一个高优先级的任务,uC/OS II会通过任务切换使CPU运行此任务。
void CPUlowInterruptHook(void)
{
if(INTCONbits.TMROIF){ //检查TMR0是否溢出
INTCONbits.TMR0IF=0; //清除中断标志
TMR0H=0xD8; //设置时间常数10 ms.(at 4MHz)
TMR0L=0xA0;
OSTimeTick(); //调用OS时钟请求
}
/*此处插入用户的中断程序*/
}
#pragma interrupt CPUhighInterruptHook
void CPUhighInterruptHook(void)
{
/*此处插入高优先级中断程序,但不能使用任何OS功能调用*/
}

3 用户实时任务编写
   uC/OS II中的实时任务是在系统初始化(调用OSInit()和OSCtxSw())后,通过OSTaskCreateExt()调用创建的,实时任务创建完成后,调用OpenTimer0()设置时钟中断,最后调用OSStart(),系统开始运行并进行任务调度。
   为了测试移植的结果,使用高奇ICD DEMO教学实验板并利用板上资源创建TempTask()、LEDTask()及通过RS232实现的Shell任务。其中,Shell任务接收并执行用户的Shell命令.并通过LCDTask()任务显示该命令.TempTask()则实现则周期性地采集与RA0-RA3相连接的温度。由于PIC的USART中只有2个字节的FIFO缓冲队列,快速的通信过程中很容易丢失数据.故除了通信的实现采用中断方式之外.还设计了一个30Byte的缓冲队列,存放接收到的数据,接收数据由设计在CPUlowInterruptHook()中的代码完成,主要代码如下:
while((PIE1bits.RCIE)&&(PIR1bits.RCIF))
{
c=RCREG;
t=q.rear+1;
if(t==MAX_LEN)//接收的数据存放到队列中
t=0;
if(t==q.front)
break; //溢出
q.rear=t;
q.data[q.rear]=c;
}

4 结束语
   目前正在进行的水质控制系统中采用PIC MCU作为下位机负责数据采集、数据简单处理、控制执行机构以及与上位机的通信。该项目中引人uC/OS II后.系统开发效率明显提高。下一步考虑将其与已经设计完成的以太网控制器结合,实现嵌人式控制系统的TCP/IP互连。