UCOS-ii在ARM处理器上的移植

本文是在原有的STM32单片机的无操作系统的程序基础上实现的ucos-ii 的移植
从micrium网站上下载官网移植版本,解压后得到如下文件夹:

Micrium_STM32_OS2\
            AppNotes
            Licensing
            Software

AppNotes文件夹中包含移植文件说明.
Licensing文件夹中包含使用许可证.
Software文件夹包含ucos-ii源码,我们的移植文件就包含在这个文件夹中.

    Software\
    CPU: stm32的标准外设库
    EvalBoards:micrium官方评估板相关代码
    uc-CPU: 基于micrium官方评估板的ucos-ii移植代码
    uc-LIB: micrium官方的一个库代码
    uCOS-II:ucos-ii源代码
    uC-Probe: 和uC-Probe相关代码

Directory Tree

在原有”裸机”程序结构下,只需要新建两个文件夹
~\UCOSII-SRC
~\UCOSII-PORT

将Micrium_STM32_OS2\Software\uCOS-II\Source中的文件复制到~\UCOSII-SRC文件夹中.
将Micrium_STM32_OS2\Software\uCOS-II\Ports\arm-cortex-m3\Generic\IAR中的文件复制到~\UCOSII-PORT文件夹中.

~\UCOSII-SRC 文件夹中的代码是ucosii中无需修改部分

\SRC
    os_core.c
    os_flag.c
    os_mbox.c
    os_mem.c
    os_mutex.c
    os_q.c
    os_sem.c
    os_task.c
    os_time.c
    os_tmr.c
    ucos_ii.h

~\UCOSII-PORT 文件夹中的代码是移植时需要修改的

 \PORT
    os_cpu.h
    os_cpu_a.asm
    os_cpu_c.c
    oc_dbg.c(用于统计任务,可不移植)

ucos-ii

os_cpu.h文件
该头文件主要定义了数据类型,处理器堆栈数据类型字长,堆栈增长方向,任务切换宏和临界区访问处理

1.全局变量
下列代码设置是否使用全局变量

#ifdef  OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT  extern
#endif

2.数据类型
ucos-ii为了保证可移植性,程序中没有直接使用int,unsigned int等定义 ,而是自己定义了一套数据类型例如INT16U,INT32U等

typedef unsigned char  BOOLEAN;
typedef unsigned char  INT8U;           /* Unsigned  8 bit quantity       */
typedef signed   char  INT8S;           /* Signed    8 bit quantity       */
typedef unsigned short INT16U;          /* Unsigned 16 bit quantity       */
typedef signed   short INT16S;          /* Signed   16 bit quantity       */
typedef unsigned int   INT32U;          /* Unsigned 32 bit quantity       */
typedef signed   int   INT32S;          /* Signed   32 bit quantity       */
typedef float          FP32;            /* Single precision floating point*/
typedef double         FP64;            /* Double precision floating point*/

//STM32是32位位宽的,这里OS_STK和OS_CPU_SR都应该为32位数据类型
typedef unsigned int   OS_STK;          /* Each stack entry is 32-bit wide*/
typedef unsigned int   OS_CPU_SR;       /* Define size of CPU status register*/

3.临界段
指的是一个小代码段,同一时刻只允许一个线程存取资源或代码区

#define  OS_CRITICAL_METHOD   3     //进入临界段的方法

#if OS_CRITICAL_METHOD == 3
#define  OS_ENTER_CRITICAL()  {cpu_sr = OS_CPU_SR_Save();}
#define  OS_EXIT_CRITICAL()   {OS_CPU_SR_Restore(cpu_sr);}
#endif

4.栈增长方向
Cortex-M相关处理器使用的是“向下生长的满栈”模式,即栈生长方向是由高地址向低地址增长的。

//定义栈的增长方向.
//CM3中,栈是由高地址向低地址增长的,所以OS_STK_GROWTH设置为1
#define  OS_STK_GROWTH        1      /* Stack grows from HIGH to LOW memory on ARM    */

