久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 9000|回復: 4
打印 上一主題 下一主題
收起左側

UCos-ii在STM32上的移植詳解

[復制鏈接]
跳轉到指定樓層
樓主
ID:419321 發表于 2018-11-1 20:01 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
UCos-ii_在STM32上的移植詳解,通過本例程可以學習一下ucosii!
下載代碼
stm32標準外設庫是stm32全系列芯片的外設驅動,有了它可以大大加速我們開發stm32。
首先從 st公司的網站下載最新的stm32標準外設庫,寫本文時最新的版本是V3.5.0。
解壓該zip文件,得到如下文件夾和文件
STM32F10x_StdPeriph_Lib_V3.5.0\
_htmresc
Libraries
Project
Utilities
Release_Notes.html
stm32f10x_stdperiph_lib_um.chm
其中Libraries包含庫的源代碼,Project包含 stm32 各個外設的使用范例和一個工程模板,Utilities是使用st公司評估板的例子, stm32f10x_stdperiph_lib_um.chm教我們怎么用標準外設庫。

工程目錄結構
既然準備使用32位單片機, 應該是個不小項目, 因此工程目錄也應該做個規劃。這里我推薦一下我所使用的目錄結構。假設工程名字叫template,建一個名為template 的文件夾,該目錄下有個3 個固定文件夾doc,src,include,doc用來存放工程相關的資料文件,src放源代碼,在 src下每個功能模塊一個文件夾,include放各個模塊都要使用的公共頭文件。 output放編譯輸出文件,內含兩個子文件夾obj和 list。
template\
   doc
   src
   include
   output\obj
             \list
整理庫代碼
由于Libraries下的CMSIS文件夾中很多代碼是和編譯器及芯片相關的,導致文件夾多且深度大,不利于工程維護,實際上一個項目往往是用固定的編譯器和芯片,因此有必要對庫進行整理。

在 src下建立 libstm32目錄
1. 把 Libraries\STM32F10x_StdPeriph_Driver\下的內容拷貝到libstm32目錄

2. 在 libstm32 目錄下建立 cmsis文件夾,把
Libraries\CMSIS\CM3\CoreSupport\下的core_cm3.c,core_cm3.h;
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\下的 stm32f10x.h,system_stm32f10x.c,system_stm32f10x.h 拷貝到 cmsis文件夾中。
3. 根據你所選的芯片類型,將
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm\下對應的啟動文件拷貝到cmsis文件夾中。這里我拷貝的是startup_stm32f10x_hd.s(大容量型stm32芯片的啟動文件)。
下面對該庫文件做個簡單介紹:
Libraries\STM32F10x_StdPeriph_Driver\下的內容很好理解就是stm32 的各個外設模塊驅動代碼。
    misc.h和 misc.c 是和 CM3 內核有關的NVIC 和SysTick 的驅動代碼。   
    Libraries\CMSIS 下是什么呢?cmsis英文全稱:Cortex Microcontroller
Software Interface Standard,是Cortex系列處理器硬件抽象層,可以理解為cortex內核的軟件接口。
    core_cm3.c, core_cm3.h
它們的目錄名為 CoreSupport,說明這兩個文件是CM3內核支撐文件,其他使用 CM3 內核的芯片也可以用,不一定是stm32。這兩個文件用來獲取設置CM3 內核,配置一些內核寄存器。
stm32f10x.h, system_stm32f10x.c, system_stm32f10x.h 和
startup_stm32f10x_hd.s在 DeviceSupport目錄下,說明這幾個文件是和具體的芯片有關的,也就是stm32 芯片的支撐文件。其中stm32f10x.h 是標準外設庫的入口,使用標準外設庫的代碼中必須包含該頭文件。system_stm32f10x.c,
system_stm32f10x.h 這兩個文件提供函數用來初始化stm32 芯片,配置 PLL、系統時鐘和內置flash 接口。startup_stm32f10x_hd.s是大容量型stm32 芯片的啟動文件。 建立工程   
使用keil MDK(我使用 4.12版)在 template 目錄下建立工程,工程名為template。選一個 stm32 系列的芯片,哪一個都無所謂(我選的是STM32F101RC,因為我的板子就是用這個芯片),接下來要注意的是當彈出是否拷貝啟動代碼到工程文件夾時要選No,因為標準外設庫里已經有啟動代碼了。
將 UV4 中project window 里的頂層目錄名改為template,并將第一個group名改為libstm32。把libstm32目錄下所有.c和.s 文件加載到工程里的libstm32。 在 src下建立一個init目錄用來放置系統初始化代碼。把Project\STM32F10x_StdPeriph_Template\下的 stm32f10x_it.c拷貝到 init文件夾中,stm32f10x_it.h,stm32f10x_conf.h 拷貝到include文件夾中。
stm32f10x_it.c,stm32f10x_it.h 是中斷服務程序文件。stm32f10x_conf.h 是標準外設庫的配置文件,對于工程中不需要的外設,可以注釋掉里面的包含的頭文件。這里我建議先僅留下stm32f10x_gpio.h,stm32f10x_rcc.h,misc.h,用到什么再打開什么,這樣編譯起來快一點,當然也可都留著。

使用stm32標準外設庫
事實上,stm32標準外設庫的使用在stm32f10x_stdperiph_lib_um.chm中的How to use the Library一節中已有說明,下面我把其中的步驟羅列一下:
1. 根據所選芯片,把Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm中的啟動代碼加到工程中,這一步在上面已經做過了。
2. 在stm32f10x.h 的66-73 行,根據所選芯片類型,去掉相應注釋,這里我去掉STM32F10X_HD 行的注釋(大容量型 stm32芯片)。
3. 去掉105 行的USE_STDPERIPH_DRIVER 注釋,啟用 stm32 標準外設庫。
4. 在system_stm32f10x.c的110-115行,根據所選芯片主頻,去掉相應注釋,默認 SYSCLK_FREQ_72MHz注釋已去掉,如果你的芯片主頻是72MHz,就不用做修改了,這里我的芯片是36MHz,注釋SYSCLK_FREQ_72MHz,去掉SYSCLK_FREQ_36MHz注釋。


跑馬燈程序
現在可以使用stm32 標準外設庫了,下面以一個簡單的跑馬燈程序說明。
在 init目錄下建立main.c 作為系統入口。
在 src下建立一個bsp目錄用來放置板級支持代碼,建立led.c,led.h。
代碼如下:
led.h

#ifndef _LED_H_
#define _LED_H_

#include <stdint.h>

#define LED_0     0
#define LED_1     1
#define LED_2     2

void led_init(void);
void led_on(uint32_t n);
void led_off(uint32_t n);

#endif

led.c

