UCOSIII在STM32F103上的移植
之前断断续续学习了UCOSIII,在脑子里已经对他有了一定的认识。在这里趁着这段时间空闲,将之前有关UCOSIII的所学整理整理,巩固学习的同时也和大家交流分享。
前言
UCOSIII是一个可裁剪、可固化、可剥夺(preemptive)的多任务系统,没有任务数目限制,是UCOS的第三代内核,UCOSIII有以下几个重要特性:
- 极短的关中断时间
- 任务数目不受限制
- 优先级数量不受限制
- 内核对象数目不受限制
- 软件定时器
- 同时等待多个内核对象
- 直接向任务发送信号
- 直接向任务发送消息
- 任务寄存器
- 任务时钟节拍处理
- 防止死锁
下面是UCOSIII与UCOS和UCOSII的对比表格:
特性 | UCOS | UCOSII | UCOSIII |
---|---|---|---|
年份 | 1992 | 1998 | 2009 |
源代码 | 有 | 有 | 有 |
可剥夺型任务调度 | 有 | 有 | 有 |
最大任务数目 | 64 | 255 | 无限制 |
优先级相同的任务数目 | 1 | 1 | 无限制 |
时间片轮转调度 | 无 | 无 | 有 |
信号量 | 有 | 有 | 有 |
互斥信号量 | 无 | 有 | 有(可嵌套) |
事件标志 | 无 | 有 | 有 |
消息邮箱 | 有 | 有 | 不再需要 |
消息队列 | 有 | 有 | 有 |
固定大小的存储管理 | 无 | 有 | 有 |
直接向任务发送信号 | 无 | 无 | 有 |
无需调度的发送机制 | 无 | 无 | 可选 |
直接向任务发送消息 | 无 | 无 | 有 |
软件定时器 | 无 | 有 | 有 |
任务挂起恢复 | 无 | 有 | 有(可嵌套) |
防止死锁 | 有 | 有 | 有 |
可裁剪 | 有 | 有 | 有 |
代码量 | 3~8K | 6~26K | 6~24K |
数据量 | 1K+ | 1K+ | 1K+ |
代码可固化 | 有 | 有 | 有 |
运行时可配置 | 有 | 有 | 有 |
编译时可配置 | 有 | 有 | 有 |
支持内核对象的ASCII命名 | 无 | 有 | 有 |
同时等待多个内核对象 | 无 | 有 | 有 |
任务寄存器 | 无 | 有 | 有 |
内置性能测试 | 无 | 基本 | 增强 |
用户可定义的介入函数 | 无 | 有 | 有 |
“POST”操作可加时间戳 | 无 | 无 | 有 |
内核觉察式调试器 | 无 | 有 | 有 |
用汇编语言优化的调度器 | 无 | 无 | 有 |
捕捉退出的任务 | 无 | 无 | 有 |
任务级时间节拍处理 | 无 | 无 | 有 |
系统服务函数的数目 | ~20 | ~90 | ~70 |
所谓移植,是指能让UCOSIII在某个微处理器或者为控制器上能够运行。为了方便移植,UCOSIII的绝大部分代码使用C语言写的。在移植过程中我们重点是需要用C语言和汇编语言编写一些与处理器有关的代码。而且UCOSIII中那些与CPU寄存器打交道的代码只能用汇编语言编写(除非C编译器支持内嵌汇编语言)。得益于UCOSIII在设计时对可移植性的充分考虑,其在各个平台上的移植还是比较容易的。值得一提的是Micrium公司已经在各个主流的处理器上做好了移植工作,这些移植好的代码在官网上是可以直接免费下载的(Micrium官网的下载地址)。我们站在巨人的肩膀上从官方移植文件入手。
移植成功的几个前提条件:
- 处理器有可用的ANSI C编译器,能生成可重入性代码。
- 处理器支持中断,并且可产生定时中断(通常在10~1000Hz之间)。
- 可以开关中断。
- 处理器支持能够容纳足够多的数据(数千字节)的硬件堆栈。
- 处理器有将堆栈指针和其他CPU寄存器读出并存储到堆栈或内存中的指令。
- 处理器有足够的RAM空间用来存储UCOSIII的变量、数据结构体和内部任务堆栈。
- 编译器应该支持32位数据类型。
移植主要涉及三方面的内容:CPU、操作系统和板级代码(板级支持包BSP)。我们的移植对象为STM32F103。
几个重要移植文件讲解
Systick设置
F103的滴答计时器是一种24位的倒计时定时器,当计到0时,将从RELOAD寄存器中自动重装载定时器初值,只要不把他在Systick控制以及状态寄存器中的使能位清零,就将永久不熄。Systick的最大使命就是定期产生异常请求作为系统时基。OS都需要这种时基来推动任务和时间的管理。这里我们在delay_init()函数中完成滴答计时器的设置。具体的函数代码如下:
//初始化延时函数
//当使用延时函数时,该函数会初始化UCOS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS
u32 reload;
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SYSCLK/8; //为系统时钟的1/8
#if SYSTEM_SUPPORT_OS //如果需要支持OS (1)
reload=SYSCLK/8; //每秒钟的计数次数 (2)
reload*=1000000/delay_ostickspersec; (3)
fac_ms=1000/delay_ostickspersec; (4)
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; (5)
SysTick->LOAD=reload; (6)
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; (7)
#else (8)
fac_ms=(u16)fac_us*1000;
#endif
}
其中(1)~(8)部分代码就是在使用UCOSIII时配置SysTick的代码,如果SYSTEM_SUPPORT_OS被定义了就说明使用了UCOSIII操作系统了,那么我们就需要配置SysTick。首先要根据UCOSIII中定义的OS_TICKS_PER_SEC来计算出SysTick的装载值reload,开启SysTick中断,将reload值写进SysTick的LOAD寄存器中,最后开启SysTick。开启了SysTick后还要编写SysTick的中断服务函数SysTick_Handler(),函数代码如下,同样也采用了条件编译。
void SysTick_Handler(void)
{
if(delay_osrunning==1) //OS开始跑了,才执行正常的调度处理
{
OSIntEnter(); //进入中断
OSTimeTick(); //调用UCOS的时钟服务函数
OSIntExit(); //触发任务切换软中断
}
}
os_cpu_a_a.asm
为了方便起见,os_cpu_a_a.asm文件将分段介绍。
IMPORT OSRunning ; External references
IMPORT OSPrioCur
IMPORT OSPrioHighRdy
IMPORT OSTCBCurPtr
IMPORT OSTCBHighRdyPtr
IMPORT OSIntExit
IMPORT OSTaskSwHook
IMPORT OS_CPU_ExceptStkBase
EXPORT OSStartHighRdy ; Functions declared in this file
上面代码分为两部分,上部分用IMPORT来定义,下半部分使用EXPORT定义。IMPORT定义表示这是一个外部变量的标号,不是在本程序定义的;EXPORT定义表示这些函数是在本文件中定义的,供其它文件调用。
NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制寄存器
NVIC_SYSPRI4 EQU 0xE000ED22 ; 系统优先级寄存器
NVIC_PENDSV_PRI EQU 0xFFFF ; PendSV中断和系统节拍中断
NVIC_PENDSVSET EQU 0x10000000 ; 触发软件中断的值
EQU和C语言里的#define一样,定义一个宏。NVIC_INT_CTRL 为中断控制寄存器,地址为0xE000ED04,NVIC_SYSPRI4 为PendSV中断优先级寄存器,地址为0xE000ED22,NVIC_PENDSV_PRI为PendSV和Systick的中断优先级,这里为0xFFFF,都为最低优先级;NVIC_PENDSVSET 可以触发软件中断,通过给中断控制寄存器NVIC_INT_CTRL 的bit28位写1来触发软件中断,因此NVIC_PENDSVSET为0x10000000。
OSStartHighRdy
LDR R0, =NVIC_SYSPRI4 ; 设置PendSV的优先级
LDR R1, =NVIC_PENDSV_PRI
STRB R1, [R0]
MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
MSR PSP, R0
LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase
LDR R1, [R0]
MSR MSP, R1
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
CPSIE I ; Enable interrupts at processor level
OSStartHang
B OSStartHang ; Should never get here
OSStartHighRdy 是由OSStart()调用,用来开启多任务的,如果多任务开启失败就会进入OSStartHang。
OSCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
OSIntCtxSw
LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
OSCtxSw 和OSIntCtxSw 这两个函数是用来做任务切换的,这两个看起来都是一样的,其实都只是触发一个PendSV中断。
PendSV_Handler
CPSID I ; Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, PendSVHandler_nosave ; Skip register save the first time (1)
SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
STM R0, {R4-R11}
LDR R1, =OSTCBCurPtr ; OSTCBCurPtr->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
PendSVHandler_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, =OSTCBCurPtr ; OSTCBCurPtr = OSTCBHighRdyPtr;
LDR R1, =OSTCBHighRdyPtr
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2] (2) ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
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 (3) ; Ensure exception return uses process stack
CPSIE I
BX LR ; Exception return will restore remaining context
上面的汇编代码才是真正的任务切换程序。对代码中标号处做详细讲解:
(1)、如果PSP为0的话说明是第一次做任务切换,而任务创建的时候会调用堆栈初始化函数OSTaskStkInit() 来初始化堆栈,在初始化的过程中已经做了入栈处理,所以这里就不需要做入栈的处理了,直接跳转到PenSV_Handler_Nosave 。
(2)、此时SP指向的就是优先级最高的任务。
(3)、因为进入中断使用的是MSP,而退出中断的时候使用的是PSP,因此这里需要将LR的位2置1。
移植前的准备工作
准备基础工程
选择一个基础的用ST官方库代码进行编写的程序作为移植基础。这里我们选择一个简单的跑马灯程序作为移植的基础程序(跑马灯程序本来是裸机跑的程序,我们要将UCOSIII移植到该跑马灯程序中)。跑马灯程序源码
UCOSIII源码
UCOSIII的源码可以在Micrium官网上下载,我们这里下载的源码为官方在STM32F1xx上移植好的工程文件。UCOSIII源码官方下载地址。UCOSIII百度网盘下载地址
下载完UCOSIII的源码我们打开文件夹,如下图所示为打开文件夹后看到的文件夹内部情况:
- EvalBoards文件夹:这个文件夹是关于STM32F107的工程文件,其中的app_cfg.h、cpu_cfg.h、includes.h、lib_cfg.h、os_app_hooks.c、os_app_hooks.h、os_cfg.h、os_cfg_app.h 这八个文件是我们移植需要的
uC_CPU文件夹:
1、cpu_core.c文件:该文件包含了适用于所有CPU架构的C代码。包含了用来测量中断关闭事件的函数(中断关闭和打开分别由CPU_CRITICAL_ENTER() 和 CPU_CRITICAL_EXIT()两个宏实现),还包含了一个可模仿前导零计算的函数(防止CPU不提供这样的指令),以及一些其他的函数功能。
2、cpu_core.h文件:包含cpu_core.c中的函数原型声明,以及用来测量中断关闭时间变量的定义。
3、cpu_def.h文件:包含uC/CPU模块使用的各种#define常量。
4、ARM_Cortex_M3文件夹:其中有3个文件GUN、IAR、RealView,我们用的是Keil MDK编译器,所以RealView是我们需要的,RealView中有3个文件:
cpu.h包含了一些类型的定义,使UCOSIII和其他模块可与CPU架构和编译器字宽无关。在该文件中用户能够找到CPU_INIT16U、CPU_INIT32U、CPU_FP32等数据类型的定义。该文件还指定了CPU使用的是大端模式还是小端模式,定义了UCOSIII使用的CPU_STK数据类型,定义了CPU_CRITICAL_ENTER()和CPU_CRITICAL_EXTI(),还包括了一些与CPU架构相关的函数声明。
cpu_a.asm文件:包含了一些汇编语言编写的函数,可用来开中断和关中断,计算前导零(如果CPU支持这条指令),以及其他一些只能用汇编语言编写的与CPU相关的函数,这个文件中的函数可以从C代码库中调用。
cpu_c.c文件:包含了一些基于特定CPU架构的但为了可移植而用C语言编写的函数C代码,作为一个普通原则,除非汇编语言能显著提高性能,否则尽量用C语言编写函数。3、uC_LIB文件夹:其由一些可移植并且与编译器无关的函数组成,UCOSIII 中不使用uC_LIB中的函数,但是这里假定lib_def.h存在,uC_LIB中包含以下文件:
lib_ascii.h和lib_ascii.c文件、lib_def.h文件、lib_math.h和lib_math.c文件、lib_mem.c和lib_mem.h文件、lib_str.c和lib_str.h文件、lib_mem_a.asm文件。以上文件有兴趣的同志们自己去查有关资料。
4、uCOS_III文件夹:这个文件夹中有两个文件Port和Source,前者为与CPU有关的文件,后者为UCOSIII3.03源码,具体文件说明见下表:
文件 | 描述 |
---|---|
os_h | 包含UCOSIII的主要的头文件,声明了常量、宏、全局变量、函数原型等 |
os_cfg_app.c | 根据os_cfg_app.h中的宏定义声明变量和数组 |
os_core.c | UCOSIII的内核功能模块 |
os_dbg.c | 包含内核调试或uC/Probe使用的常量声明 |
os_flag.c | 包含事件标志的管理代码 |
os_int.c | 包含中断处理任务的代码 |
os_mem.c | 包含UCOSIII固定大小的存储分区的管理代码 |
os_msg.c | 包含消息处理的代码 |
os_mutex.c | 包含互斥信号量的管理代码 |
os_pend_multi.c | 包含允许任务同时等待多个信号量或者多个消息队列的代码 |
os_prio.c | 包含位映射表的管理代码,用于追踪那些已经就绪的任务 |
os_q.c | 包含消息队列的管理代码 |
os_sem.c | 包含信号量的管理代码 |
os_stat.c | 包含统计任务的管理代码 |
os_task.c | 包含任务的管理代码 |
os_tick.c | 包含可管理正在延时和超时等待的任务代码 |
os_time.c | 包含允许任务延时一段时间的代码 |
os_tmr.c | 包含软件定时器的代码 |
os_type.h | 包含UCOSIII数据类型的声明 |
os_var.c | 包含UCOSIII的全局变量 |
UCOSIII移植
新建相应的文件夹
在待移植的工程目录(跑马灯程序)中新建一个UCOSIII文件夹,然后将我们下载的Micrium官方移植工程中的uC-CPU、uC-LIB、UCOS-III这三个文件复制到工程中。这里还需在UCOSIII文件中新建两个文件:UCOS_BSP、UCOS_CONFIG,如下图所示:
向UCOS_CONFIG中添加文件
复制Micrium官方移植好的工程中的相关文件到UCOS_CONFIG文件夹下,文件路径为:Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\uCOS-III
向UCOS_BSP添加文件
复制Micrium官方移植好的工程的相关文件到UCOS_BSP文件下,路径为:Micrium官方移植\Software\EvalBoards\Micrium\uC-Eval-STM32F107\BSP
向工程中添加分组
准备好了所需文件后,还要将文件添加到我们的工程中去,在KEIL中先添加分组,如下图所示:
添加完分组还要给分组添加文件,添加情况如图所示:
为了编译时能找到相关文件,这里还需要设置包含路径,设置情况如下图所示:
修改bsp.c和bsp.h文件
这里修改好的bsp.c文件代码如下:
#define BSP_MODULE
#include <bsp.h>
#define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC)
#define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000)
#define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004)
#define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004)
#define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24
#define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00
/*
*********************************************************************************************************
* BSP_CPU_ClkFreq()
* Description : Read CPU registers to determine the CPU clock frequency of the chip.
* Argument(s) : none.
* Return(s) : The CPU clock frequency, in Hz.
* Caller(s) : Application.
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT32U BSP_CPU_ClkFreq (void)
{
RCC_ClocksTypeDef rcc_clocks;
RCC_GetClocksFreq(&rcc_clocks);
return ((CPU_INT32U)rcc_clocks.HCLK_Frequency);
}
/*$PAGE*/
/*
*********************************************************************************************************
* CPU_TS_TmrInit()
* Description : Initialize & start CPU timestamp timer.
* Argument(s) : none.
* Return(s) : none.
* Caller(s) : CPU_TS_Init().
* This function is an INTERNAL CPU module function & MUST be implemented by application/
* BSP function(s) [see Note #1] but MUST NOT be called by application function(s).
* Note(s) : (1) CPU_TS_TmrInit() is an application/BSP function that MUST be defined by the developer
* if either of the following CPU features is enabled :
* (a) CPU timestamps
* (b) CPU interrupts disabled time measurements
* See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1'
* & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'.
* (2) (a) Timer count values MUST be returned via word-size-configurable 'CPU_TS_TMR'
* data type.
* (1) If timer has more bits, truncate timer values' higher-order bits greater
* than the configured 'CPU_TS_TMR' timestamp timer data type word size.
* (2) Since the timer MUST NOT have less bits than the configured 'CPU_TS_TMR'
* timestamp timer data type word size; 'CPU_CFG_TS_TMR_SIZE' MUST be
* configured so that ALL bits in 'CPU_TS_TMR' data type are significant.
* In other words, if timer size is not a binary-multiple of 8-bit octets
* (e.g. 20-bits or even 24-bits), then the next lower, binary-multiple
* octet word size SHOULD be configured (e.g. to 16-bits). However, the
* minimum supported word size for CPU timestamp timers is 8-bits.
* See also 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #2'
* & 'cpu_core.h CPU TIMESTAMP DATA TYPES Note #1'.
* (b) Timer SHOULD be an 'up' counter whose values increase with each time count.
* (c) When applicable, timer period SHOULD be less than the typical measured time
* but MUST be less than the maximum measured time; otherwise, timer resolution
* inadequate to measure desired times.
* See also 'CPU_TS_TmrRd() Note #2'.
*********************************************************************************************************
*/
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; /* Enable Cortex-M4's DWT CYCCNT reg. */
BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u;
BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA;//¿ªÆôCYCCNT
CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq);
}
#endif
/*$PAGE*/
/*
*********************************************************************************************************
* CPU_TS_TmrRd()
* Description : Get current CPU timestamp timer count value.
* Argument(s) : none.
* Return(s) : Timestamp timer count (see Notes #2a & #2b).
* Caller(s) : CPU_TS_Init(),
* CPU_TS_Get32(),
* CPU_TS_Get64(),
* CPU_IntDisMeasStart(),
* CPU_IntDisMeasStop().
* This function is an INTERNAL CPU module function & MUST be implemented by application/
* BSP function(s) [see Note #1] but SHOULD NOT be called by application function(s).
* Note(s) : (1) CPU_TS_TmrRd() is an application/BSP function that MUST be defined by the developer
* if either of the following CPU features is enabled :
* (a) CPU timestamps
* (b) CPU interrupts disabled time measurements
* See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1'
* & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'.
* (2) (a) Timer count values MUST be returned via word-size-configurable 'CPU_TS_TMR'
* data type.
* (1) If timer has more bits, truncate timer values' higher-order bits greater
* than the configured 'CPU_TS_TMR' timestamp timer data type word size.
* (2) Since the timer MUST NOT have less bits than the configured 'CPU_TS_TMR'
* timestamp timer data type word size; 'CPU_CFG_TS_TMR_SIZE' MUST be
* configured so that ALL bits in 'CPU_TS_TMR' data type are significant.
* In other words, if timer size is not a binary-multiple of 8-bit octets
* (e.g. 20-bits or even 24-bits), then the next lower, binary-multiple
* octet word size SHOULD be configured (e.g. to 16-bits). However, the
* minimum supported word size for CPU timestamp timers is 8-bits.
* See also 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #2'
* & 'cpu_core.h CPU TIMESTAMP DATA TYPES Note #1'.
* (b) Timer SHOULD be an 'up' counter whose values increase with each time count.
* (1) If timer is a 'down' counter whose values decrease with each time count,
* then the returned timer value MUST be ones-complemented.
* (c) (1) When applicable, the amount of time measured by CPU timestamps is
* calculated by either of the following equations :
* (A) Time measured = Number timer counts * Timer period
* where
*
* Number timer counts Number of timer counts measured
* Timer period Timer's period in some units of
* (fractional) seconds
* Time measured Amount of time measured, in same
* units of (fractional) seconds
* as the Timer period
*
* Number timer counts
* (B) Time measured = ---------------------
* Timer frequency
*
* where
*
* Number timer counts Number of timer counts measured
* Timer frequency Timer's frequency in some units
* of counts per second
* Time measured Amount of time measured, in seconds
*
* (2) Timer period SHOULD be less than the typical measured time but MUST be less
* than the maximum measured time; otherwise, timer resolution inadequate to
* measure desired times.
*********************************************************************************************************
*/
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
CPU_TS_TMR ts_tmr_cnts;
ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT;
return (ts_tmr_cnts);
}
#endif
/*$PAGE*/
/*
*********************************************************************************************************
* CPU_TSxx_to_uSec()
* Description : Convert a 32-/64-bit CPU timestamp from timer counts to microseconds.
* Argument(s) : ts_cnts CPU timestamp (in timestamp timer counts [see Note #2aA]).
* Return(s) : Converted CPU timestamp (in microseconds [see Note #2aD]).
* Caller(s) : Application.
* This function is an (optional) CPU module application programming interface (API)
* function which MAY be implemented by application/BSP function(s) [see Note #1] &
* MAY be called by application function(s).
* Note(s) : (1) CPU_TS32_to_uSec()/CPU_TS64_to_uSec() are application/BSP functions that MAY be
* optionally defined by the developer when either of the following CPU features is
* enabled :
* (a) CPU timestamps
* (b) CPU interrupts disabled time measurements
* See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1'
* & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'.
* (2) (a) The amount of time measured by CPU timestamps is calculated by either of
* the following equations :
*
* 10^6 microseconds
* (1) Time measured = Number timer counts * ------------------- * Timer period
* 1 second
*
* Number timer counts 10^6 microseconds
* (2) Time measured = --------------------- * -------------------
* Timer frequency 1 second
*
* where
*
* (A) Number timer counts Number of timer counts measured
* (B) Timer frequency Timer's frequency in some units
* of counts per second
* (C) Timer period Timer's period in some units of
* (fractional) seconds
* (D) Time measured Amount of time measured,
* in microseconds
*
* (b) Timer period SHOULD be less than the typical measured time but MUST be less
* than the maximum measured time; otherwise, timer resolution inadequate to
* measure desired times.
*
* (c) Specific implementations may convert any number of CPU_TS32 or CPU_TS64 bits
* -- up to 32 or 64, respectively -- into microseconds.
*********************************************************************************************************
*/
#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
修改完的bsp.h文件代码如下:
#ifndef BSP_PRESENT
#define BSP_PRESENT
#ifdef BSP_MODULE
#define BSP_EXT
#else
#define BSP_EXT extern
#endif
#include <stdio.h>
#include <stdarg.h>
#include <cpu.h>
#include <cpu_core.h>
#include <lib_def.h>
#include <lib_ascii.h>
#include <stm32f10x_conf.h>
#endif
修改os_cpu_c.c文件
在os_cpu_c.c文件开始添加includes.h头文件。如图所示:
修改os_cfg_app.h
我们还需要os_cfg_app.h文件,os_cfg_app.h主要是对UCOSIII内部一些系统任务的配置,如任务优先级、任务堆栈、UCOSIII的系统时钟节拍等等,os_cfg_app.h文件都是一些宏定义,比较简单,代码如下:
#ifndef OS_CFG_APP_H
#define OS_CFG_APP_H
/*
************************************************************************************************************************
* CONSTANTS
************************************************************************************************************************
*/
/* --------------------- MISCELLANEOUS ------------------ */
#define OS_CFG_MSG_POOL_SIZE 100u /* Maximum number of messages */
#define OS_CFG_ISR_STK_SIZE 128u /* Stack size of ISR stack (number of CPU_STK elements) */
#define OS_CFG_TASK_STK_LIMIT_PCT_EMPTY 10u /* Stack limit position in percentage to empty */
/* ---------------------- IDLE TASK --------------------- */
#define OS_CFG_IDLE_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
/* ------------------ ISR HANDLER TASK ------------------ */
#define OS_CFG_INT_Q_SIZE 10u /* Size of ISR handler task queue */
#define OS_CFG_INT_Q_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
/* ------------------- STATISTIC TASK ------------------- */
#define OS_CFG_STAT_TASK_PRIO (OS_CFG_PRIO_MAX-2u) /* Priority */
#define OS_CFG_STAT_TASK_RATE_HZ 10u /* Rate of execution (10 Hz Typ.) */
#define OS_CFG_STAT_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
/* ------------------------ TICKS ----------------------- */
#define OS_CFG_TICK_RATE_HZ 200u /* Tick rate in Hertz (10 to 1000 Hz) */
#define OS_CFG_TICK_TASK_PRIO 1u /* Priority */
#define OS_CFG_TICK_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
#define OS_CFG_TICK_WHEEL_SIZE 17u /* Number of 'spokes' in tick wheel; SHOULD be prime */
/* ----------------------- TIMERS ----------------------- */
#define OS_CFG_TMR_TASK_PRIO 2u /* Priority of 'Timer Task' */
#define OS_CFG_TMR_TASK_RATE_HZ 100u /* Rate for timers (10 Hz Typ.) */
#define OS_CFG_TMR_TASK_STK_SIZE 128u /* Stack size (number of CPU_STK elements) */
#define OS_CFG_TMR_WHEEL_SIZE 17u /* Number of 'spokes' in timer wheel; SHOULD be prime */
#endif
在UCOSIII中有5个系统任务:空闲任务、时钟节拍任务、统计任务、定时任务和中断服务管理任务,在系统初始化的时候至少要创建两个任务。空闲任务的优先级应该为最低OS_CFG_PRIO_MAX_1,如果使用中断管理任务的话那么中断服务管理任务的优先级应该为最高0.其他3个任务的用户优先级可以自己设定。本次移植中将统计任务设定为OS_CFG_PRIO_MAX_2, 即倒数第二的优先级;时钟节拍任务设置为1;定时器任务优先级为2。所以0、1、2、OS_CFG_PRIO_MAX_1、OS_CFG_PRIO_MAX_2这5个优先级我们的普通任务不可用了。
修改sys.h
很简单只要将SYSTEM_SUPPORT_OS置1,以支持UCOS操作系统。代码如下:
#define SYSTEM_SUPPORT_OS 1
测试软件设计
在这里我们设计了一个测试软件:建立3个任务,2个为LED灯的闪烁,一个为测试浮点计算,代码如下:
#define START_TASK_PRIO 3
#define START_STK_SIZE 512
OS_TCB StartTaskTCB;
CPU_STK START_TASK_STK[START_STK_SIZE];
void start_task(void *p_arg);
#define LED0_TASK_PRIO 4
#define LED0_STK_SIZE 128
OS_TCB Led0TaskTCB;
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
void led0_task(void *p_arg);
#define LED1_TASK_PRIO 5
#define LED1_STK_SIZE 128
OS_TCB Led1TaskTCB;
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
void led1_task(void *p_arg);
#define FLOAT_TASK_PRIO 6
#define FLOAT_STK_SIZE 128
OS_TCB FloatTaskTCB;
__align(8) CPU_STK FLOAT_TASK_STK[FLOAT_STK_SIZE];
void float_task(void *p_arg);
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
LED_Init();
OSInit(&err);
OS_CRITICAL_ENTER();
OSTaskCreate((OS_TCB * )&StartTaskTCB,
(CPU_CHAR * )"start task",
(OS_TASK_PTR )start_task,
(void * )0,
(OS_PRIO )START_TASK_PRIO,
(CPU_STK * )&START_TASK_STK[0],
(CPU_STK_SIZE)START_STK_SIZE/10,
(CPU_STK_SIZE)START_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //ÈÎÎñÑ¡Ïî
(OS_ERR * )&err);
OS_CRITICAL_EXIT();
OSStart(&err);
while(1);
}
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER();
OSTaskCreate((OS_TCB * )&Led0TaskTCB,
(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 )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OSTaskCreate((OS_TCB * )&Led1TaskTCB,
(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 )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OSTaskCreate((OS_TCB * )&FloatTaskTCB,
(CPU_CHAR * )"float test task",
(OS_TASK_PTR )float_task,
(void * )0,
(OS_PRIO )FLOAT_TASK_PRIO,
(CPU_STK * )&FLOAT_TASK_STK[0],
(CPU_STK_SIZE)FLOAT_STK_SIZE/10,
(CPU_STK_SIZE)FLOAT_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err);
OS_CRITICAL_EXIT();
}
void led0_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED0=0;
OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err);
LED0=1;
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
void led1_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
LED1=~LED1;
OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
void float_task(void *p_arg)
{
CPU_SR_ALLOC();
static float float_num=0.01;
while(1)
{
float_num+=0.01f;
OS_CRITICAL_ENTER();
printf("float_numµÄֵΪ: %.4f\r\n",float_num);
OS_CRITICAL_EXIT();
delay_ms(500);
}
}
测试代码运行的效果为:led0_task任务让LED0亮200ms灭500ms,led1_task任务让LED1亮500ms灭500ms,float_task任务测试浮点计算,每次增加0.01。用来验证UCOSIII的FPU是否移植成功。如下图所示为测试代码实际调试现象:
终于写完了,第一次尝试Markdown编辑,已经深深的爱上她了。太帮了。还有本次UCOSIII移植大部分参考F103ZE原子资料 ,很棒的,有什么问题大家可以参考F103ZE原子资料 。这里附上论坛链接:原子(很赞的哦,快点我) 。