基于MSP430F5529的μc/os嵌入式实时操作系统移植

μc/os移植的条件

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)中相关函数和变量的声明定义

OS_CPU_A.ASM文件改写

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

OS_CPU_A.ASM中与MSP430硬件机制相关的部分

1.MSP430单片机有R0~R15十六个通用寄存器,其中PC占用R0,SP占用R1,SR占用R3,因此,切换任务时有16个寄存器值需要保存。

2.MSP430开关中断

_EINT开总中断
DINT关总中断

3.中断处理流程

  1. 任何当前执行的指令完成。
  2. 指向下一条指令的PC 被压入堆栈。
  3. SR 被压入堆栈。
  4. 如果在最后一个指令执行期间由多个中断出现,那么具有最高优先级的中断被选中并等待被处理。
  5. 在单一源标志上,中断请求标志自动复位。对于软件处理,多个源标志保持被设定。
  6. SR 被清除。这将终止任何低功耗模式。由于GIE 位被清除,之后的中断被禁用。
  7. 中断矢量的内容被载入到PC:程序继续在中断处理例程所处的地址上执行。

4.中断处理程序由以下指令终止:

RETI(从中断处理例程返回)

5.时钟中断源时钟的选择

在这里使用的是看门狗时钟那么相应的也要打开看门时钟的中断,但是不要错误的把看门狗设置成安全模式,这样会造成系统反复重启

6.MSP430为16位单片机,堆栈是向下生长

os_cpu_c.c文件的改写

μ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文件的改写

定义与硬件无关的数据类型,堆栈的生长方向

/*
 * 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_ */

DEMO

两个不同频率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_cfg.h文件

在我们的系统中使用系统的节拍器需要定义时钟任务的优先级
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

使用宏定义的方式决定需要开启和关闭的功能

你可能感兴趣的:(基于MSP430F5529的μc/os嵌入式实时操作系统移植)