#include "stm32f10x.h"
#include "led.h"

void led_init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);
}




void led_on(uint32_t n) {
switch (n) {
case LED_0:
     GPIO_SetBits(GPIOC, GPIO_Pin_6);
      break;
    case LED_1:
      GPIO_SetBits(GPIOC, GPIO_Pin_7);
      break;
    case LED_2:
      GPIO_SetBits(GPIOC, GPIO_Pin_8);
      break;
    default:
      break;
  }
}

void led_off(uint32_t n){
  switch (n) {
    case LED_0:
      GPIO_ResetBits(GPIOC, GPIO_Pin_6);
      break;
    case LED_1:                             
      GPIO_ResetBits(GPIOC, GPIO_Pin_7);
     break;
    case LED_2:
      GPIO_ResetBits(GPIOC, GPIO_Pin_8);
      break;
    default:
     break;
}
}

main.c
#include "led.h"
static void delay(uint32_t ms){
uint32_t count = 8000;
  while (ms--) {
    while (count--);
    count = 8000;
  }
}

int main(void){
  led_init();
   for (;;) {
    led_on(LED_0);
   led_off(LED_1);
  led_off(LED_2);
  delay(1000);

  led_off(LED_0);
  led_on(LED_1);
  led_off(LED_2);
  delay(1000);

  led_off(LED_0);
  led_off(LED_1);
  led_on(LED_2);  
  delay(1000);
}
}

在 project中建立init, bsp 組,并將各種代碼加入。在工程的Options 中, c/c++
選項卡的 Include Paths 中添加.\include;  .\src\libstm32\cmsis; .\src\libstm32\inc;
.\src\bsp;。
Output選項卡 Select Folder for Objects 中選.\output\obj。
Listing 選項卡 Select Folder for Listings中選.\output\list。
Debug選項卡選use ULINK Cortex Debugger, Run to main()打鉤,這一步大家可以根據自己手上的仿真器做不同選擇。編譯運行。 ucosii在stm32 上的移植詳解

雖然目前網上已經有不少關于ucosii在stm32 上的移植版本,包括micrium也有官方移植版本。但這些版本具體是怎么移植出來的,又該怎么基于移植好的ucosii開發應用軟件,網上介紹的并不多。這里介紹一下我的移植經歷,希望對大家有所幫助。
我的移植基本上是從零開始的。首先想要做好移植,有兩方面的內容是必須要了解。1.目標芯片;2.ucosii 內核原理。
雖然我們移植的目標芯片是stm32,但操作系統的移植基本是針對Cortex-M3內核(以下簡稱CM3)而言的,所以我們只需了解CM3 內核就好了。stm32芯片就是CM3內核加上各種各樣的外設。
怎么才能了解CM3 呢?看一本書<<ARM Cortex-M3 權威指南>>(宋巖譯,網上多的很)就好了,很多同學可能想,看完這本書移植的新鮮勁都沒了,因此我把該書和移植有關的章節都列了出來,并對其中的重點內容進行介紹,我數了數相關章節還不到100頁,就這點內容,總要看了吧。
相關章節如下:
chapter2 Cortex-M3 概覽
2.1 - 2.9
主要了解Cortex-M3的概貌。剛開始看時不用追求全部理解,后面會有詳細介紹,很多內容多看幾遍就明白。其中2.8 指令集,只要了解,CM3只使用thumb2就 ok了。
chapter3 Cortex-M3 基礎
3.1 寄存器組
    R0-R12: 通用寄存器
R13: 堆棧寄存器
有兩個,MSP 和 PSP,同時只能看見一個 引用R13時,引用的是正在使用的那個
MSP:可用于異常服務和應用程序
PSP:只能用于應用程序
系統復位后,用的堆棧指針是MSP。
  
R14: 連接寄存器,又名LR,存儲返回地址
R15: 程序計數寄存器,又名PC
3.2 特殊功能寄存器
程序狀態字寄存器組(PSRs)
中斷屏蔽寄存器組(PRIMASK, FAULTMASK, BASEPRI)
控制寄存器(CONTROL)
程序狀態字寄存器組(PSRs)分為
應用程序 PSR(APSR)
中斷號 PSR(IPSR)
執行 PSR(EPSR)     每個都是32位,由于這3 個寄存器有效位是錯開的,因此可以組合訪問。
中斷屏蔽寄存器組(PRIMASK, FAULTMASK, BASEPRI),這三個寄存器用于
控制異常的使能和除能。
控制寄存器(CONTROL)它有兩個作用:
1.定義特權級別
2.選擇當前使用哪個堆棧指針
3.3 操作模式和特權極別
操作模式: 處理者模式和線程模式
異常處理:處理者模式
主程序:線程模式
ucosii 不區分特權級和用戶級,程序始終工作在特權級
這兩個堆棧指針的切換是全自動的,就在出入異常服務例程時由硬件處理。
3.4 - 3.7
沒什么好講的,需要看。
3.8 復位序列
0x00000000 MSP 初值
0x00000004 PC 初值 復位向量
chapter7 異常
7.1 異常類型
分為系統異常(編號1-15)和外部中斷(大于15)
7.2 優先級
CM3支持3 個固定的高優先級和多達256 級的可編程優先級。
在 NVIC 中,每個中斷都有一個優先級配置寄存器(1 個byte),用來配置該
中斷的優先級。但該寄存器并不是每個位都被使用,不同制造商生產的芯片不相同,
譬如stm32使用 4位,也就是說stm32支持16 個可編程優先級(參考: chapter9)。  
注意該寄存器是以 MSB對齊的,因此 stm32每個中斷的優先級配置寄存器7:4
位有效,3:0 位無效。
對于優先級,CM3 又分為搶占優先級和亞優先級,
NVIC中的應用程序中斷及復位控制寄存器(AIRCR)的優先級分組(10:8)描述了
如何劃分搶占優先級和亞優先級。
什么意思?以stm32 為例,優先級配置寄存器不是7:4位有效嗎,如果 AIRCR
中的優先級分組值為4,則優先級配置寄存器的7:5 位確定搶占優先級,位4 確定
亞優先級。此時所有中斷有8個搶占優先級,每個搶占優先級有2 個亞優先級。
搶占優先級高的中斷可以搶占搶占優先級低的中斷,即搶占優先級決定了中斷是
否可以嵌套。
相同搶占優先級的中斷不能嵌套,但當搶占優先級相同的異常有不止一個到來
時,就優先響應亞優先級最高的異常。
參考附錄D  
表D.9 中斷優先級寄存器陣列 0xE000_E400 - 0xE000_E4EF 共240 個。
表D.16 系統異常優先級寄存器 0xE000_ED18 - 0xE000_ED23 共12 個。
優先級相同,看中斷號,中斷號小的優先。
7.3 向量表   
初始在0x00000000 處,可以通過向量表偏移量寄存器(VTOR)(地址:
0xE000_ED08)更改,一般無需更改。
7.4 中斷輸入及掛起行為
需要看。
7.5 Fault異常
可不看。
7.6 SVC和PendSV
SVC
SVC 主要用在分特權級和用戶級的操作系統,ucosii 不區分特權級和用戶級,
可以不管這個東西。
這里說點題外話,一開始我很奇怪為什么會提供這種中斷,因為這種中斷一般
都是用在大型的操作系統上,如linux系統上,可CM3 又不提供MMU,應該是無
法移植linux系統。后來我才知道uclinux是針對沒有MMU的嵌入式系統而設計的,
不過還是很懷疑有人會在像stm32這種芯片上用uclinux。
PendSV
PendSV 中斷主要做上下文切換,也就是任務切換,是ucosii 移植過程中最重要
的中斷。
主要有兩點:
1.PendSV中斷是手工往NVIC的PendSV懸起寄存器中寫1產生的(由OS
寫)。
2.PendSV中斷優先級必須設為最低。
在講移植代碼時會介紹具體是如何做的。
對于7.6的PendSV 部分應認真研讀一下。
chapter8 NVIC 與中斷控制
NVIC負責芯片的中斷管理,它和CM3內核緊密相關。
如果對于CM3 中斷配置不是很了解,可以看看 8.1, 8.2, 8.3, 8.4 節。
8.7 節講述了 SysTick定時器,需要看。
chapter9 中斷的具體行為
9.1 中斷/異常的響應序列
當CM3 開始響應一個中斷時
1.xPSR, PC, LR, R12以及 R3‐R0 入棧
2.取向量
     3.選擇堆棧指針 MSP/PSP,更新堆棧指針SP,更新連接寄存器LR,更新程序
