RTOS之UCOS(七)---UCOS系统移植


经过前一篇文章 固件库的移植,我们的处理器就能完成从上电复位到进入main函数的过程,在main函数中初始化我们要用到的外设,并完成外设的中断处理函数。如果我们需要处理器完成的任务比较单一,可以不使用操作系统,比如BLE协议栈应用开发就是用了状态机模型,但如果需要处理器完成的任务比较多,为了便于管理可以引入轻巧的实时操作系统,下面以UCOS-II为例介绍下实时操作系统的移植。

1. UCOS的底层支持要素

前面有一篇文章介绍任务调度时谈到任务调度的核心是通过触发PendSV中断,在其中断处理函数中执行任务切换工作,所以PendSV应该算系统移植的一个重点。任务周期性切换的驱动源是SYSTICK系统滴答时钟,这个号称操作系统心跳的滴答定时器SYSTICK的配置和中断处理同样应该是系统移植的一个重点。对照前面提到的系统启动三要素:时钟系统、中断系统、存储系统,SYSTICK与PendSV应该算时钟与中断部分的底层支持要素。

操作系统一般涉及到多任务间的同步,虽然有互斥量与信号量这类偏上层的事件控制块保证任务间同步,也有临界区这种偏底层的工具保证任务内的原子操作,既然是偏底层的,临界区应该也是移植的一个关注点,而且临界区涉及到开关中断,也属于时钟与中断部分的底层支持要素。

接下来看看存储方面有哪些跟处理器直接相关的要素,我们学习单片机或编程语言时应该了解过相同的数据类型在不同的处理器平台或编译器上可能会表现出不同的位宽,例如整型int可能在16位单片机上占16位宽度也即2字节而在32位单片机上占32位宽度也即4字节,所以根据实际的处理器型号重新定义每种数据类型的位宽算是操作系统移植的一个重点。对堆栈的操作一般涉及寄存器的功能配置(比如xPSR状态寄存器),所以堆栈初始化、堆栈生长方向等也应该是操作系统移植的一个重点。数据类型位宽重定义与堆栈初始化算是存储部分的底层支持要素。

2. UCOSⅡ的移植

先下载UCOS源码,UCOS开发者Micrium除了提供单纯的UCOS源码,也针对很多平台提供了适配或移植后的源码,本文基于STM32F10X硬件平台,针对该平台适配后的源码下载网址如下:
https://www.micrium.com/downloadcenter/download-results/?searchterm=mi-stm32f107&supported=true
UCOSII STM32F107下载界面
下载Micrium_uC-Eval-STM32F107_uCOS-II系统源码,解压后的文件结构如下:
UCOSⅡ源码目录结构

  • EvalBoards:支持评估(开发)板STM32F107的相关文件(可以理解为工程模板示例文件),包含了BSP与UCOSⅡ两个文件夹,BSP目录保存底层板级启动相关的文件,这里的启动文件较旧,我们使用前面介绍的更新的ST V3.5版固件库文件作为启动文件;UCOSⅡ目录保存系统配置文件,我们移植时会借用部分配置文件;
  • uC-CPU:跟CPU架构相关的文件,我们依然使用ST标准外设固件库提供相关的功能,不使用该文件夹下的文件;
  • uC-LIB:这个是Micrium官方的库,可以部分替代C-LIB,KEIL MDK还提供了Micro-LIB微库,我们可以选择使用C-LIB或Micro-LIB,当然也可以选择这个uC-LIB,如果不想使用uC-LIB,可以忽略这个文件夹;
  • uCOS-II:这个文件夹是我们移植的关键,其中Source保存了UCOSⅡ的源码,移植时不需要更改;Port保存了与处理器平台相关的接口文件,是移植时的重点,需要根据处理器型号做出部分更改。

下面用一个简图展示下不同目录模块在移植过程中的关系:
UCOSⅡ源码目录关系
其中BSP由上一节介绍的STM32F10x_StdPeriph_Lib_V3.5.0提供板级支持,如果前面ST固件库已移植完成,可以忽略BSP这部分。然后就是UCOSⅡ Port与Source目录下文件的移植。最后是应用层的配置文件来自…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-II目录,这个等下层移植完成后再介绍。下面先看下UCOSⅡ目录下的文件结构:
UCOSⅡ目录文件结构
其中Source目录下的源码不需要任何更改,在前面的文章中也介绍过,下面再简单总结下该目录下各文件功能:

文件名 功能描述
os_core.c 内核数据结构管理,ucos-ii的核心,涵盖内核的初始化、任务切换、事件块管理、事件标志组管理等功能
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 定义了全局变量、内核数据结构。函数声明等,也是ucos-ii的核心

下面重点看下Port目录下各文件的功能:

文件名 功能描述
oc_cpu_a.asm 与处理器相关的汇编代码,主要是与任务切换相关,比如状态寄存器保存恢复、SYSTICK与PendSV配置与中断处理
os_cpu_c.c 定义用户钩子函数,提供扩充软件功能的的接口,任务堆栈初始化函数也在这里定义
os_cpu.h 定义数据类型、处理器相关代码、声明函数原型
os_dbg.c 内核调试相关数据和相关函数

先看os_cpu.h,里面数据类型位宽及堆栈生长方向已经针对STM32F107做了调整,所以不需要变更,代码如下:

