UCOSIII學習之UCOSIII系統移植
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,
(CP