計數器 PC
對移植ucosii 來說,需要注意1,3
9.2 異常返回
在CM3 中,進入中斷時,LR 寄存器的值會被自動更新。9.6 節對更新后的值進
行說明。這里統稱EXC_RETURN。返回時通過把 EXC_RETURN往PC里寫來識
別返回動作的。因為EXC_RETURN是一個特殊值,所以對于CM3,匯編語言就
不需要類似reti 這種指令,而用C語言開發時,不需要特殊編譯器命令指示一個函數為中斷服務程序。實際上,中斷服務程序如果是c代碼編寫,匯編成匯編代碼,
函數結尾一般是reti。
9.3 嵌套的中斷
只要注意:中斷嵌套不能過深即可。
9.4 和9.5
這兩節說明CM3對中斷的響應能力大大提高了,主要是硬件機制的改進。但對
移植來說,并不需要關注。
9.6 異常返回值
對不同狀態進入中斷時,LR 寄存器的值進行說明,需要看。這里有一點需要注
意,該點在講移植代碼時再介紹。
9.7 和9.8
對移植來說,并不需要關注。
chapter10 Cortex-M3的低層編程
這一章僅需關注 10.2節,因為對移植來說匯編與C的接口是必須面對的。
10.2 匯編與 C 的接口
有兩點需要知道:
1.當主調函數需要傳遞參數(實參)時,它們使用R0‐R3。其中 R0傳遞第一
個,R1 傳遞第 2個……在返回時,把返回值寫到R0 中。
2.在函數中,用匯編寫代碼時,R0-R3, R12可以隨便使用,而使用R4‐R11,
則必須先PUSH,后POP。
以上內容和移植多少都有些關系,剛開始看,可能不太明白,多看幾遍就好了。 ucosii在stm32 上的移植詳解 2  

在詳解1 中主要講了移植需要用到的CM3 內核知識,本文講一講ucosii 的原理
和代碼組成。ucosii最經典的學習資料莫過于邵貝貝老師的<<嵌入式實時操作系統
uc/os-ii(第2 版)>>,我想這本書對學 ucosii 已經足夠了,因為他把ucosii V2.55代
碼都講了一遍。移植前應該好好看看此書。
下面說說我對ucosii 的理解。應該說 ucosii 這個內核還是比較簡單的,基本可
以分為任務調度,任務同步和內存管理三個部分。
任務調度
ucosii 為保證實時性,給每個任務分配一個不同的優先級。當發生任務切換時,
總是切換到就緒的最高優先級任務。有2種情況會發生任務切換。
1.任務等待資源就緒或自我延時;
2.退出中斷;
情況1可以理解為任務主動放棄cpu 的使用權。
情況2可以理解為中斷后,某種資源可能就緒了,需要任務切換。
需要注意的是SysTick中斷,這個中斷是os的“心跳”,必須得有。這樣就使得
cpu 會發生周期性地做任務切換。由于 ucosii不支持時間片輪轉調度,因此在該中
斷中必須做的工作僅有os的時間管理。也就是調用OSTimeTick()。
任務同步
任務同步和大多數操作系統的做法差不多,如果學過操作系統或是有多線程編
程經驗的話,應該很好理解。無非是任務A因為某個資源未就緒,就放棄cpu 使用
權,等任務B或是中斷使該資源就緒,當再次任務進行切換時如果任務A 優先級最
高,則任務A繼續執行。具體怎么實現就看邵老師的書吧。
內存管理
ucosii 的內存管理比較簡單,就不說了。
下面看看ucosii 代碼組成:
os_core.c是 ucosii 的核心,它包含了內核初始化,任務切換,事件塊管理等,
其中事件塊是各個同步量(這里我把互斥量,信號量,郵箱,隊列統稱為同步量,
不是很科學,圖個方便。事件標志組不是以事件塊為基礎的,不過原理也差不多)
的基礎。
os_task.c  任務管理代碼。
os_flag.c
os_mbox.c
os_mutex.c
os_q.c
os_sem.c  各個同步量管理代碼。
os_mem.c  內存管理代碼。
os_time.c  時間管理代碼,主要做各種延時。
os_tmr.c
定時器管理代碼,這部分代碼時從V2.81版才開始有的,邵老師的書講的是
V2.55版的代碼,是沒有這部分內容的。如果前面的代碼都理解的話,這部分代碼
也是不難理解的。一個定時器大體由3 部分組成:定時時間,回調函數和屬性。當
定時時間到了的話,就進行一次回調函數的處理,定時器屬性說明定時器是周期性
的定時還是只做一次定時。如果用戶使能了OS_TMR_EN,ucosii 會在內部創建一
個定時器任務,負責處理各個定時器。這個任務一般應該由硬件定時器的中斷函數
中調用 OSTmrSignal()去激活。所以從本質上說os_tmr.c中的定時器是由一個硬件
定時器分化出來的。
默認情況下是由 SysTick中斷里通過 OSTimeTickHook()去激活定時器任務的。  
移植相關文件
os_cpu.h:       進行數據類型定義,處理器相關代碼和幾個函數原型。
os_cpu_c.c:     定義一些用戶hook 函數。
os_cpu_a.asm: 移植需要用匯編代碼完成的函數,主要就是任務切換函數。
os_dbg.c:       內核調試相關數據和函數,可以不改。
ucosii 內核就介紹到這里。
ucosii在stm32 上的移植詳解 3  
移植詳解1和2 中主要講了移植需要用到的基礎知識,本文則對具體的移植過
程進行介紹。
首先從 micrium網站上下載官方移植版本(編譯器使用ARM/Keil的,V2.86 版
本,V2.85有問題)。
    下載地址:micrium點com/page/downloads/ports/st/stm32
    解壓縮后得到如下文件夾和文件:
    Micrium\
       AppNotes
       Licensing
       Software
       ReadMe.pdf