// uCOS-II\Ports\os_cpu.h
/*
*********************************************************************************************************
*                                              DATA TYPES
*                                         (Compiler Specific)
*********************************************************************************************************
*/

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

typedef unsigned int   OS_STK;                   /* Each stack entry is 32-bit wide                    */
typedef unsigned int   OS_CPU_SR;                /* Define size of CPU status register (PSR = 32 bits) */

#define  OS_STK_GROWTH        1u                  /* Stack grows from HIGH to LOW memory on ARM        */

涉及到的函数如下:

// uCOS-II\Ports\os_cpu.h
/*
*********************************************************************************************************
*                                              PROTOTYPES
*********************************************************************************************************
*/

#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       OS_CPU_PendSVHandler(void);

                                                  /* See OS_CPU_C.C                                    */
void       OS_CPU_SysTickHandler(void);
void       OS_CPU_SysTickInit(INT32U  cnts);
#endif

上面的函数中,涉及到底层SYSTICK与PendSV的函数需要调整,由于底层BSP部分不使用UCOS代码直接使用STM32F10x固件库的代码,所以二者需要做好匹配。在STM32F10x固件库的中断向量表(见startup_stm32f10x_hd.s文件)中SysTick与PendSV的异常处理函数名分别为SysTick_Handler与PendSV_Handler,跟UCOS源码中的不一致,这里把startup_stm32f10x_hd.s文件中的SysTick_Handler与PendSV_Handler分别全部替换为UCOS源码中(见os_cpu.h文件)的函数名OS_CPU_SysTickHandler与OS_CPU_PendSVHandler(当然也可以反过来把UCOS源码中的处理函数名全部替换为ST固件库中的处理函数名),SysTick与PendSV异常触发后就可以通过中断向量表调用真正的异常处理函数了。既然这里选择了修改ST固件库中断向量表中的文件名,UCOS中的oc_cpu_a.asm文件(里面的代码在前篇任务调度器中介绍过)就不需要变更了。

下面再看下os_cpu_c.c中的代码,主要是一些钩子函数定义,下面只展示部分重要的代码(堆栈初始化、SYSTICK初始化及异常处理函数):

// uCOS-II\Ports\os_cpu_c.c
/*
*********************************************************************************************************
*                                          SYS TICK DEFINES
*********************************************************************************************************
*/

#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010uL)) /* SysTick Ctrl & Status Reg. */
#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U *)0xE000E014uL)) /* SysTick Reload  Value Reg. */
#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018uL)) /* SysTick Current Value Reg. */
#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01CuL)) /* SysTick Cal     Value Reg. */
#define  OS_CPU_CM3_NVIC_PRIO_ST    (*((volatile INT8U  *)0xE000ED23uL)) /* SysTick Handler Prio  Reg. */

#define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT                    0x00010000uL   /* Count flag.                */
#define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC                  0x00000004uL   /* Clock Source.              */
#define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN                    0x00000002uL   /* Interrupt enable.          */
#define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE                   0x00000001uL   /* Counter mode.              */
#define  OS_CPU_CM3_NVIC_PRIO_MIN                               0xFFu    /* Min handler prio.          */

/*
*********************************************************************************************************
*                                          SYS TICK HANDLER
*
* Description: Handle the system tick (SysTick) interrupt, which is used to generate the uC/OS-II tick
*              interrupt.
*
* Arguments  : none.
*
* Note(s)    : 1) This function MUST be placed on entry 15 of the Cortex-M3 vector table.
*********************************************************************************************************
*/

void  OS_CPU_SysTickHandler (void)
{
   
    OS_CPU_SR  cpu_sr;


    OS_ENTER_CRITICAL();                         /* Tell uC/OS-II that we are starting an ISR          */
    OSIntNesting++;
    OS_EXIT_CRITICAL();

    OSTimeTick();                                /* Call uC/OS-II's OSTimeTick()                       */

    OSIntExit();                                 /* Tell uC/OS-II that we are leaving the ISR          */
}

/*
*********************************************************************************************************
*                                          INITIALIZE SYS TICK
*
* Description: Initialize the SysTick.
*
* Arguments  : cnts          is the number of SysTick counts between two OS tick interrupts.
*
* Note(s)    : 1) This function MUST be called after OSStart() & after processor initialization.
*********************************************************************************************************
*/

void  OS_CPU_SysTickInit (INT32U  cnts)
{
   
    OS_CPU_CM3_NVIC_ST_RELOAD = cnts - 1u;
                                                 /* Set prio of SysTick handler to min prio.           */
    OS_CPU_CM3_NVIC_PRIO_ST   = OS_CPU_CM3_NVIC_PRIO_MIN;
                                                 /* Enable timer.                                      */
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM3_NVIC_ST_CTRL_ENABLE;
                                                 /* Enable timer interrupt.                            */
    OS_CPU_CM3_NVIC_ST_CTRL  |= OS_CPU_CM3_NVIC_ST_CTRL_INTEN;
}

/*
*********************************************************************************************************
*                                        INITIALIZE A TASK'S STACK
*
* Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the
*              stack frame of the task being created.  This function is highly processor specific.
*
* Arguments  : task          is a pointer to the task code
*
*              p_arg         is a pointer to a user supplied data area that will be passed to the task
*                            when the task first executes.
*
*              ptos          is a pointer to the top of stack.  It is assumed that 'ptos' points to
*                            a 'free' entry on the task stack.  If OS_STK_GROWTH is set to 1 then
*                  
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流云IoT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值