5.任务切换宏
定义任务切换宏,OSCtxSw()是用汇编代码写的,代码在os_cpu_a.asm中

#define OS_TASK_SW() OSCtxSw()

6.函数原型
如果定义了进入临界段的模式为3,就声明开中断和关中断函数

#if OS_CRITICAL_METHOD == 3u      /* See OS_CPU_A.ASM  */
OS_CPU_SR  OS_CPU_SR_Save(void);
void       OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif

一些任务管理函数

void       OSCtxSw(void);//用户任务切换函数
void       OSIntCtxSw(void);//中断任务切换函数 
void       OSStartHighRdy(void);//在操作系统第一次启动时调用的任务切换
void       OSPendSV(void);//用户中断处理函数,目前版本为OSPendSV

os_cpu_c.c文件
ucosii移植时需要我们写10个相当简单的C函数:9个钩子函数和1个任务堆栈结构初始化函数。
void OSInitHookBegin(void) //系统初始化函数开头的钩子函数
void OSInitHookEnd(void) //系统初始化函数结尾的钩子函数
void OSTaskCreateHook(OS_TCB * ptcb) //创建任务钩子函数
void OSTaskDelHook(OS_TCB * ptcb) //删除任务钩子函数
void OSTaskIdleHook(void) //空闲任务钩子函数
void OSTaskStatHook(void) //统计任务钩子函数
OS_STK OSTaskStkInit(void ( * task)(void * p_arg),void * p_arg,OS_STK * ptos,INT16U opt) //任务堆栈结构初始化函数
void OSTaskSwHook(void) //任务切换钩子函数
void OSTCBInitHook(OS_TCB * ptcb) //任务控制块初始化钩子函数
void OSTimeTickHook(void) //时钟节拍钩子函数

这些函数除了OSTaskStkInit(),都是一些hook函数。这些hook函数如果不使能的话,都不会用上。OSTaskStkInit()的详细讲解查看相应的资料。

os_cpu_a.asm文件
主要包含五个汇编函数和一个异常处理函数,这些函数由于需要保存/恢复寄存器,所以 只能由汇编语言编写,不可由C语言实现
1 OS_CPU_SR_Save( )函数
该函数执行中断屏蔽寄存器保存,然后禁止中断执行临界段模式3,该功能由OS_ENTER_CRITICAL()宏定义调用。

/*OS_ENTER_CRITICAL()里进入临界段调用,保存现场环境*/
OS_CPU_SR_Save
    MRS     R0, PRIMASK     ;读取PRIMASK到R0,R0为返回值 
    CPSID   I               ;PRIMASK=1,关中断(NMI和硬件FAULT可以响应)
    BX      LR              ;返回

2 OS_CPU_SR_Restore( )函数
该函数由OS_EXIT_CRITICAL()宏定义调用,用于恢复现场

/*OS_ENTER_CRITICAL()里退出临界段调用,用于恢复现场*/
OS_CPU_SR_Restore
    MSR     PRIMASK, R0     ;读取R0到PRIMASK中,R0为参数
    BX      LR              ;返回

3 OSStartHighRdy( )函数
启动最高优先级任务,由OSStart()函数调用,调用前必须先调用OSTaskCreate()创建至少一个任务,否则系统会崩溃

OSStartHighRdy
        LDR     R4, =NVIC_SYSPRI2      ; set the PendSV exception priority
        LDR     R5, =NVIC_PENDSV_PRI
        STR     R5, [R4]

        MOV     R4, #0                 ; set the PSP to 0 for initial context switch call
        MSR     PSP, R4

        LDR     R4, =OSRunning         ; OSRunning = TRUE
        MOV     R5, #1
        STRB    R5, [R4]

                                       ;切换到最高优先级的任务
        LDR     R4, =NVIC_INT_CTRL     ;rigger the PendSV exception (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]

        CPSIE   I                      ;enable interrupts at processor level

