前面有一篇文章介绍任务调度时谈到任务调度的核心是通过触发PendSV中断,在其中断处理函数中执行任务切换工作,所以PendSV应该算系统移植的一个重点。任务周期性切换的驱动源是SYSTICK系统滴答时钟,这个号称操作系统心跳的滴答定时器SYSTICK的配置和中断处理同样应该是系统移植的一个重点。对照前面提到的系统启动三要素:时钟系统、中断系统、存储系统,SYSTICK与PendSV应该算时钟与中断部分的底层支持要素。
操作系统一般涉及到多任务间的同步,虽然有互斥量与信号量这类偏上层的事件控制块保证任务间同步,也有临界区这种偏底层的工具保证任务内的原子操作,既然是偏底层的,临界区应该也是移植的一个关注点,而且临界区涉及到开关中断,也属于时钟与中断部分的底层支持要素。
接下来看看存储方面有哪些跟处理器直接相关的要素,我们学习单片机或编程语言时应该了解过相同的数据类型在不同的处理器平台或编译器上可能会表现出不同的位宽,例如整型int可能在16位单片机上占16位宽度也即2字节而在32位单片机上占32位宽度也即4字节,所以根据实际的处理器型号重新定义每种数据类型的位宽算是操作系统移植的一个重点。对堆栈的操作一般涉及寄存器的功能配置(比如xPSR状态寄存器),所以堆栈初始化、堆栈生长方向等也应该是操作系统移植的一个重点。数据类型位宽重定义与堆栈初始化算是存储部分的底层支持要素。
先下载UCOS源码,UCOS开发者Micrium除了提供单纯的UCOS源码,也针对很多平台提供了适配或移植后的源码,本文基于STM32F10X硬件平台,针对该平台适配后的源码下载网址如下:
https://www.micrium.com/downloadcenter/download-results/?searchterm=mi-stm32f107&supported=true
下载Micrium_uC-Eval-STM32F107_uCOS-II系统源码,解压后的文件结构如下:
下面用一个简图展示下不同目录模块在移植过程中的关系:
其中BSP由上一节介绍的STM32F10x_StdPeriph_Lib_V3.5.0提供板级支持,如果前面ST固件库已移植完成,可以忽略BSP这部分。然后就是UCOSⅡ Port与Source目录下文件的移植。最后是应用层的配置文件来自…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-II目录,这个等下层移植完成后再介绍。下面先看下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
* 'ptos' will contain the HIGHEST valid address of the stack. Similarly, if
* OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address
* of the stack.
*
* opt specifies options that can be used to alter the behavior of OSTaskStkInit().
* (see uCOS_II.H for OS_TASK_OPT_xxx).
*
* Returns : Always returns the location of the new top-of-stack once the processor registers have
* been placed on the stack in the proper order.
*
* Note(s) : 1) Interrupts are enabled when your task starts executing.
* 2) All tasks run in Thread mode, using process stack.
*********************************************************************************************************
*/
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000uL; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
*(--stk) = (INT32U)OS_TaskReturn; /* R14 (LR) */
*(--stk) = (INT32U)0x12121212uL; /* R12 */
*(--stk) = (INT32U)0x03030303uL; /* R3 */
*(--stk) = (INT32U)0x02020202uL; /* R2 */
*(--stk) = (INT32U)0x01010101uL; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111uL; /* R11 */
*(--stk) = (INT32U)0x10101010uL; /* R10 */
*(--stk) = (INT32U)0x09090909uL; /* R9 */
*(--stk) = (INT32U)0x08080808uL; /* R8 */
*(--stk) = (INT32U)0x07070707uL; /* R7 */
*(--stk) = (INT32U)0x06060606uL; /* R6 */
*(--stk) = (INT32U)0x05050505uL; /* R5 */
*(--stk) = (INT32U)0x04040404uL; /* R4 */
return (stk);
}
上面的代码也不需要修改,其中初始化堆栈函数OSTaskStkInit中对寄存器R1-R12所赋的值没什么意义,取上面的值主要是为了方便调试,但xPSR第24位(即T位)需要置1,否则第一次执行任务时Fault。SYSTICK初始化函数用的寄存器操作,这里不依赖UCOS源码中的BSP部分,所以也不需要修改代码,但也可以使用库函数的形式实现该函数,实现代码如下:
// uCOS-II\Ports\os_cpu_c.c
void OS_CPU_SysTickInit (void)
{
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks); //获取系统时钟
SysTick_Config(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC); //初始化并使能SysTick定时器
}
OS_CPU_SysTickInit函数可以使用原代码,也可以使用上面修改后的代码,需要注意两个函数的参数不一致,如果使用UCOS原代码,在调用该函数时需要传参,比如像下面这样:
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
OS_CPU_SysTickInit(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC);
到这里移植基本结束,实际上主要修改了两个异常处理函数名,使UCOS源码中的SysTick与PendSV异常处理函数名与ST固件库中断向量表中的函数名一致。接下来再看…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-II目录下的应用层配置文件:
由于我们没有使用uC-CPU与uC-LIB,所以自然也不需要cpu_cfg.h与lib_cfg.h,在ST固件库移植时已经有一个stm32f10x_conf.h文件,这里不再使用该文件。其余文件功能介绍如下:
文件名 | 功能描述 |
---|---|
app_cfg.h | 主要定义任务优先级、任务堆栈等,由于应用不同,需要重写该文件 |
app_hooks.c | 定义一些钩子函数 |
app.c | 定义任务函数及main函数等,需要重写该文件 |
includes.h | 包含头文件,禁用LIBRARIES与APP / BSP部分,将ST包含头文件替换为stm32f10x.h |
os_cfg.h | 条件编译选项,可以通过关闭某些编译选项裁剪内核 |
app_hooks.c与os_cfg.h可以不进行任何修改,includes.h则禁用LIBRARIES与APP / BSP部分的头文件且替换ST部分的头文件,剩下的就是重写app.c与app_cfg.h的代码了。我们继续使用上面验证ST固件库时的LED示例,创建两个任务分别用于管理LED0与LED1的亮灭,使用一个信号量事件来控制两个任务间的同步,示例代码如下:
// uCOS-II\Config\app_cfg.h
#ifndef __APP_CFG_H__
#define __APP_CFG_H__
/*
*********************************************************************************************************
* TASK PRIORITIES
*********************************************************************************************************
*/
#define APP_TASK_START_PRIO 4
#define OS_TASK_TMR_PRIO (OS_LOWEST_PRIO - 2)
#define LED0_TASK_PRIO 7
#define LED1_TASK_PRIO 6
/*
*********************************************************************************************************
* TASK STACK SIZES
* Size of the task stacks (# of OS_STK entries)
*********************************************************************************************************
*/
#define APP_TASK_START_STK_SIZE 128
#define APP_CFG_TASK_LED_STK_SIZE 128
#define BUFF_SIZE 1000
#define LED0_STK_SIZE 64
#define LED1_STK_SIZE 64
// uCOS-II\Config\app.c
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include
#include
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static OS_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
OS_EVENT *led_sem;
OS_STK LED0_TASK_STK[LED0_STK_SIZE];
OS_STK LED1_TASK_STK[LED1_STK_SIZE];
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
void LED_Init(void);
static void AppTaskCreate (void);
static void AppEventCreate (void);
static void AppTaskStart (void *p_arg);
void led0_task(void *pdata);
void led1_task(void *pdata);
/*
*********************************************************************************************************
* main()
*
* Description : This is the standard entry point for C code. It is assumed that your code will call
* main() once you have performed all necessary initialization.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
int main (void)
{
LED_Init();
OSInit(); /* Initialize "uC/OS-II, The Real-Time Kernel" */
OSTaskCreate(AppTaskStart,(void *)0,(OS_STK *)&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],APP_TASK_START_PRIO ); /* Create the start task */
OSStart(); /* Start multitasking (i.e. give control to uC/OS-II) */
}
/*
*********************************************************************************************************
* STARTUP TASK
*
* Description : This is an example of a startup task. As mentioned in the book's text, you MUST
* initialize the ticker only once multitasking has started.
*
* Arguments : p_arg is the argument passed to 'AppTaskStart()' by 'OSTaskCreate()'.
*
* Returns : none
*
* Notes : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
* used. The compiler should not generate any code for this statement.
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg)
{
OS_CPU_SR cpu_sr=0;
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
OS_CPU_SysTickInit(rcc_clocks.HCLK_Frequency/OS_TICKS_PER_SEC);
OS_ENTER_CRITICAL();
#if (OS_TASK_STAT_EN > 0)
OSStatInit(); /* Determine CPU capacity */
#endif
AppEventCreate(); /* Create Application Kernel objects */
AppTaskCreate(); /* Create application tasks */
OSTaskSuspend(APP_TASK_START_PRIO); /* Suspend the start task */
OS_EXIT_CRITICAL();
}
/*
*********************************************************************************************************
* CREATE APPLICATION TASKS
*
* Description: This function creates the application tasks.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
OSTaskCreate(led0_task,(void *)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO);
OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);
}
/*
*********************************************************************************************************
* AppEventCreate()
*
* Description : Create application kernel objects tasks.
*
* Argument(s) : none
*
* Return(s) : none
*
* Caller(s) : AppTaskStart()
*
* Note(s) : none.
*********************************************************************************************************
*/
static void AppEventCreate(void)
{
led_sem = OSSemCreate(0);
}
/*
*********************************************************************************************************
* FUNCTION DEFINITION
*********************************************************************************************************
*/
void led0_task(void *pdata)
{
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
OSTimeDly(1000);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
OSTimeDly(1000);
OSSemPost(led_sem);
};
}
void led1_task(void *pdata)
{
INT8U err;
while(1)
{
OSSemPend(led_sem,0,&err);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
OSTimeDly(500);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
OSTimeDly(500);
};
}
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}
到这里就完成了UCOSⅡ的移植,同时也完成了测试例程的编写,编译成功无报错,下面给出PB.5与PE.5两个LED灯电平变化的仿真结果:
从仿真结果看,两个任务工作正常,且信号量事件也起作用了,烧录到开发板中LED灯的亮灭效果跟预期一致,到这里UCOSⅡ的移植经验证没有问题。移植后的代码:https://github.com/StreamAI/UCOS_STM32
UCOSⅢ的移植比UCOSⅡ复杂些,主要是由于UCOSⅢ Source代码跟底层关系相对密切,移植UCOSⅢ需要将uC-CPU、uC-LIB一起移植,对比下UCOSⅡ与UCOSⅢ Source里面的头文件内容就可以看出来:
// uCOS-II\Config\includes.h
/*
*********************************************************************************************************
* uC/OS-II VERSION NUMBER
*********************************************************************************************************
*/
#define OS_VERSION 29207u /* Version of uC/OS-II (Vx.yy mult. by 10000) */
/*
*********************************************************************************************************
* INCLUDE HEADER FILES
*********************************************************************************************************
*/
#include
#include
#include
// uCOSIII\Source\os.h
/*
************************************************************************************************************************
* uC/OS-III VERSION NUMBER
************************************************************************************************************************
*/
#define OS_VERSION 30301u /* Version of uC/OS-III (Vx.yy.zz mult. by 10000) */
/*
************************************************************************************************************************
* INCLUDE HEADER FILES
************************************************************************************************************************
*/
#include
#include
#include
#include
#include
#include
UCOSⅢ下载网址跟UCOSⅡ基本一样,选择下载对象uC/OS-Ⅲ V3.03.01,目录结构跟UCOSⅡ也类似,不再详细解释。但UCOSⅢ支持的功能更丰富些,二者的性能对比如下:
下面给出Source系统各源文件的主要功能作用如下:
打开Ports文件夹os_cpu.h文件查看PendSV与SysTick的异常处理函数名分别为OS_CPU_PendSVHandler与OS_CPU_SysTickHandler,跟前面UCOSⅡ的一致,所以对启动文件startup_stm32f10x_hd.s的修改也跟前面移植UCOSⅡ时一致。下面为了方便起见,使用UCOSⅢ定义的OS_CPU_SysTickInit函数,不再对其进行更改。也就是到目前为止,Source、Ports、uC-CPU、uC-LIB这四个文件夹均未做任何改动。
下面开始看…\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III目录下的文件,UCOSⅢ的配置文件比UCOSⅡ也要多一些,目录如下:
上图一共10个C语言文件,除了stm32f10x_conf.h外(ST固件库已有该文件),其余的都添加到工程内,但app.c与app_cfg.h需要重写。includes.h文件修改跟前面移植UCOSⅡ时类似,禁用APP / BSP部分的头文件且替换ST部分的头文件,由于UCOSⅢ需要uC-CPU与uC-LIB的支持,所以includes.h内依然包含LIBRARIES部分的头文件。
下面依然使用验证UCOSⅡ移植结果的示例程序,但由于UCOSⅢ的API函数接口与UCOSⅡ存在较大差异,所以下面把重写后的app.c与app_cfg.h代码附上:
// uCOSIII\Config\app_cfg.h
#ifndef __APP_CFG_H__
#define __APP_CFG_H__
/*
*********************************************************************************************************
* TASK PRIORITIES
*********************************************************************************************************
*/
#define APP_TASK_START_PRIO 2
#define LED0_TASK_PRIO 7
#define LED1_TASK_PRIO 6
/*
*********************************************************************************************************
* TASK STACK SIZES
* Size of the task stacks (# of OS_STK entries)
*********************************************************************************************************
*/
#define APP_TASK_START_STK_SIZE 128
#define LED0_STK_SIZE 64
#define LED1_STK_SIZE 64
// uCOSIII\Config\app.c
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include
#include
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
OS_SEM led_sem;
/*
*********************************************************************************************************
* TCB
*********************************************************************************************************
*/
static OS_TCB AppTaskStartTCB;
static OS_TCB LED0TCB;
static OS_TCB LED1TCB;
/*
*********************************************************************************************************
* STACKS
*********************************************************************************************************
*/
static CPU_STK AppTaskStartStk[APP_TASK_START_STK_SIZE];
static CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
static CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void AppTaskCreate (void);
static void AppObjCreate (void);
static void AppTaskStart (void *p_arg);
void LED_Init(void);
void led0_task(void *pdata);
void led1_task(void *pdata);
/*
*********************************************************************************************************
* main()
*
* Description : This is the standard entry point for C code. It is assumed that your code will call
* main() once you have performed all necessary initialization.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
int main (void)
{
OS_ERR err;
LED_Init();
OSInit(&err); /* Init uC/OS-III. */
OSTaskCreate((OS_TCB *)&AppTaskStartTCB, /* Create the start task */
(CPU_CHAR *)"App Task Start",
(OS_TASK_PTR ) AppTaskStart,
(void *) 0,
(OS_PRIO ) APP_TASK_START_PRIO,
(CPU_STK *)&AppTaskStartStk[0],
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,
(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */
}
/*
*********************************************************************************************************
* STARTUP TASK
*
* Description : This is an example of a startup task. As mentioned in the book's text, you MUST
* initialize the ticker only once multitasking has started.
*
* Arguments : p_arg is the argument passed to 'AppTaskStart()' by 'OSTaskCreate()'.
*
* Returns : none
*
* Notes : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
* used. The compiler should not generate any code for this statement.
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
OS_ERR err;
(void)p_arg;
CPU_SR_ALLOC();
CPU_Init();
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
cpu_clk_freq = rcc_clocks.HCLK_Frequency; /* Determine SysTick reference freq. */
cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; /* Determine nbr SysTick increments */
OS_CPU_SysTickInit(cnts); /* Init uC/OS periodic time src (SysTick). */
OS_CRITICAL_ENTER();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
//CPU_IntDisMeasMaxCurReset();
AppObjCreate(); /* Create Application Objects */
AppTaskCreate(); /* Create Application Tasks */
OSTaskSuspend((OS_TCB*)&AppTaskStartTCB,&err);
OS_CRITICAL_EXIT();
}
/*
*********************************************************************************************************
* CREATE APPLICATION TASKS
*
* Description: This function creates the application tasks.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
static void AppTaskCreate (void)
{
OS_ERR err;
OSTaskCreate((OS_TCB *)&LED0TCB, /* Create the start task */
(CPU_CHAR *)"LED0 Task",
(OS_TASK_PTR ) led0_task,
(void *) 0,
(OS_PRIO ) LED0_TASK_PRIO,
(CPU_STK *)&LED0_TASK_STK[0],
(CPU_STK_SIZE) LED0_STK_SIZE / 10,
(CPU_STK_SIZE) LED0_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSTaskCreate((OS_TCB *)&LED1TCB, /* Create the start task */
(CPU_CHAR *)"LED1 Task",
(OS_TASK_PTR ) led1_task,
(void *) 0,
(OS_PRIO ) LED1_TASK_PRIO,
(CPU_STK *)&LED1_TASK_STK[0],
(CPU_STK_SIZE) LED1_STK_SIZE / 10,
(CPU_STK_SIZE) LED1_STK_SIZE,
(OS_MSG_QTY ) 5u,
(OS_TICK ) 0u,
(void *) 0,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
}
/*
*********************************************************************************************************
* CREATE APPLICATION EVENTS
*
* Description: This function creates the application kernel objects.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
static void AppObjCreate (void)
{
OS_ERR err;
OSSemCreate((OS_SEM*)&led_sem,
(CPU_CHAR*)"LED SEM",
(OS_SEM_CTR)0,
(OS_ERR*)&err);
}
/*
*********************************************************************************************************
* FUNCTION DEFINITION
*********************************************************************************************************
*/
void led0_task(void *pdata)
{
OS_ERR err;
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
OSTimeDly(1000,OS_OPT_TIME_DLY,&err);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
OSTimeDly(1000,OS_OPT_TIME_DLY,&err);
OSSemPost(&led_sem,OS_OPT_POST_1,&err);
};
}
void led1_task(void *pdata)
{
OS_ERR err;
while(1)
{
OSSemPend(&led_sem,0,OS_OPT_PEND_BLOCKING,(CPU_TS *)0,&err);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
OSTimeDly(500,OS_OPT_TIME_DLY,&err);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
OSTimeDly(500,OS_OPT_TIME_DLY,&err);
};
}
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB.5
GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5推挽输出
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化GPIO
GPIO_SetBits(GPIOE,GPIO_Pin_5); //PE.5 输出高
}
编译结果如下:
编译报了两个错误,分别是未定义的CPU_TS_TmrRd与CPU_TS_TmrInit符号,这是两个跟时间戳相关的函数名,都定义在bsp.c中。有两种方法解决该问题:一种是移植bsp.c与bsp.h文件,里面的大部分内容要删除,只保留跟CPU_TS_TmrRd与CPU_TS_TmrInit相关的代码,该方案经验证移植后运行正常仿真报错;另一种是在os_cfg.h中关闭跟时间戳相关的编译选项,这就不用再移植bsp.c与bsp.h文件了,这里采用这第二种方案,关闭编译选项的配置如下:
// uCOSIII\Config\os_cfg.h中关闭的编译选项
#define OS_CFG_TS_EN 0u /* Enable (1) or Disable (0) time stamping */
#define OS_CFG_SCHED_LOCK_TIME_MEAS_EN 0u /* Include code to measure scheduler lock time */
// uCOSIII\Config\cpu_cfg.h中关闭的宏定义
/*
*********************************************************************************************************
* CPU TIMESTAMP CONFIGURATION
*********************************************************************************************************
*/
/* Configure CPU timestamp features (see Note #1) : */
#define CPU_CFG_TS_32_EN DEF_DISABLED
#define CPU_CFG_TS_64_EN DEF_DISABLED
/* DEF_DISABLED CPU timestamps DISABLED */
/* DEF_ENABLED CPU timestamps ENABLED */
/*
*********************************************************************************************************
* CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION
*********************************************************************************************************
*/
#if 0 /* Configure CPU interrupts disabled time ... */
#define CPU_CFG_INT_DIS_MEAS_EN /* ... measurements feature (see Note #1a). */
#endif
两个文件中与时间戳相关的编译选项关闭后,重新编译没有报错:
仿真结果如下图示:
仿真结果跟预期一致,运行结果也跟预期一致,多任务调度与信号量机制都正常工作了,UCOSⅢ的移植经验证没有问题。移植后的代码:https://github.com/StreamAI/UCOS_STM32
下图中的前两个目录分别是UCOSⅡ与UCOSⅢ针对STM32F107的官方源码,直接从Micrium官网下载后未经过任何更改;中间三个目录分别是STM32、UCOSⅢ与UCOSⅡ移植后并经示例程序验证成功的工程源码;最后一个压缩包是STM32F10x的固件库文件,从ST官网下载后删除了占空间较大的chm帮助文件,其余文件未经任何更改。