AppNotes包含ucosii移植說明文件。這兩個文件中我們僅需關心
Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf。因
為這個文件對ucosii 在CM3內核移植過程中需要修改的代碼進行了說明。
Licensing包含ucosii使用許可證。
Software 下有好幾個文件夾,在本文的移植中僅需關心uCOS-II即可。
CPU: stm32 標準外設庫
EvalBoards: micrium 官方評估板相關代碼
uc-CPU: 基于 micrium官方評估板的 ucosii 移植代碼
uC-LCD:micrium官方評估板LCD驅動代碼
uc-LIB: micrium官方的一個庫代碼
uCOS-II: ucosii 源代碼
uC-Probe: 和uC-Probe 相關代碼
ReadMe.pdf就不說了。
好了,官方的東西介紹完了,該我們自己建立工程著手移植了。關于建立工程,
并使用stm32 標準外設庫在我之前的文章《stm32標準外設庫使用詳解》已有介紹,
這里請大家下載其中模板代碼(http://download.csdn.net/source/3448543),本文
的移植是基于這個工程的。
建立文件夾
template\src\ucosii
template\src\ucosii\src
template\src\ucosii\port; 把 Micrium\Software\uCOS-II\Source 下的文件拷貝至template\src\ucosii\src;
把Micrium\Software\uCOS-II\Ports\ARM-Cortex-M3\Generic\RealView下的文
件拷貝至 template\src\ucosii\port;
ucosii\src下的代碼是ucosii 中無需修改部分
ucosii\port下的代碼是移植時需要修改的。為防止對源碼的誤改動造成移植失
敗,可以把ucosii\src下的代碼文件設為只讀。
這里根據AN-1018.pdf和移植詳解1、2 中介紹的移植基礎知識,對ucosii\port
下的代碼解釋一下。
os_cpu.h
#ifdef   OS_CPU_GLOBALS
#define  OS_CPU_EXT
#else
#define  OS_CPU_EXT  extern
#endif
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
就不解釋了。
typedef unsigned int OS_STK;  
typedef unsigned int OS_CPU_SR;
因為CM3是32位寬的,所以OS_STK(堆棧的數據類型)被類型重定義為
unsigned int。
因為CM3的狀態寄存器(xPSR)是32 位寬的,因此OS_CPU_SR 被類型重
定義為unsigned int。OS_CPU_SR 是在 OS_CRITICAL_METHOD方法 3中保存
cpu 狀態寄存器用的。在 CM3 中,移植 OS_ENTER_CRITICAL(),
OS_EXIT_CRITICAL()選方法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
   具體定義宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),其中
OS_CPU_SR_Save()和OS_CPU_SR_Restore()是用匯編代碼寫的,代碼在
os_cpu_a.asm 中,到時再解釋。
#define  OS_STK_GROWTH        1
CM3中,棧是由高地址向低地址增長的,因此OS_STK_GROWTH 定義為1。  
#define OS_TASK_SW() OSCtxSw()
   定義任務切換宏,OSCtxSw()是用匯編代碼寫的,代碼在os_cpu_a.asm中,到
時再解釋。
#if OS_CRITICAL_METHOD == 3                        
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);
  
void       OS_CPU_SysTickHandler(void);
void       OS_CPU_SysTickInit(void);
INT32U     OS_CPU_SysTickClkFreq(void);
申明幾個函數,這里要注意最后三個函數需要注釋掉,為什么呢?
OS_CPU_SysTickHandler()定義在os_cpu_c.c中,是 SysTick 中斷的中斷處
理函數,而stm32f10x_it.c,中已經有該中斷函數的定義SysTick_Handler(),這里
也就不需要了,是不是很奇怪官方移植版為什么會這樣弄吧,后面我會解釋的。
OS_CPU_SysTickInit()定義在os_cpu_c.c 中,用于初始化 SysTick定時器,它
依賴于 OS_CPU_SysTickClkFreq(),而此函數我們自己會實現,所以注釋掉。
OS_CPU_SysTickClkFreq()定義在BSP.C (Micrium\Software\EvalBoards)中,
而本文移植中并未用到BSP.C,后面我們會自己實現,因此可以把它注釋掉。