4 OSCtxSw( )函数
当任务放弃CPU 使用权时,就会调用OS_TASK_SW( )函数,但在Cortex-M3中,任务切换的工作都放在PendSV的中断处理程序中,以加快处理速度,因此OS_TASK_SW()只需要简单的悬挂(允许)PendSV中断即可。OS_TASK_SW()函数是由OS_Sched()函数(在OS_CORE.C中)调用

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0;
#endif
    OS_ENTER_CRITICAL();              //保存全局中断标志,关中断
    if (OSIntNesting == 0) {         /* 如果没中断服务运行      */
        if (OSLockNesting == 0) {            /* 调度器未上锁   */
            OS_SchedNew(); /*计算就绪任务里优先级最高的任务,结果保存在OSPrioHighRdy*/
            if (OSPrioHighRdy != OSPrioCur) {   /*如果得到的最高优先级就绪任务不等于当前,注:当前运行的任务也在就绪表里*/
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//得到任务控制块指针
#if OS_TASK_PROFILE_EN > 0
                OSTCBHighRdy->OSTCBCtxSwCtr++;   /* 统计任务切换到次任务的计数器加1 */
#endif
                OSCtxSwCtr++;  /* 统计任务切换次数的计数器加1*/
                OS_TASK_SW(); /*悬起PSV(即PendSV)异常,进行任务切换*/
            }
        }
    }
    OS_EXIT_CRITICAL(); //恢复全局中断标志,退出临界段,开中断执行PSV服务
}

#define  OS_TASK_SW()  OSCtxSw()

OSCtxSw
        PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL      ;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
        POP     {R4, R5}
        BX      LR

5 OSIntCtxSw( )函数
中断级任务切换函数OSIntCtxSw( )与OSCtxSw( )类似。若任务运行过程中产生了中断,且中断服务例程使得一个比当前被中断任务的优先级更高的任务就绪时,ucos-ii内核就会在中断返回之前调用函数OSIntCtxSw()将自己从中断态调度到就绪态,另外一个优先级更高的就绪任务切换运行态。
OSCtxSw()函数做的是任务之前的切换,例如任务因为等待某个资源或做延时,就会调用这个函数来进行任务调度,任务调度进行任务切换;OSIntCtxSw( )函数则是中断退出时,如果优先级就绪任务并不是被中断的任务就会被调用,由中断状态切换到最高优先级就绪任务中,所以OSIntCtxSw( )又称中断级的中断任务。

OSIntCtxSw
        PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL      ;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
        POP     {R4, R5}
        BX      LR
        NOP

6 OSPendSV( )函数
函数OSPendSV()是Cortex-M3处理器进入异常服务例程时,通过一次PendSV异常中断完成任务上下文切换时的用户线程模式到特权模式的转换,自动压栈了R0~R3,R12,LR(连接寄存器R14),PSR(程序状态寄存器)和PC(R15),并且在返回时自动弹出。

PendSV_Handler
    CPSID   I                                                   ; Prevent interruption during context switch
    MRS     R0, PSP                                             ; PSP is process stack pointer 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注
    CBZ     R0, PendSV_Handler_Nosave                           ; Skip register save the first time

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

                                                                ; At this point, entire context of process has been saved
PendSV_Handler_Nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR       

其他需要移植的文件:
oc_cfg.h文件
头文件oc_cfg.h是系统功能配置文件。ucos-ii是依据编译时的条件编译来实现软件系统的裁剪性的。根据所需要的功能对系统进行裁剪。

#define OS_MAX_EVENTS 2 /* 应用中最多事件控制块的数目... */
 /* ... 必须大于 0 */
#define OS_MAX_FLAGS 5 /* 应用中最多事件标志组的数目... */
 /* ... 必须大于 0 */
#define OS_MAX_MEM_PART 5 /* 最多内存块的数目... */
 /* ... 必须大于 0 */
#define OS_MAX_QS 2 /* 应用中最多对列控制块的数目... */
 /* ... 必须大于 0 */
#define OS_MAX_TASKS 11 /* 应用中最多任务数目... */
 /* ... 必须大于等于2 */

