uCOS II的移植需要满足以下要求:
1)处理器的C编译器可以产生可重入代码:可以使用C调用进入和退出Critical Code(临界区代码);
2)处理器必须支持硬件中断,并且需要一个定时中断源;
3)处理器需能容纳一定数据的硬件堆栈;
4)处理器需有能在CPU寄存器与内存和堆栈交换数据的指令。
CPU的接口部分(Ports)–需要移植的代码部分
汇编文件(OS_CPU_A.ASM)、处理器相关C文件(OS_CPU.H、OS_CPU_C.C)中相关函数和变量的声明定义
OSStartHighRdy() //OS启动时调用,加载用户最高优先级的任务
OSCtxSw() //任务级调度
OSIntCtxSw() //中断级调度
OSTickISR() //时钟中断响应,将检测到延时结束的任务加入就绪队列,
.cdecls C, LIST, "msp430.h"
;用户钩子,对应os_cpu_c.c中的HOOK函数
.ref OSIntExit
.ref OSIntNesting
.ref OSISRStkPtr
.ref OSPrioCur
.ref OSPrioHighRdy
.ref OSRunning
.ref OSTCBCur
.ref OSTCBHighRdy
.ref OSTaskSwHook
.ref OSTimeTick
;全局量
.global OSCtxSw
.global OSCPURestoreSR
.global OSCPUSaveSR
.global OSIntCtxSw
.global OSStartHighRdy
.global OSTickISR
;中断使能操作之前保存SR,恢复中断使能时CPU状态不变
.asmfunc ;保存SR寄存器
OSCPUSaveSR:
MOV.W SR, R12
DINT
NOP
RETA
.endasmfunc
.asmfunc ;恢复SR寄存器,保持CPU状态
OSCPURestoreSR:
MOV.W R12, SR
NOP
RETA
.endasmfunc
.asmfunc
;OS初始化后,准备运行时调用
;准备运行的最高优先级的任务。任务就绪表第一个
;被OSStart()调用
OSStartHighRdy:
CALLA #OSTaskSwHook
MOV.B #1, &OSRunning ; 内核运行标志
MOVX.A SP, &OSISRStkPtr ; 保存中断堆栈,系统堆栈
MOVX.A &OSTCBHighRdy, R13 ; 将最高优先级任务准备
MOVX.A @R13, SP ;将SP指向OSTCBHighRdy,即任务堆栈
POPM.A #12, R15
RETI ; 模拟中断,中断退出应执行的内容在OSISRStkPtr中
.endasmfunc
.asmfunc
;任务级上下文切换
OSCtxSw:
POP.W R12 ;CALLA调用,当前PC地址被压入系统栈
POP.W R13 ;这里使用的是扩展内存地址,地址长度为20位
PUSH.W R12 ;因此压栈的写法有变化
RLAM.A #4, R13
RLAM.A #4, R13
RLAM.A #4, R13
MOVX.W SR, R12
ADDX.A R13, R12
PUSH.W R12 ;保存PC和SR
PUSHM.A #12, R15 ; 保存上一个任务的上下文到它的任务堆栈
MOVX.A &OSTCBCur, R13 ;任务堆栈构建完成
MOVX.A SP, 0(R13) ;前一个任务的上下文保存
CALLA #OSTaskSwHook ;用户钩子
MOV.B &OSPrioHighRdy, R13 ;切换任务
MOV.B R13, &OSPrioCur
MOVX.A &OSTCBHighRdy, R13 ;
MOVX.A R13, &OSTCBCur
MOVX.A @R13, SP ; 堆栈寄存器指向任务堆栈
POPM.A #12, R15 ; 从任务PCB中恢复寄存器内容,程序开始执行
RETI ; 从中断返回
.endasmfunc
;OSIntCtxSw()是唯一一个与编译器相关的函数,也是用户问的最多的。
;中断级上下文切换
;在此之前必须已经对上下文内容进行了保存
;由于中断已经发生,此处不需要再保存CPU寄存器
;OSIntCtxSw()需要调整堆栈指针,去掉堆栈中一些不需要的内容,以使堆栈中只包含任务的运行环境
.asmfunc
OSIntCtxSw:
CALLA #OSTaskSwHook
MOV.B &OSPrioHighRdy, R13
MOV.B R13, &OSPrioCur
MOVX.A &OSTCBHighRdy, R13
MOVX.A R13, &OSTCBCur
MOVX.A @R13, SP ; 堆栈寄存器指向任务PCB
POPM.A #12, R15 ;从任务PCB中恢复寄存器内容,程序开始执行
RETI ; 中断返回
.endasmfunc
;中断服务程序中会执行OSTimeTick()
;每进行一次中断,OSTimeTick()都会遍历所有挂起任务然后对有延时的任务控制块TCB中的OSTCBDly减1
;这样,当延时减为0时,就会把减为0的任务在就绪表中进行注册登记。
.sect ".text:_isr" ; 这个函数在中断向量空间
.asmfunc
;
OSTickISR: ;看门狗时钟中断
PUSHM.A #12, R15 ;保存寄存器内容
BIC.W #0x01, &SFRIE1 ;禁止看门狗中断
CMP.B #0, &OSIntNesting ;无中断嵌套,跳转
JNE OSTickISR1 ;跳转到下面的模块
MOVX.A &OSTCBCur, R13 ;保存任务堆栈
MOVX.A SP, 0(R13)
MOVX.A &OSISRStkPtr, SP ;加载中断堆栈
;只有当中断全部执行完毕,
OSTickISR1:
INC.B &OSIntNesting ;产生了一个时钟中断
BIS.W #0x01, &SFRIE1 ; 禁止看门狗
NOP
EINT ; 允许中断嵌套
NOP
CALLA #OSTimeTick ; 调用节拍处理函数
DINT ; 一般禁用中断之前调用OSIntExit()
NOP
CALLA #OSIntExit ;退出中断
CMP.B #0, &OSIntNesting ;中断无嵌套时,跳转
JNE OSTickISR2
MOVX.A &OSTCBHighRdy, R13 ;恢复任务堆栈寄存器
MOVX.A @R13, SP
OSTickISR2:
POPM.A #12, R15
RETI ; 从中断中返回
.endasmfunc
.sect WDT_VECTOR
.short OSTickISR ; 中断向量,看门狗定时器模式
.end
_EINT开总中断
DINT关总中断
RETI(从中断处理例程返回)
在这里使用的是看门狗时钟那么相应的也要打开看门时钟的中断,但是不要错误的把看门狗设置成安全模式,这样会造成系统反复重启
μC/OS-II 的移植需要用户改写OS_CPU_C.C中的六个函数:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
实际需要修改的只有OSTaskStkInit()函数,其他五个函数需要声明,但不一定有实际内容。这五个函数都是用户定义的对操作系统功能的扩展
其他的钩子函数已经嵌入到系统,会在操作系统执行某些操作时被调用,可以通过os_cfg.h来配置让系统不使用这些钩子
/*
* os_cpu.c
*
* Created on: 2016年11月20日
* Author: Tom
*/
#define OS_CPU_GLOBALS
#include "ucos_ii.h"
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookBegin (void) //进入OSInit()函数后,OSInitHookBegin就会被调用
{
}
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookEnd (void) //:OSInitHookEnd与OSInitHookBegin相似,
{
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTaskCreateHook (OS_TCB *ptcb) //OS_TCBInit会调用OSTaskCreateHook
{
ptcb = ptcb; /* 防止编译警告 */
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTaskDelHook (OS_TCB *ptcb) //OSTaskDel()会调用OSTaskDelHook(ptcb)
{
ptcb = ptcb; /* 防止编译警告 */
}
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251
void OSTaskIdleHook (void) //任务终止时调用
{
#if 0
LPM0; //进入低功耗
#endif
}
#endif
#if OS_CPU_HOOKS_EN > 0 //任务开始执行时调用
void OSTaskStatHook (void)
{
}
#endif
//创建任务时调用,初始化堆栈
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
INT16U tmp;
INT16U *top;
opt = opt;
top = (INT16U *)ptos;
top--;
*top-- = (INT16U) ((INT32U)task & 0xFFFF);
tmp = (INT32U)(((INT32U)task & 0xF0000) >> 4);
*top-- = (INT16U)0x0008 | tmp;
*top-- = (INT16U)0x0015;
*top-- = (INT16U)0x1515;
*top-- = (INT16U)0x0014;
*top-- = (INT16U)0x1414;
*top-- = (INT16U)0x0013;
*top-- = (INT16U)0x1313;
*top-- = (INT16U)((INT32U)p_arg >> 16);
*top-- = (INT16U)((INT32U)p_arg && 0xFFFF);
*top-- = (INT16U)0x0011;
*top-- = (INT16U)0x1111;
*top-- = (INT16U)0x0010;
*top-- = (INT16U)0x1010;
*top-- = (INT16U)0x0009;
*top-- = (INT16U)0x0909;
*top-- = (INT16U)0x0008;
*top-- = (INT16U)0x0808;
*top-- = (INT16U)0x0007;
*top-- = (INT16U)0x0707;
*top-- = (INT16U)0x0006;
*top-- = (INT16U)0x0606;
*top-- = (INT16U)0x0005;
*top-- = (INT16U)0x0505;
*top-- = (INT16U)0x0004;
*top = (INT16U)0x0404;
return ((OS_STK *)top);
}
//钩子会在系统触发某些操作时被调用,钩子里不添加代码就什么也不做,调用完马上返回
//钩子中添加的代码都是与系统相关的扩展,一般情况下不用添加,有特殊需要时才需要添加
#if OS_CPU_HOOKS_EN > 0
void OSTaskSwHook (void) //任务切换时调用这个钩子
{
}
#endif
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSTCBInitHook (OS_TCB *ptcb) //任务控制块初始化时调用
{
ptcb = ptcb;
}
#endif
#if OS_CPU_HOOKS_EN > 0
void OSTimeTickHook (void) //节拍器调用这个函数,可以添加代码,使每个节拍执行某些工作
{
}
#endif
重点就是OSTaskStkInit()函数的改写
定义与硬件无关的数据类型,堆栈的生长方向
/*
* os_cpu.h
*
* Created on: 2016年11月20日
* Author: Tom
*/
#ifndef OS_CPU_H_
#define OS_CPU_H_
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned int INT16U;
typedef signed int INT16S;
typedef unsigned long INT32U;
typedef signed long INT32S;
typedef float FP32;
typedef double FP64;
typedef unsigned int OS_STK; //堆栈宽度
typedef unsigned int OS_CPU_SR; //状态寄存器位宽
/*中断模式选择第三种*/
#define OS_CRITICAL_METHOD 3
/*简单中断指令,开关中断*/
#if OS_CRITICAL_METHOD == 1
#define OS_ENTER_CRITICAL() _DINT() /* 禁止*/
#define OS_EXIT_CRITICAL() _EINT() /* 允许*/
#endif
/*根据进中断的前的判断,退出中断时来决定中断使能*/
#if OS_CRITICAL_METHOD == 2
#define OS_ENTER_CRITICAL() /* 禁止*/
#define OS_EXIT_CRITICAL() /* 允许*/
#endif
/*中断使能与否与先前的中断状态有关。进中断前保存状态寄存器值,退出中断时恢复中断*/
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR())
#define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr))
#endif
#define OS_STK_GROWTH 1 /* 堆栈由高地址向低地址增长 ,与处理器堆栈有关*/
#define OS_TASK_SW() OSCtxSw() /*任务切换函数 ,中断服务程序(ISR)的入口点必须指向汇编函数OSCtxSw()*/
OS_CPU_EXT OS_STK *OSISRStkPtr; /*时钟中断堆栈指针*/
OS_CPU_SR OSCPUSaveSR(void); /*保存状态寄存器SR*/
void OSCPURestoreSR(OS_CPU_SR cpu_sr); /*恢复状态寄存器SR*/
OS_CPU_EXT OS_STK *OSISRStkPtr; /*中断响应的堆栈*/
void OSCtxSw (void); //任务级切换
void OSIntCtxSw (void); //中断级切换
void OSStartHighRdy (void); //OS启动时,开始执行用户最高优先级任务
#endif /* OS_CPU_H_ */
两个不同频率LED闪烁
#include
#include"ucos_ii.h"
#define TASK_STK_SIZE 64
OS_STK TaskStartStkA[TASK_STK_SIZE];
OS_STK TaskStartStkB[TASK_STK_SIZE];
void TaskStartA(void *data); /* 前导声明任务(函数) */
void TaskStartB(void *data); /* 前导声明任务(函数) */
void main (void)
{
WDTCTL = WDTPW + WDTHOLD; /* 禁止看门狗 */
OSInit(); /* 初始化uCOS-II */
OSTaskCreate(TaskStartA, (void *)0, &TaskStartStkA[TASK_STK_SIZE - 1], 0);
OSTaskCreate(TaskStartB, (void *)0, &TaskStartStkB[TASK_STK_SIZE - 1], 1);
OSStart(); /* 开始任务调度 */
}
void TaskStartA (void *pdata)
{
pdata = pdata; /* 无任何意义,防止编译器报警 */
WDTCTL = WDT_MDLY_32; /* 设置时钟节拍间隔为32ms */
SFRIE1|=WDTIE; /* 开看门狗定时器中断 */
/*************************************
* 应用程序初始化
*************************************/
P1SEL &= BIT0;
P1DIR |= BIT0; //led
while (1) {
P1OUT = BIT0;
OSTimeDly(10); /* 眼时10个时钟节拍,挂起本任务等待延时结束 */
P1OUT &= ~BIT0;
OSTimeDly(10);
}
}
void TaskStartB (void *pdata)
{
pdata = pdata; /* 无任何意义,防止编译器报警 */
WDTCTL = WDT_MDLY_32; /* 设置时钟节拍间隔为32ms */
SFRIE1|=WDTIE; /* 开看门狗定时器中断 */
P4SEL &= BIT7;
P4DIR |= BIT7;
while (1) {
P4OUT |= BIT7;
OSTimeDly(30); /* 眼时30个时钟节拍,挂起本任务等待延时结束 */
P4OUT &= ~BIT7;
OSTimeDly(30);
}
}
在我们的系统中使用系统的节拍器需要定义时钟任务的优先级
OS_TASK_TMR_PRIO
使用宏定义它
如果不需要使用延时等函数可以通过OS_TMR_EN来关闭它,那么也就不需要定义OS_TASK_TMR_PRIO了
除了这些os_cfg.h文件中只需要根据你的需求来裁剪系统,通过下面常量的定义
#define OS_FLAG_EN
#define OS_MBOX_EN
#define OS_MEM_EN 0
#define OS_MUTEX_EN
#define OS_Q_EN
#define OS_SEM_EN
#define OS_TMR_EN
#define OS_DEBUG_EN
#define OS_APP_HOOKS_EN
#define OS_EVENT_MULTI_EN
使用宏定义的方式决定需要开启和关闭的功能