os_cpu_c.c
ucosii 移植時需要我們寫 10個相當簡單的C函數。
OSInitHookBegin()
OSInitHookEnd()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTaskStkInit()
OSTaskSwHook()
OSTCBInitHook()
OSTimeTickHook()
這些函數除了OSTaskStkInit(),都是一些hook 函數。這些hook 函數如果不使
能的話,都不會用上,也都比較簡單,看看就應該明白了,所以就不介紹。
下面就說一說OSTaskStkInit()。說之前還是得先說一下任務切換,因為初始化
任務堆棧,是為任務切換服務的。代碼在正常運行時,一行一行往下執行,怎么才
能跑到另一個任務(即函數)執行呢?首先大家可以回想一下中斷過程,當中斷發
生時,原來函數執行的地方(程序計數器PC、處理器狀態寄存器及所有通用寄存
器,即當前代碼的現場)被保存到棧里面去了,然后開始取中斷向量,跑到中斷函
數里面執行。執行完了呢,想回到原來函數執行的地方,該怎么辦呢,只要把棧中
保存的原來函數執行的信息恢復即可(把棧中保存的代碼現場重新賦給cpu的各個
寄存器),一切就都回去了,好像什么事都沒發生一樣。這個過程大家應該都比較
熟悉,任務切換和這有什么關系,試想一下,如果有3 個函數 foo1(), foo2(), foo3()
像是剛被中斷,現場保存到棧里面去了,而中斷返回時做點手腳(調度程序的作用),
想回哪個回哪個,是不是就做了函數(任務)切換了。看到這里應該有點明白
OSTaskStkInit()的作用了吧,它被任務創建函數調用,所以要在開始時,在棧中作
出該任務好像剛被中斷一樣的假象。(關于任務切換的原理邵老師書中的3.06 節有
介紹)。
那么中斷后棧中是個什么情形呢,<<ARM Cortex-M3 權威指南>>中9.1.1 有介
紹,xPSR,PC,LR,R12,R3-R0被自動保存到棧中的,R11-R4 如果需要保存,
只能手工保存。因此OSTaskStkInit()的工作就是在任務自己的棧中保存cpu的所有
寄存器。這些值里R1-R12都沒什么意義,這里用相應的數字代號(如R1 用
0x01010101)主要是方便調試。
其他幾個:
xPSR = 0x01000000L,xPSR T位(第24位)置1,否則第一次執行任務時
Fault,
PC 肯定得指向任務入口,
R14 = 0xFFFFFFFEL,最低4位為E,是一個非法值,主要目的是不讓使用
R14,即任務是不能返回的。
R0 用于傳遞任務函數的參數,因此等于p_arg。
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)0x01000000L;  /* xPSR        */
*(--stk)  = (INT32U)task;         /* Entry Point */
/* R14 (LR) (init value will cause fault if ever used)*/
*(--stk)  = (INT32U)0xFFFFFFFEL;   
*(--stk)  = (INT32U)0x12121212L;  /* R12 */
*(--stk)  = (INT32U)0x03030303L;  /* R3  */
*(--stk)  = (INT32U)0x02020202L;  /* R2  */
*(--stk)  = (INT32U)0x01010101L;  /* R1  */
*(--stk)  = (INT32U)p_arg;        /* R0 : argument  */
/* Remaining registers saved on process stack */
*(--stk)  = (INT32U)0x11111111L;  /* R11 */
*(--stk)  = (INT32U)0x10101010L;  /* R10 */
*(--stk)  = (INT32U)0x09090909L;  /* R9  */
*(--stk)  = (INT32U)0x08080808L;  /* R8  */
*(--stk)  = (INT32U)0x07070707L;  /* R7  */
*(--stk)  = (INT32U)0x06060606L;  /* R6  */
*(--stk)  = (INT32U)0x05050505L;  /* R5  */
*(--stk)  = (INT32U)0x04040404L;  /* R4  */
return (stk);
}
把 OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()注釋掉。
#define  OS_CPU_CM3_NVIC_ST_CTRL    (*((volatile INT32U *)0xE000E010))  
#define  OS_CPU_CM3_NVIC_ST_RELOAD  (*((volatile INT32U
*)0xE000E014))  
#define  OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U
*)0xE000E018))  
#define  OS_CPU_CM3_NVIC_ST_CAL     (*((volatile INT32U *)0xE000E01C))   
#define  OS_CPU_CM3_NVIC_ST_CTRL_COUNT      0x00010000
#define  OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
#define  OS_CPU_CM3_NVIC_ST_CTRL_INTEN       0x00000002
#define  OS_CPU_CM3_NVIC_ST_CTRL_ENABLE   0x00000001
把上面這些宏定義也注釋掉,因為它們都用于OS_CPU_SysTickHandler(),
OS_CPU_SysTickInit()。
os_cpu_a.asm
這個文件包含著必須用匯編寫的代碼。
    EXTERN  OSRunning    ; External references
    EXTERN  OSPrioCur
    EXTERN  OSPrioHighRdy
    EXTERN  OSTCBCur
    EXTERN  OSTCBHighRdy
    EXTERN  OSIntNesting
    EXTERN  OSIntExit
    EXTERN  OSTaskSwHook
    申明這些變量是在其他文件定義的,本文件只做引用(有幾個好像并未引用,不
過沒有關系)。
    EXPORT  OS_CPU_SR_Save    ; Functions declared in this file
    EXPORT  OS_CPU_SR_Restore
    EXPORT  OSStartHighRdy
    EXPORT  OSCtxSw
    EXPORT  OSIntCtxSw
    EXPORT  OS_CPU_PendSVHandler
    申明這些函數是在本文件中定義的。
NVIC_INT_CTRL   EQU     0xE000ED04   ;中斷控制及狀態寄存器ICSR的地址
NVIC_SYSPRI14   EQU     0xE000ED22   ;PendSV 優先級寄存器的地址 NVIC_PENDSV_PRI  EQU   0xFF   ;PendSV中斷的優先級為255(最低)
NVIC_PENDSVSET   EQU   0x10000000   ;位28為1
定義幾個常量,類似C語言中的#define預處理指令。
OS_CPU_SR_Save
MRS     R0, PRIMASK   ;讀取 PRIMASK 到R0 中,R0為返回值
CPSID   I             ;PRIMASK=1,關中斷(NMI和硬 fault可以響應)
BX      LR            ;返回
OS_CPU_SR_Restore
MSR     PRIMASK, R0   ;讀取 R0到 PRIMASK中,R0為參數
BX      LR            ;返回
OSStartHighRdy()由OSStart()調用,用來啟動最高優先級任務,當然任務必須在
OSStart()前已被創建。
OSStartHighRdy
    ;設置PendSV中斷的優先級 #1
LDR     R0, =NVIC_SYSPRI14    ;R0 = NVIC_SYSPRI14
LDR     R1, =NVIC_PENDSV_PRI  ;R1 = NVIC_PENDSV_PRI
STRB    R1, [R0] ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
;設置PSP為 0 #2
MOVS    R0, #0                ;R0 = 0
MSR     PSP, R0               ;PSP = R0
;設置OSRunning為TRUE
    LDR     R0, =OSRunning        ;R0 = OSRunning
    MOVS    R1, #1                ;R1 = 1
STRB    R1, [R0]              ;OSRunning = 1
;觸發PendSV中斷 #3
LDR     R0, =NVIC_INT_CTRL    ;R0 = NVIC_INT_CTRL
LDR     R1, =NVIC_PENDSVSET   ;R1 = NVIC_PENDSVSET
STR     R1, [R0]   ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
CPSIE   I             ;開中斷   
OSStartHang   ;死循環,應該不會到這里
B       OSStartHang  