#define OS_LOWEST_PRIO 12 /* 定义任务的最低优先级... */
/* ... 不得大于 63 ! */
#define OS_TASK_IDLE_STK_SIZE 512 /* 统计任务堆栈容量( # 按照OS_STK的宽度数目) */
#define OS_TASK_STAT_EN 1 /* 允许 (1) 或者禁止 (0) 统计任务 */
#define OS_TASK_STAT_STK_SIZE 512 /* 空闲任务堆栈容量 (#按照OS_STK的宽度数目) */
#define OS_ARG_CHK_EN 1 /* 允许 (1) 或者禁止 (0) 变量检查 */
#define OS_CPU_HOOKS_EN 1 /* 在处理器移植文件中允许使用 uC/OS-II 的接口函数 */
/* -------------------事件标志管理 ------------------- */
#define OS_FLAG_EN 1 /* 允许 (1) 或者禁止 (0) 产生事件标志相关代码 */
#define OS_FLAG_WAIT_CLR_EN 1 /* 允许生成 Wait on Clear 事件标志代码 */
#define OS_FLAG_ACCEPT_EN 1 /* 允许生成 OSFlagAccept() */
#define OS_FLAG_DEL_EN 1 /* 允许生成 OSFlagDel() */
#define OS_FLAG_QUERY_EN 1 /* 允许生成 OSFlagQuery() */

/* -------------------消息邮箱管理 -------- ---------- */
#define OS_MBOX_EN 1 /* 允许 (1) 或者禁止 (0) 产生消息邮箱相关代码 */
#define OS_MBOX_ACCEPT_EN 1 /* 允许生成 OSMboxAccept() */
#define OS_MBOX_DEL_EN 1 /* 允许生成 OSMboxDel() */
#define OS_MBOX_POST_EN 1 /* 允许生成 OSMboxPost() */
#define OS_MBOX_POST_OPT_EN 1 /* 允许生成 OSMboxPostOpt() */
#define OS_MBOX_QUERY_EN 1 /* 允许生成 OSMboxQuery() */

#define OS_MEM_EN 1 /* 允许 (1) 或者禁止 (0) 产生内存相关代码 */
#define OS_MEM_QUERY_EN 1 /* 允许生成 OSMemQuery() */
/* ---------------- 互斥型信号量管理 ----------------- */
#define OS_MUTEX_EN 1 /* 允许 (1) 或者禁止 (0) 产生互斥型信号量相关代码 */
#define OS_MUTEX_ACCEPT_EN 1 /* 允许生成 OSMutexAccept() */
#define OS_MUTEX_DEL_EN 1 /* 允许生成 OSMutexDel() */
#define OS_MUTEX_QUERY_EN 1 /* 允许生成 OSMutexQuery() */
/* -------------------消息队列号管理 ----------------- */
#define OS_Q_EN 1 /* 允许 (1) 或者禁止 (0) 产生消息队列相关代码 */
#define OS_Q_ACCEPT_EN 1 /* 允许生成 OSQAccept() */
#define OS_Q_DEL_EN 1 /* 允许生成 OSQDel() */
#define OS_Q_FLUSH_EN 1 /* 允许生成 OSQFlush() */
#define OS_Q_POST_EN 1 /* 允许生成 OSQPost() */
#define OS_Q_POST_FRONT_EN 1 /* 允许生成 OSQPostFront() */
#define OS_Q_POST_OPT_EN 1 /* 允许生成 OSQPostOpt() */
#define OS_Q_QUERY_EN 1 /* 允许生成 OSQQuery() */
/* ---------------------信号管理 --------------------- */
#define OS_SEM_EN 1 /* 允许 (1) 或者禁止 (0) 产生信号量相关代码 */
#define OS_SEM_ACCEPT_EN 1 /* 允许生成 OSSemAccept() */
#define OS_SEM_DEL_EN 1 /* 允许生成 OSSemDel() */
#define OS_SEM_QUERY_EN 1 /* 允许生成 OSSemQuery() */
/* -------------------任务管理 ----------------------- */
#define OS_TASK_CHANGE_PRIO_EN 1 /* 允许生成 OSTaskChangePrio() 函数代码 */
#define OS_TASK_CREATE_EN 1 /* 允许生成 OSTaskCreate() 函数代码 */
#define OS_TASK_CREATE_EXT_EN 1 /* 允许生成 OSTaskCreateExt() 函数代码 */
#define OS_TASK_DEL_EN 1 /* 允许生成 OSTaskDel() 函数代码 */
#define OS_TASK_SUSPEND_EN 1 /* 允许生成 OSTaskSuspend() and OSTaskResume() 函数代码*/
#define OS_TASK_QUERY_EN 1 /* 允许生成 OSTaskQuery() 函数代码 */