#1.PendSV中斷的優先級應該為最低優先級,原因在<<ARM Cortex-M3 權威指南
>>的 7.6節已有說明。
#2.PSP 設置為 0,是告訴具體的任務切換程序(OS_CPU_PendSVHandler()),
這是第一次任務切換。做過切換后PSP就不會為 0 了,后面會看到。
#3.往中斷控制及狀態寄存器ICSR(0xE000ED04)第28位寫1即可產生PendSV中
斷。這個<<ARM Cortex-M3 權威指南>>8.4.5 其它異常的配置寄存器有說明。
當一個任務放棄 cpu 的使用權,就會調用OS_TASK_SW()宏,而
OS_TASK_SW()就是 OSCtxSw()。OSCtxSw()應該做任務切換。但是在CM3 中,
所有任務切換都被放到PendSV 的中斷處理函數中去做了,因此OSCtxSw()只需簡
單的觸發PendSV 中斷即可。OS_TASK_SW()是由 OS_Sched()調用。
void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR  cpu_sr = 0;
#endif

OS_ENTER_CRITICAL();
    if (OSIntNesting == 0) {
        if (OSLockNesting == 0) {
            OS_SchedNew();
            if (OSPrioHighRdy != OSPrioCur) {
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
                OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
                OSCtxSwCtr++;  
                OS_TASK_SW();    /* 觸發PendSV 中斷 */
            }
        }
    }
    /* 一旦開中斷,PendSV中斷函數會執行(當然要等更高優先級中斷處理完) */
   OS_EXIT_CRITICAL();   
} OSCtxSw
    ;觸發PendSV中斷
    LDR     R0, =NVIC_INT_CTRL    ;R0 = NVIC_INT_CTRL  
    LDR     R1, =NVIC_PENDSVSET   ;R1 = NVIC_PENDSVSET
    STR     R1, [R0]              ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
    BX      LR                    ;返回
    當一個中斷處理函數退出時,OSIntExit()會被調用來決定是否有優先級更高的任
務需要執行。如果有 OSIntExit()對調用OSIntCtxSw()做任務切換。
OSIntCtxSw
    ;觸發PendSV中斷
    LDR     R0, =NVIC_INT_CTRL
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR
    看到這里有些同學可能奇怪怎么OSCtxSw()和 OSIntCtxSw()完全一樣, 事實上,
這兩個函數的意義是不一樣的,OSCtxSw()做的是任務之間的切換,如任務A因為
等待某個資源或是做延時切換到任務B,而OSIntCtxSw()則是中斷退出時,由中斷
狀態切換到另一個任務。由中斷切換到任務時, CPU寄存器入棧的工作已經做完了,
所以無需做第二次了(參考邵老師書的3.10節)。這里只不過由于CM3的特殊機
制導致了在這兩個函數中只要做觸發PendSV中斷即可,具體切換由PendSV 中斷
來處理。
    前面已經說過真正的任務切換是在PendSV中斷處理函數里做的,由于CM3在
中斷時會有一半的寄存器自動保存到任務堆棧里,所以在PendSV 中斷處理函數中
只需保存R4-R11 并調節堆棧指針即可。
PendSV 中斷處理函數偽代碼如下:
OS_CPU_PendSVHandler()
{
        if (PSP != NULL) {
                Save R4-R11 onto task stack;
                OSTCBCur->OSTCBStkPtr = SP;
        }
        OSTaskSwHook();
        OSPrioCur = OSPrioHighRdy;
        OSTCBCur = OSTCBHighRdy;         PSP = OSTCBHighRdy->OSTCBStkPtr;
        Restore R4-R11 from new task stack;
        Return from exception;  
}

OS_CPU_PendSVHandler             ;xPSR, PC, LR, R12, R0-R3 已自動保存
    CPSID   I                    ;任務切換期間需要關中斷
    MRS     R0, PSP              ;R0 = PSP
    ;如果PSP == 0,跳到OS_CPU_PendSVHandler_nosave 執行 #1
    CBZ     R0, OS_CPU_PendSVHandler_nosave   
    ;保存R4-R11 到任務堆棧
    SUBS    R0, R0, #0x20        ;R0 -= 0x20                          
    STM     R0, {R4-R11}         ;保存 R4-R11到任務堆棧
    ;OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, =OSTCBCur        ;R1 = &OSTCBCur
    LDR     R1, [R1]             ;R1 = *R1 (R1 = OSTCBCur)
    STR     R0, [R1]             ;*R1 = R0 (*OSTCBCur = SP) #2                        
OS_CPU_PendSVHandler_nosave
;調用OSTaskSwHook()
PUSH    {R14}                ;保存R14,因為后面要調用函數            
    LDR     R0, =OSTaskSwHook    ;R0 = &OSTaskSwHook  
    BLX     R0                   ;調用OSTaskSwHook()
    POP     {R14}                ;恢復 R14
    ;OSPrioCur = OSPrioHighRdy;
    LDR     R0, =OSPrioCur       ;R0 = &OSPrioCur
    LDR     R1, =OSPrioHighRdy   ;R1 = &OSPrioHighRdy
    LDRB    R2, [R1]             ;R2 = *R1 (R2 = OSPrioHighRdy)
    STRB    R2, [R0]             ;*R0 = R2 (OSPrioCur = OSPrioHighRdy)
    ;OSTCBCur = OSTCBHighRdy;
    LDR     R0, =OSTCBCur        ;R0 = &OSTCBCur      
    LDR     R1, =OSTCBHighRdy    ;R1 = &OSTCBHighRdy
    LDR     R2, [R1]             ;R2 = *R1 (R2 = OSTCBHighRdy)
    STR     R2, [R0]             ;*R0 = R2 (OSTCBCur = OSTCBHighRdy) LDR     R0, [R2]             ;R0 = *R2 (R0 = OSTCBHighRdy), 此時 R0是新任務
的 SP
;SP = OSTCBHighRdy->OSTCBStkPtr #3  
LDM     R0, {R4-R11}   ;從任務堆棧SP恢復R4-R11
ADDS    R0, R0, #0x20  ;R0 += 0x20
MSR     PSP, R0    ;PSP = R0,用新任務的 SP加載PSP
ORR     LR, LR, #0x04 ;確保 LR 位2 為 1,返回后使用進程堆棧 #4  
CPSIE   I                    ;開中斷
BX      LR                   ;中斷返回
    END
#1 如果 PSP == 0,說明OSStartHighRdy()啟動后第一次做任務切換,而任務剛創
建時R4-R11 已經保存在堆棧中了,所以不需要再保存一次了。
#2 OSTCBStkPtr是任務控制塊結構體的第一個變量,所以*OSTCBCur = SP(不是
很科學)就是 OSTCBCur->OSTCBStkPtr = SP;
#3 和#2類似。
#4 因為在中斷處理函數中使用的是 MSP,所以在返回任務后必須使用PSP,所以
LR位2 必須為 1。
os_dbg.c
用于系統調試,可以不管。
需要修改的代碼就介紹到這里,如果還有不明白之處,就再看看AN-1018.pdf,
邵老師的書和<<ARM Cortex-M3權威指南>>。
ucosii在stm32 上的移植詳解 4  
詳解3中有一個問題還沒解釋,就是stm32f10x_it.c 中已經有 SysTick中斷函
數的定義SysTick_Handler(),為什么官方版非要弄個OS_CPU_SysTickHandler()。
答案就在啟動文件上,一般我們自己開發基于stm32芯片的軟件,都會使用標準外
設庫CMSIS中提供的啟動文件,而官方移植的啟動文件卻是自己寫的,在兩個文
件 init.s,vectors.s 中
(Micrium\Software\EvalBoards\ST\STM3210B-EVAL\RVMDK)。init.s 負責進入
main(),vectors.s 設置中斷向量。OS_CPU_SysTickHandler 和
OS_CPU_PendSVHandler就是在vectors.s 中被設置的。
我的移植是使用標準外設庫CMSIS中startup_stm32f10x_hd.s作為啟動文件
的,那該怎么在這個文件中設置OS_CPU_SysTickHandler呢,事實上在
startup_stm32f10x_hd.s文件中,PendSV 中斷向量名為PendSV_Handler,所以
只需用OS_CPU_PendSVHandler把所有出現PendSV_Handler的地方替換掉就可
以了。
那么為什么OS_CPU_SysTickHandler 不用這種方式處理呢,這樣也就不用注
釋 os_cpu.c 中的 OS_CPU_SysTickHandler(),這主要是基于兩個原因:
1. startup_stm32f10x_hd.s盡量少該,能不改就不改。
2. 如果保留OS_CPU_SysTickHandler(),在以后開發過程中,改動
OS_CPU_SysTickHandler()中的內容可能性是非常大的,如果一不小把該文件其他
部分改了造成了問題,這個 bug 就非常難查了,所以我一般移植好后就把ucosii的
這些文件設置為只讀。
    對于上面的原因1,一開始移植時,我曾做過在PendSV_Handler()中調用
OS_CPU_PendSVHandler(),后來發現這樣不行,這是為什么呢?問題出在LR寄
存器上。
PendSV_Handler()
{
        OS_CPU_PendSVHandler();
}
匯編出來的代碼會是這樣:
PendSV_Handler PROC
   PUSH     {r4,lr}
   BL       OS_CPU_PendSVHandler
   POP      {r4,pc}
ENDP 這樣在進入OS_CPU_PendSVHandler之后,LR 寄存器中存放的是指令 POP
{r4,pc}的地址+1。在 OS_CPU_PendSVHandler 中的ORR LR, LR, #0x04 就不會
起作用,也就無法使用PSP,移植因此失敗。其實在AN-1018.pdf的 3.04.06中也
有強調OS_CPU_PendSVHandler必須被放置在中斷向量表中。 一開始我也沒注意。  
到這里移植的大部分工作都做完了,下面剩下的就是把工程配置好,SysTick
中斷處理好。
在工程中建立ucosii 組,把 ucosii下的文件都加進該組。這里別忘了把
os_cpu_a.asm 加入。
    在工程的Options中,c/c++選項卡的 Include Paths 中添
加.\src\ucosii\src;.\src\ucosii\port。
    編譯工程,會發現缺少app_cfg.h 和 os_cfg.h 文件,app_cfg.h是用來配置應用
軟件的,主要是任務的優先級和堆棧大小,中斷優先級等信息。目前還沒有基于
ucosii 開發應用軟件,所以只需在include 文件夾中創建一個空的app_cfg.h 文件即
可。os_cfg.h 是用來配置ucosii 系統的。拷貝
Micrium\Software\EvalBoards\ST\STM3210B-EVAL\RVMDK\OS-Probe\os_cfg.h
到 template\include,對其做如下修改:
#define OS_APP_HOOKS_EN           0
#define OS_DEBUG_EN               0
#define OS_EVENT_MULTI_EN         0
#define OS_SCHED_LOCK_EN          0
#define OS_TICK_STEP_EN           0
#define OS_TASK_CHANGE_PRIO_EN    0  
#define OS_TASK_QUERY_EN          0  
#define OS_TASK_STAT_EN           0
#define OS_TASK_STAT_STK_CHK_EN   0
#define OS_TASK_SUSPEND_EN        0  
#define OS_FLAG_EN                0
#define OS_MBOX_EN                0  
#define OS_TIME_DLY_HMSM_EN       0  
#define OS_TIME_DLY_RESUME_EN     0
#define OS_TIME_GET_SET_EN        0
#define OS_TIME_TICK_HOOK_EN      0
所做的修改主要是把一些功能給去掉,減少內核大小,也利于調試。等移植完
成后,如果需要該功能,再做開啟。     接下來就剩下處理好SysTick中斷和啟動任務了。SysTick是系統的“心跳”,本
質上來說就是一個定時器。先把原來main.c中的內容刪除,添加如下代碼:
#include "ucos_ii.h"
#include "stm32f10x.h"
static OS_STK startup_task_stk[STARTUP_TASK_STK_SIZE];
static void systick_init(void)
{
        RCC_ClocksTypeDef rcc_clocks;
        RCC_GetClocksFreq(&rcc_clocks);
        SysTick_Config(rcc_clocks.HCLK_Frequency / OS_TICKS_PER_SEC);
}
static void startup_task(void *p_arg)
{
        systick_init();     /* Initialize the SysTick. */
#if (OS_TASK_STAT_EN > 0)
        OSStatInit();      /* Determine CPU capacity. */
#endif
/* TODO: create application tasks here */
         
        OSTaskDel(OS_PRIO_SELF);
}

int main(void)
{
        OSInit();
        OSTaskCreate(startup_task, (void *)0,
              &startup_task_stk[STARTUP_TASK_STK_SIZE - 1],
              STARTUP_TASK_PRIO);
         OSStart();
         return 0;
}
systick_init()用來初始化并啟動 SysTick 定時器。
     RCC_GetClocksFreq()用來獲取系統時鐘。
     SysTick_Config()初始化并使能SysTick定時器。      這里要注意的是 OS_TICKS_PER_SEC,它是每秒鐘的 ticks數,如果為1000,
就是1s中1000 個 ticks,也就是說 1ms就會產生一個SysTick中斷。系統的時間
片為1ms。
    在邵老師的書中3.11 節已有明確說明,必須在調用OSStart()之后,才能開啟時
鐘節拍器(SysTick)。一般會把它放在第一個任務(啟動任務)中。
    startup_task()用來創建其他應用任務,創建完其他任務后,就會自己刪除自己。  
    文件中的STARTUP_TASK_STK_SIZE,STARTUP_TASK_PRIO 需要在