/* ----------------------时间管理 -------------------- */
#define OS_TIME_DLY_HMSM_EN 1 /* 允许生成OSTimeDlyHMSM() 函数代码 */
#define OS_TIME_DLY_RESUME_EN 1 /* 允许生成OSTimeDlyResume() 函数代码 */
#define OS_TIME_GET_SET_EN 1 /* 允许生成 OSTimeGet() 和 OSTimeSet() 函数代码 */
/* -------------------- 混合管理 --------------------- */
#define OS_SCHED_LOCK_EN 1 /* 允许生成 OSSchedLock() 和 OSSchedUnlock() 代码 */
#define OS_TICKS_PER_SEC 200 /* 设置每秒的节拍数目 */
typedef INT16U OS_FLAGS; /* 事件标志的数据类型 (8位, 16位 或 32 位) */

includes.h文件
该头文件是应用程序相关的文件,ucos-ii的主头文件,在每个*.C文件中都要包含这个文件。

/*标准功能头文件*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
/*系统相关*/
#include "ucos_ii.h"
#include "os_cpu.h"
#include "os_cfg.h"
/*与应用程序相关*/                  
#include <stm32f10x.h>   

把上述的代码添加到原“裸机”程序中后,并对PORT文件夹中的文件作相应的修改后,即可将ucos-ii移植到STM32单片机中。
ucos-ii

为了验证移植是否成功,在移植后的系统上创建几个个简单任务,看看任务切换是否成功来验证移植是否ok,因为任务切换可以说是ucosii最核心的功能。

//串口1通信任务
static void Task_Com1(void* p_arg)
{
    INT8U err;
    unsigned char * msg;
    (void)p_arg;      
    while(1)
    {
        msg=(unsigned char *)OSMboxPend(Com1_MBOX,0,&err);//等待串口接收指令成功的邮箱信息
        if(msg[0]=='L'&&msg[1]==0x31)
        {
            milsec1=atoi(&msg[3]);  //LED1 的延时毫秒
        }
        else if(msg[0]=='L'&&msg[1]==0x32)
        {
            milsec2=atoi(&msg[3]);  //LED2 的延时毫秒
        }
        memset(USART_RX_BUF,'\0',sizeof(USART_RX_BUF));
    }
}

//LED1闪烁任务  
static void Task_Led1(void* p_arg)     
{
    (void) p_arg;
    while(1)
    {
        LED_LED0_ON();
        OSTimeDlyHMSM(0, 0, 0, milsec1);

        LED_LED0_OFF();
        OSTimeDlyHMSM(0, 0, 0, milsec1);    
    }
}

//LED2闪烁任务
static void Task_Led2(void* p_arg)
{
    (void) p_arg;       
   while (1)
   {
      LED_LED1_ON();
      OSTimeDlyHMSM(0, 0, 0, milsec2);

      LED_LED1_OFF();
      OSTimeDlyHMSM(0, 0, 0, milsec2);  
   }
}   

在ucos-ii系统中建立任务,驱动2个LED,通过电脑端发送 的串口指令来控制2个LED延时闪烁间隔。
最后将程序编译调试后,将程序烧到板子中,可以看到2个LED灯是以电脑发送的时间间隔闪烁。可以任务移植初步成功了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值