app_cfg.h中定義。代碼如下:
/* task priority */
#define STARTUP_TASK_PRIO                          4
/* task stack size */
#define STARTUP_TASK_STK_SIZE                  80
在 stm32f10x_it.c 中,還需要添加SysTick中斷的處理代碼:
void SysTick_Handler(void)
{  
        OSIntEnter();
        OSTimeTick();
        OSIntExit();
}
    這個代碼是仿照OS_CPU_SysTickHandler()中代碼的,在邵老師書的 3.11節亦
有說明。這里就不解釋。
    至此ucosii在stm32 上的移植已全部完成。
ucosii在stm32 上的移植詳解 5  

詳解1-4 把移植過程都已經介紹了。接下來的工作是驗證移植是否ok 以及如何
基于移植好的ucosii 開發應用程序。前一個問題可以說是后一個問題的特殊情況,
一般我們會創建兩個簡單的任務,看看任務切換是否成功來驗證移植是否ok,因為
任務切換可以說是ucosii 最核心的功能。

任務代碼(main.c):
static void task1(void *p_arg)
{
        for (;;)
        {
                led_on(LED_0);
                OSTimeDly(500);
                led_off(LED_0);
                OSTimeDly(500);   
        }
}
static void task2(void *p_arg)
{
        for (;;)
        {
                led_on(LED_1);
                OSTimeDly(500);
                led_off(LED_1);
                OSTimeDly(500);
        }
}
在 startup_task()創建任務:
err = OSTaskCreate(task1, (void *)0,
                   &task1_stk[TASK1_STK_SIZE-1], TASK1_PRIO);        
err = OSTaskCreate(task2, (void *)0,
                   &task2_stk[TASK2_STK_SIZE-1], TASK2_PRIO); 把任務的堆棧大小和優先級寫入app_cfg.h,定義任務堆棧,編譯調試。
在任務中打斷點,用模擬器調試可以發現已經可以做任務切換了。如果有板子,
燒到板子中運行,可以看到兩個燈會以1Hz 的頻率閃爍。
可以認為移植初步成功,內核其他功能有待在應用中繼續驗證。
如何基于移植好的ucosii 開發應用程序呢?
    開發應用程序大部分都是為了處理或控制一個真實的物理系統,而真實的物理系
統往往都是模擬系統,為了方便計算機處理,首先需要對系統做離散化處理。針對
ucosii,離散化過程是通過系統“心跳”(SysTick)來實現的。一般應用程序都有多
個任務(不多任務誰用ucosii 啊),任務可以分為周期任務和非周期任務。周期任
務是周期性循環地處理事情的任務,而非周期任務一般是某個條件觸發才執行的任
務。這里有一個問題,SysTick的時間是多少合適。SysTick 的時間一般取周期性任
務中周期最短的時間值。譬如說,系統里有3個周期性任務:系統主任務(如處理
pid等,任務周期4ms),鍵盤掃描任務(任務周期16ms),通信任務(任務周期
128ms),SysTick時間就取4ms。當然在SysTick 時間較小時,要注意系統負荷
問題,這時最好測一下cpu 使用率及各個任務的時間等。
    周期性任務的開發套路是怎么樣的呢?看看定時器任務的做法就知道了,代碼在
os_tmr.c。首先在 OSTmr_Init()中初始化OSTmrSemSignal,然后 OSTmr_Task()
任務會一直等待OSTmrSemSignal,等 到OSTmrSemSignal 后去處理各個定時器。
那么誰在釋放OSTmrSemSignal呢?OSTmrSignal(),這個函數要求放在一定頻率
的時鐘中斷里,默認是在SysTick中斷中(如果使能OS_TIME_TICK_HOOK_EN)。
好了,現在我們可以總結總結周期性任務的一般套路了。
首先在任務初始化函數中初始化一個信號量(一般會用信號量),偽代碼如下:
void task_init(void)
{
        task_sem = OSSemCreate(0);
}
在任務中等待信號量
void task (void *p_arg)
{
        for (;;)
        {
                OSSemPend(task_sem, 0, &err);                 /* TODO: task handle here */
        }
}
周期性的釋放信號量  
OSSemPost(task_sem);
    對于上面所說系統主任務,OSSemPost(task_sem)可以放在SysTick_Handler()
中。所以一般來說 OS_CPU_SysTickHandler()改動的可能性是非常大的。
    非周期任務的開發套路又是怎樣的呢?其實和周期性任務是差不多的,只是信號
量不是周期性地釋放,而是按需釋放。
    其他內核功能就不多介紹了,大家按需使用,不是很難。
    該移植代碼在我自己開發的一個小玩意上已得到一段時間的驗證,未發現問題。
但由于水平所限,并不敢保證該移植是沒有任何問題的,殷切希望大家批評指正。

完整的pdf格式文檔51黑下載地址:
UCos-ii_在STM32上的移植詳解.pdf (326.01 KB, 下載次數: 36)


評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享淘帖 頂1 踩
回復

使用道具 舉報

沙發
ID:5203 發表于 2018-11-3 09:27 | 只看該作者
謝謝分享
回復

使用道具 舉報

板凳
ID:138270 發表于 2019-4-6 16:21 | 只看該作者
謝謝分享
回復

使用道具 舉報

地板
ID:138270 發表于 2019-4-6 16:22 | 只看該作者
正在學習,謝謝分享
回復

使用道具 舉報

5#
ID:138270 發表于 2019-4-6 16:25 | 只看該作者
謝謝分享,正在學習
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 天天操人人干 | 在线观看中文字幕视频 | 久久久精 | 亚洲成人精品免费 | 亚洲国产一区在线 | 91视频网 | 成人免费高清 | 欧美精品v国产精品v日韩精品 | 国产成人叼嘿视频在线观看 | 中文字幕 欧美 日韩 | 午夜久久久久 | 国产xxx在线观看 | 亚洲网在线| 久久久国产一区 | 啪啪毛片 | 国产在线精品一区二区三区 | www.色综合 | 久久一区二区三区免费 | 久久艹免费视频 | 免费观看www7722午夜电影 | 欧美a∨ | 色视频在线免费观看 | 欧美婷婷 | 日本黄色短片 | 久久中文一区二区 | 中文字幕在线免费观看 | av网站免费在线观看 | 亚洲一一在线 | av无遮挡| 国产黄色网 | 欧美日韩在线精品 | 二区在线观看 | 无码一区二区三区视频 | 亚洲不卡在线观看 | 男女性毛片 | 久久精品免费一区二区 | 国产四虎 | 亚洲精品乱码久久久久久按摩 | 国产成人免费 | 日韩视频福利 | 国产精品视频一区二区三 |