找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開始

搜索
查看: 5156|回復(fù): 2
打印 上一主題 下一主題
收起左側(cè)

CMSIS_RTOS_Tutorial自譯中文版

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:349192 發(fā)表于 2018-6-11 09:49 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
一.序言
本資料是Trevor Martin編寫的《The Designers Guide to the Cortex-M Processor Family》的摘要,并得到Elsevier的再版許可。查詢更多細(xì)節(jié),請(qǐng)到本資料尾部進(jìn)階章節(jié)。
本資料著力于介紹RTX,RTX可運(yùn)行在基于Cortex-M構(gòu)架的微控制器上。尤其,RTX符合CMSIS標(biāo)準(zhǔn)。CMSIS全稱"Cortex Microcontroller Interface Standard",定義了基于Cortex-M構(gòu)架的微控制器標(biāo)準(zhǔn)的RTOS Api。CMSIS RTOS Api提供基于RTOS開發(fā)的接口,掌握后可跨多系列微控制器使用。另外,CMSIS RTOS Api也為高級(jí)應(yīng)用(如Java虛擬機(jī),UML)等提供標(biāo)準(zhǔn)接口。同時(shí),CMSIS RTOS Api也是不依賴于硬件層的標(biāo)準(zhǔn)接口,支持代碼重復(fù)使用。
作為新手,適應(yīng)RTOS需要大量的練習(xí),但RTOS的便利性會(huì)使使用者再也不想回到裸板程序。
1.1.起步-安裝工具

要運(yùn)行本資料中的示例代碼,必須安裝MDK-ARM工具鏈?稍谌缦碌刂废螺d最新版本,并運(yùn)行安裝程序。
http://www.keil.com/mdk5/install
該安裝程序是MDK-ARM的核心,包括IDE,編譯/鏈接工具和基本調(diào)試模塊。不包括對(duì)基于Cortex-M構(gòu)架微控制器的具體型號(hào)支持,要支持具體的型號(hào)需要下載"Device Family Pack"(集合了啟動(dòng)文件,F(xiàn)lash編程算法和調(diào)試支持)。
本資料中的練習(xí),均基于STM32F103RB微控制器,所以需要安裝對(duì)應(yīng)的"Device Family Pack"。
初次安裝完畢,Pack Installer會(huì)自動(dòng)啟動(dòng)。也可通過工具欄啟動(dòng),如圖所示:
在Pack Installer選擇相應(yīng)的模塊安裝,Pack Installer自動(dòng)下載并安裝。
1.2.安裝例程

本資料中涉及到的例程也被做成CMSIS pack下載。在Pack Installer中雙擊Hitex.CMSIS_RTOS_Turorial.xxxx.pack file即可自動(dòng)安裝。
安裝完成后界面如圖:
1.3.硬件需求

不需要硬件支持!
Keil工具鏈包含基于Cortex-M構(gòu)架微控制器的模擬器,并可完全模擬其模型(包括CPU和外設(shè))。這就意味著可以在debug模式下模擬運(yùn)行。

二.綜述
學(xué)習(xí)本資料,分三個(gè)步驟:
  • 首先建立一個(gè)基于Cortex-M構(gòu)架微控制器的RTOS工程,并運(yùn)行起來;
  • 進(jìn)一步深入RTOS每個(gè)細(xì)節(jié),體驗(yàn)RTOS對(duì)應(yīng)用程序的貢獻(xiàn)和閃光點(diǎn);
  • 對(duì)RTOS有個(gè)整體認(rèn)識(shí)后,深入探討如何配置RTOS選項(xiàng);

對(duì)于沒有接觸過RTOS的新手來說,以下兩點(diǎn)需要克服:

  • 進(jìn)程(或者叫任務(wù)),重點(diǎn)在理解進(jìn)程的運(yùn)行原理;
  • 進(jìn)程見通訊,重點(diǎn)是理解進(jìn)程間的同步通訊;

2.1.進(jìn)入RTOS的第一步

  • RTOS的核心是調(diào)度器(支持輪換、搶占和協(xié)同多任務(wù)),時(shí)間和內(nèi)存管理服務(wù)。進(jìn)程間通訊由額外模塊如信號(hào)、信號(hào)量、互斥量、消息隊(duì)列、消息郵箱等支持完成。而,中斷則通過特權(quán)進(jìn)程由內(nèi)核調(diào)度。


2.2導(dǎo)入CMSIS-RTOS Api

  • 添加頭文件 <cmsis_os.h>即可調(diào)用CMSIS_RTOS Api,如下:
    #include <cmsis_os.h>
    該頭文件作為CMSIS-RTOS標(biāo)準(zhǔn)文件。對(duì)于符合CMSIS-RTOS標(biāo)準(zhǔn)的Keil 內(nèi)置RTX是默認(rèn)的Api。其他RTOS應(yīng)該會(huì)包含其特有Api,但只要支持CMSIS_RTOS,即可通過此方式引入。

2.3進(jìn)程

  • 標(biāo)準(zhǔn)C語言的最小程序塊是函數(shù)(函數(shù)被其他代碼并完成某些特定的運(yùn)算)。在CMSIS-RTOS中,基本代碼單元是進(jìn)程。進(jìn)程與函數(shù)類似,但也有一些不同。
    函數(shù)示例: 進(jìn)程示例:
    Unsigned int fun(void) void thread(void)
    { {
    .... ......
    Return(ch); while(1){........}
    } }
    最大的區(qū)別是,函數(shù)總歸要返回到被調(diào)用處,而進(jìn)程則是無限循環(huán),不會(huì)主動(dòng)結(jié)束。
    RTOS程序有一定數(shù)量的進(jìn)程組成,在調(diào)度器控制下運(yùn)行。調(diào)度器使用Systick中斷生成時(shí)間片,并以時(shí)間片為單位分配各個(gè)進(jìn)程的運(yùn)行時(shí)間。如,進(jìn)程1運(yùn)行5ms,而后調(diào)度器將CPU分配給進(jìn)程2一個(gè)相似的時(shí)間段,然后將cpu分別給進(jìn)程3,......。宏觀來看,好像各個(gè)進(jìn)程同步運(yùn)行。
    在概念層面,程序包含數(shù)個(gè)同步運(yùn)行的進(jìn)程,而每個(gè)進(jìn)程則是完成特定功能的獨(dú)立代碼段。進(jìn)程代碼的獨(dú)立性,使設(shè)計(jì)代碼、測試可限制在進(jìn)程內(nèi)進(jìn)行,進(jìn)而組合各個(gè)進(jìn)程完成程序設(shè)計(jì),這就使進(jìn)程利于面向?qū)ο蟪绦蛟O(shè)計(jì)。同樣道理,進(jìn)程可將調(diào)試局限在進(jìn)程內(nèi),也更利于調(diào)試。后續(xù)可發(fā)現(xiàn),進(jìn)程對(duì)提高代碼復(fù)用性同樣有利。
    進(jìn)程在創(chuàng)建時(shí),系統(tǒng)自動(dòng)分配進(jìn)程ID,用作進(jìn)程的標(biāo)識(shí),如下:
    osThreadId    id1,id2,id3
    完成進(jìn)程切換,需要制定硬件定時(shí)器作為RTOS時(shí)間片參考,并消耗一定的代碼開銷。當(dāng)進(jìn)程切換時(shí),系統(tǒng)需要保存正在運(yùn)行的進(jìn)程信息,同時(shí)加載將要運(yùn)行的進(jìn)程的信息,統(tǒng)稱為"任務(wù)切換時(shí)間",它是評(píng)估RTOS的重要指標(biāo)。進(jìn)程信息保存在進(jìn)程控制塊中。


2.4創(chuàng)建進(jìn)程

  • 一個(gè)創(chuàng)建進(jìn)程的示例:
    Void thread1(void const *parm);
    osThreadDef(thread1,osPriorityNormal, 1, 0);
    優(yōu)先級(jí) 實(shí)例數(shù) stacksize(0默認(rèn)size)
    osThreadId thread1_id = osThreadCreate(osThread(thread1),NULL);
    Void thread1(void const *parm)
    {
    //init code
    While(1) //thread body
    {
    ......
    }
    }

2.5進(jìn)程優(yōu)先級(jí)和進(jìn)程管理

  • 一經(jīng)創(chuàng)建,系統(tǒng)分配進(jìn)程ID,進(jìn)程ID是管理進(jìn)程的標(biāo)識(shí)。
    進(jìn)程有優(yōu)先級(jí)特性,如圖:
    管理進(jìn)程包括設(shè)置優(yōu)先級(jí),讀取優(yōu)先級(jí),進(jìn)程消亡等,Api如下:
    osStatus osThreadSetPriority(threadID,priority);
    osPriority osThreadGetPriority(threadID);
    osStatus osThreadTerminate(threadID);

2.6多實(shí)例進(jìn)程

  • 進(jìn)程支持多實(shí)例,如Ex4 Multiple Instance,代碼摘錄:
    void Led_Switch(void const *argument)
    {
    LED_ON((uint32_t)argument);
    Delay(500);
    LED_OFF((uint32_t)argument);
    Delay(500);
    }
    osThreadDef(osThread(Led_Swtich),osPriorityNormal,2,0);
    osThreadId id1 = osThreadCreate(osThread(Led_Swtich),(void *)1);
    osThreadId id2 = osThreadCreate(osThread(Led_Swtich),(void *)2);

2.7開始RTOS

默認(rèn),RTOS自main函數(shù)起接管系統(tǒng)調(diào)度,所以main函數(shù)是第一個(gè)運(yùn)行中進(jìn)程。一旦進(jìn)入main函數(shù),首先調(diào)用osKernelInitialize()停用調(diào)度,以爭取時(shí)間完成硬件初始化(如GPIO配置,USART配置等)和創(chuàng)建需要的進(jìn)程或其他RTOS組件,而后調(diào)用osKernelStart()將系統(tǒng)控制權(quán)交還給RTOS,如下代碼示例:
Void main(void)
{
osKernelInitialize();
//user code
Init_thread();
osKernelStart();
}
上例中,main進(jìn)程在完成進(jìn)程創(chuàng)建后運(yùn)行到"}"時(shí)消亡,這在RTOS中是不推薦的。Main函數(shù)也可作為一個(gè)進(jìn)程,并通過ID管理,如下:
osThreadId main_id;
Void main(void)
{
osKernelInitialize();
//user code
main_id = osThreadGetId(); //返回當(dāng)前進(jìn)程ID
Init_thread();
osKernelStart();
While(1) //thread body
{
}
}
三.時(shí)間管理RTOS提供基本的時(shí)間管理組件。
3.1延時(shí)函數(shù)
事件組件中最基本的服務(wù)就是延時(shí)函數(shù),在應(yīng)用程序中可直接調(diào)用延時(shí)函數(shù),非常方便。
插曲:盡管RTOS內(nèi)核約5K左右,相比于非RTOS系統(tǒng)中延時(shí)循環(huán)的無用代碼消耗,RTOS還是優(yōu)勢明顯,這就是RTOS出現(xiàn)的原因。
函數(shù)原型:void osDelay(uint32_t millisec)
調(diào)用延時(shí)函數(shù)的進(jìn)程會(huì)進(jìn)入WAIT_DELAY狀態(tài)并持續(xù)延時(shí)函數(shù)制定的時(shí)間(millisec),調(diào)度器轉(zhuǎn)向其他READY狀態(tài)進(jìn)程。延時(shí)結(jié)束,進(jìn)程進(jìn)入READY狀態(tài),等待調(diào)度器調(diào)度。
3.2等待事件
除了等待制定的時(shí)間,osWait也可中斷進(jìn)程并使進(jìn)程進(jìn)入等待狀態(tài)直至被重新觸發(fā)(觸發(fā)時(shí)間可以是信號(hào)、信號(hào)量、消息等),并且osWait同樣支持指定延時(shí)的周期。
函數(shù)原型:osStatus osWait(uint32_t millisec)
注意:keil RTX不支持此節(jié)內(nèi)容。
3.3虛擬定時(shí)器
CMSIS-RTOS支持虛擬定時(shí)器,虛擬定時(shí)器向下計(jì)數(shù),溢出時(shí)運(yùn)行用戶定義的call-back函數(shù)。虛擬定時(shí)器可以定位為單次和循環(huán)模式,步驟如下:
  • 定義回調(diào)函數(shù)(call-back);
  • 定義定時(shí)器結(jié)構(gòu)體;
  • 創(chuàng)建定時(shí)器;
  • 啟動(dòng)定時(shí)器;

如Ex 6 Virtual Timers代碼摘錄:
void callback(void const *pram)
{
Switch((uint32_t) pram)
{
case 0:
GPIOB->ODR ^= 0x8;
Break;
case 1:
GPIOB->ODR ^= 0x4;
Break;
case 2:
GPIOB->ODR ^= 0x2;
Break;
case 3:
Break;
}
}
osTimerDef(osTimer(Timer0_handle),callback);
osTimerDef(osTimer(Timer1_handle),callback);
osTimerDef(osTimer(Timer2_handle),callback);
osTimerDef(osTimer(Timer3_handle),callback);
osTimerId timer0 = osTimerCreate(osTimer(Timer0_hanlder),osTimerPeriodic,(void *)0);
osTimerId timer1 = osTimerCreate(osTimer(Timer1_hanlder),osTimerPeriodic,(void *)1);
osTimerId timer2 = osTimerCreate(osTimer(Timer2_hanlder),osTimerPeriodic,(void *)2);
osTimerId timer3 = osTimerCreate(osTimer(Timer3_hanlder),osTimerPeriodic,(void *)3);
osTimerStart(timer0_handle,0x100);
osTimerStart(timer1_handle,0x100);
osTimerStart(timer2_handle,0x100);
osTimerStart(timer3_handle,0x100);
3.4微秒延時(shí)
借用系統(tǒng)中的Systick原始計(jì)數(shù)值可實(shí)現(xiàn)微妙延時(shí)。微妙延時(shí)不觸發(fā)調(diào)度,它僅僅暫停執(zhí)行指定時(shí)間段,遵循如下步驟:
  • 獲取Systick原始計(jì)數(shù)值;
  • 定義延時(shí)時(shí)間段;
  • 等待延時(shí)結(jié)束;

示例代碼如下:
Uint32_t tick,delayPeriod;
tick = osKernelSysTick(); //1
delayPeriod = osKernelTickMicroSec(100); //2
do{
....
}while((osKernelSysTick()-tick) < delayPeriod); //3
3.5空閑進(jìn)程
空閑進(jìn)程在系統(tǒng)沒有可用進(jìn)程時(shí)被調(diào)用,以防止系統(tǒng)沒有進(jìn)程可用。
空閑進(jìn)程定義在RTX_Conf_CM.c中,并允許用戶自定義代碼。通常情況下,空閑進(jìn)程中配置CPU進(jìn)入低功耗狀態(tài)。如此一來,當(dāng)Systick中斷或其他中斷時(shí)喚醒調(diào)度,如有可運(yùn)行進(jìn)程則運(yùn)行進(jìn)程,否則繼續(xù)進(jìn)入低功耗狀態(tài)。
示例代碼:Ex 7 Idle
四.信號(hào)
4.1功能描述

CMSIS-RTOS keil RTX的進(jìn)程支持多達(dá)16個(gè)信號(hào),信號(hào)保存在進(jìn)程控制塊中。當(dāng)進(jìn)程中存在等待信號(hào)時(shí)(不管是單個(gè)等待信號(hào)還是多個(gè)等待信號(hào)),進(jìn)程暫停執(zhí)行直至其他進(jìn)程發(fā)出了被等待的信號(hào)。
調(diào)用信號(hào)等待函數(shù),將觸發(fā)當(dāng)前進(jìn)程中止運(yùn)行并進(jìn)入等待狀態(tài)。處于等待狀態(tài)的進(jìn)程滿足以下兩個(gè)條件時(shí)退出等待,進(jìn)入可被調(diào)度狀態(tài):
  • 等待的信號(hào)被置位;
  • 等待的時(shí)間溢出;

信號(hào)等待函數(shù)原型:osEvent osSignalWait(uint32_t signals,uint32_t millisec)
等待時(shí)間設(shè)置為0fffff,表示始終不溢出;
等待時(shí)間設(shè)置為0時(shí),表示任一信號(hào)置位即可引起中止等待;
其他進(jìn)程可置位或清除等待信號(hào):
Uint32_t osSignalSet(osThreadId thread_id,uint32_t signals)
Uint32_t osSignalClear(osThreadId thread_id,uint32_t signals)
另,調(diào)用osEvent.value.signals,返回值指示當(dāng)前被置位信號(hào)。
4.2例程

"Ex8 Signals"
4.3中斷進(jìn)程

CMSIS-RTOS使用Systick中斷作為系統(tǒng)時(shí)鐘。因Systick中斷的服務(wù)等級(jí)設(shè)定為最低級(jí),當(dāng)中斷服務(wù)程序(ISR)的運(yùn)行時(shí)間超出一個(gè)Systick中斷時(shí),系統(tǒng)中斷將受到影響。
于是,在CMSIS-RTOS中,正確的處理方式是將中斷服務(wù)定義為一個(gè)進(jìn)程,進(jìn)程中等待信號(hào)。而在中斷服務(wù)中,僅僅給中斷進(jìn)程發(fā)送信號(hào)。從而極大縮短中斷服務(wù)程序的長度,轉(zhuǎn)而在線程中執(zhí)行。
示例代碼:
osThreadDef(osThread(isr_thread),osPriorityNormal,1,0);
osThreadId Isr_thread_id = osThreadCreate(osThread(isr_thread),NULL);
中斷線程:void isr_thread(void const *pram)
{
....
While(1)
{
osSignalWait(isrSignal,waitForever);
.....
}
}
中斷服務(wù)程序:void IRQ_Handler(void)
{
osSignalSet(Isr_thread_id,isrSignal);
}
4.4內(nèi)核權(quán)限調(diào)用SVC

CMSIS-RTOS運(yùn)行在unprivilege模式下,當(dāng)需要在進(jìn)程中訪問privilege資源時(shí),有兩種方式:
  • 在配置文件中提升進(jìn)程的權(quán)限至privilege狀態(tài)(如下圖所示),但會(huì)造成所有的進(jìn)程運(yùn)行在privilege模式下,影響系統(tǒng)安全。

參考例程"Ex9 interrupt signal"
  • 在需要privilege權(quán)限時(shí)運(yùn)行"系統(tǒng)級(jí)"代碼。

4.4.1SVC

遵循如下步驟:
  • 新建"系統(tǒng)級(jí)"代碼列表(.s匯編文件),如下圖:
    SVC_Tables.s代碼如下:
       
    AREA    SVC_TABLE, CODE, READONLY
    EXPORT  SVC_Count
    SVC_Cnt         EQU    (SVC_End-SVC_Table)/4
    SVC_Count       DCD     SVC_Cnt
    ; Import user SVC functions here.
    IMPORT  __SVC_1                   //第一個(gè)"系統(tǒng)級(jí)"代碼
    EXPORT  SVC_Table
    SVC_Table
    ; Insert user SVC functions here. SVC 0 used by RTL Kernel.
    DCD     __SVC_1                 ; user SVC function
    SVC_End
    END
    其中__SVC_1就是"系統(tǒng)級(jí)"用戶代碼函數(shù)。
  • 建立"系統(tǒng)級(jí)"代碼與"進(jìn)程級(jí)"代碼接口,示例:
    void __svc(1) init_ADC (void);
    __svc(1)代表SVC_Tables.s中第一個(gè)"系統(tǒng)級(jí)"代碼,同樣如果有多個(gè)應(yīng)用,依次遞增即可。
  • 編寫"系統(tǒng)級(jí)"代碼,如下圖:
    Void __SVC_1 (void)
    {
    ......
    }
    在完成定義后,進(jìn)程中調(diào)用init_ADC()將自動(dòng)執(zhí)行__SVC_1()中的代碼。

五.信號(hào)量
5.1功能描述
與信號(hào)類似,信號(hào)量是兩個(gè)或多個(gè)進(jìn)程同步的方法。
信號(hào)量項(xiàng)是一個(gè)包含多個(gè)信號(hào)的容器。當(dāng)進(jìn)程執(zhí)行到需要信號(hào)量的代碼段(進(jìn)程申請(qǐng)信號(hào)量),如果信號(hào)量中有信號(hào)可用(包含不少于一個(gè)的信號(hào)),則進(jìn)程繼續(xù)執(zhí)行,并且信號(hào)量中的信號(hào)數(shù)自減一。相反,如信號(hào)量中無信號(hào)可用(包含0個(gè)信號(hào)),則進(jìn)程中止執(zhí)行并等待信號(hào)量中的信號(hào)可用。
同時(shí),進(jìn)程中可向信號(hào)量添加信號(hào)數(shù)目,從而引起信號(hào)量中的可用信號(hào)數(shù)增一。
如上圖。假定信號(hào)量初始化為只有一個(gè)可用信號(hào),當(dāng)任務(wù)1提出申請(qǐng)時(shí),信號(hào)量中含有1個(gè)可用信號(hào),則任務(wù)1繼續(xù)執(zhí)行并引起信號(hào)量中的可用信號(hào)為0。此時(shí)任務(wù)2若提出申請(qǐng)因信號(hào)量中無可用信號(hào),任務(wù)2進(jìn)入信號(hào)量等待狀態(tài),直至任務(wù)1釋放信號(hào)量。
可見,進(jìn)程可以釋放信號(hào)給信號(hào)量。
5.2創(chuàng)建信號(hào)量
示例代碼:
osSemaphoreId sem1;
osSemaphoreDef(sem1);
sem1 = osSemaphoreCreate(osSemaphore(sem1),SIX_TOKENS);
定義了一個(gè)含有6個(gè)可用信號(hào)的信號(hào)量sem1。
信號(hào)量初始化后,進(jìn)程中即可申請(qǐng)信號(hào)量,使用函數(shù):
osSemaphoreWait(osSemaphoreId sem_id,uint_32 millisec)
Millisec = 0xffff wait for ever
信號(hào)量使用結(jié)束后,釋放信號(hào)量,使用函數(shù):
osSemaphoreRelease(osSemaphoreId sem_id);
5.3例程
Ex11 Interrupt Signals
5.4使用場景
5.4.1信號(hào)
兩個(gè)線程間同步執(zhí)行是信號(hào)量最基本的應(yīng)用。示例代碼:
osSemaphoreId sem_id;
osSemaphoreDef(sem_id);
void task1(void)
{
Sem_id = osSemaphoreCreate(osSemaphore(sem1),0);
While(1)
{
Fun(A);
osSemaphoreRelease(sem1);
}
}
Void task2(void)
{
While(1)
{
osSemaphoreWait(sem1,osWaitForever);
Fun(B);
}
}
在這個(gè)案例中,F(xiàn)un(A)始終先于Fun(B)執(zhí)行。
5.4.2限額
限額用于限制某些資源的配額。例如,某個(gè)指定的內(nèi)存塊只運(yùn)行指定數(shù)目的應(yīng)用訪問。
如下例程,信號(hào)量初始化為5個(gè)信號(hào),每個(gè)申請(qǐng)信號(hào)量的線程造成信號(hào)自減,當(dāng)獲取信號(hào)量的進(jìn)程為5個(gè)時(shí),后續(xù)申請(qǐng)信號(hào)量的進(jìn)程進(jìn)入等待狀態(tài),直至已獲取配額的進(jìn)程釋放信號(hào)量,代碼例程:
osThreadId sem_id;
osThreadDef(sem1);
Void task1(void)
{
Sem_id = osThreadCreate(osThread(sem1),5);
While(1)
{
osSemaphoreWait(sem1,osWaitForever);
ProcessBuffer();
osSemaphoreRelease(sem1);
}
}
Void task2(void)
{
While(1)
{
osSemaphoreWait(sem1,osWaitForever);
ProcessBuffer();
osSemaphoreRelease(sem1);
}
}
......
例程"Ex12 Multiplex"。
5.4.3互鎖(2個(gè)線程同步)
互鎖是兩個(gè)線程同步的另一種通用模式;ユi確保兩個(gè)線程得到同一互鎖點(diǎn)。如下例程:
osSemaphoreId arrival1,arrival2;
osSemaphoreDef(sem1);
osSemaphoreDef(sem2);
Void task1(void)
{
arrival1 = osSemaphore(osSemephore(sem1),0);
arrival2= osSemaphore(osSemephore(sem2),0);
While(1)
{
FunA1();
osSemaphoreRelease(arrival2);
osSemaphoreWait(arrival1);
FunA2();
}
}
Void task2(void)
{
While(1)
{
FunB1();
osSemaphoreRelease(arrival1);
osSemaphoreWait(arrival2);
FunB2();
}
}
此例程中,確保FunA2()、FunB2()同步執(zhí)行。
5.4.4屏障(多個(gè)線程同步)
屏障是多個(gè)進(jìn)程同步的有效模式,它的總體思路:設(shè)置一個(gè)初始化為0的信號(hào)量作為屏障,并在所有進(jìn)程達(dá)到同步點(diǎn)時(shí)依次釋放屏障中的信號(hào)量,達(dá)到同步執(zhí)行的目的。
例程"Ex14 Barrier"
5.5注意事項(xiàng)
信號(hào)量是RTOS中極端有效的模式。然而,因信號(hào)量可在進(jìn)程中增減甚至銷毀,信號(hào)量中可用配額數(shù)比較難把控,使用時(shí)必須實(shí)時(shí)把控可用配額數(shù)。
六.互斥量6.1功能描述
單從功能來講,互斥量可以看做只含有一個(gè)可用配額且不可被創(chuàng)建和銷毀的特殊信號(hào)量;コ饬恐饕糜诜乐箤(duì)硬件的訪問沖突,比如同一時(shí)刻只能有一個(gè)應(yīng)用訪問串口,否則將造成數(shù)據(jù)混亂。
申請(qǐng)互斥量的進(jìn)程,必須等待互斥量中存在有效配額,否則進(jìn)入等待狀態(tài)。
6.2創(chuàng)建互斥量
創(chuàng)建互斥量與創(chuàng)建信號(hào)量類似,示例代碼如下:
osMutexId    uart_mutex;
osMutexDef(Mutex1);
進(jìn)程中創(chuàng)建互斥量:uart_mutex = osMutexCreate(osMutex(Mutex1));
其他進(jìn)程申請(qǐng)互斥量:osMutexWait(uart_mutex);
使用完畢釋放互斥量:osMutexRelease(uart_mutex);
6.3例程
例程"Ex15 Mutex"
6.4注意事項(xiàng)
互斥量的使用限制多,也更安全,但扔要注意以下內(nèi)容:
  • 使用完畢必須及時(shí)釋放互斥量,否則將造成后續(xù)進(jìn)程無法使用該資源;
  • 調(diào)用ThreadTerminate()函數(shù)消亡進(jìn)程時(shí),必須確保該進(jìn)程沒有占用互斥量,否則將造成后續(xù)進(jìn)程無法使用該資源;

七.數(shù)據(jù)交換信號(hào)、信號(hào)量、互斥量只用于進(jìn)程之間的觸發(fā),但對(duì)進(jìn)程間的數(shù)據(jù)交換無能為力。進(jìn)程間數(shù)據(jù)交換最簡單的方式是全局變量,但即使在簡單的系統(tǒng)中,把握和靈活應(yīng)用全局變量也是不小的挑戰(zhàn),因?yàn)槿肿兞繒?huì)引起一系列不可預(yù)知錯(cuò)誤。
在RTOS中,消息隊(duì)列郵箱隊(duì)列是進(jìn)程間數(shù)據(jù)交互最為有效、安全的方式。
消息隊(duì)列和郵箱隊(duì)列的工作方式基本一樣,唯一的區(qū)別是消息隊(duì)列中傳輸?shù)氖谴粨Q數(shù)據(jù),而郵箱隊(duì)列中傳輸是指向待交換數(shù)據(jù)的指針,如下圖所示:
使用消息隊(duì)列和郵箱隊(duì)列進(jìn)行數(shù)據(jù)交換有如下好處:
  • 規(guī)范進(jìn)程間數(shù)據(jù)交換的接口和緩存,為設(shè)計(jì)子系統(tǒng)提供可能;
  • 規(guī)范進(jìn)程的輸入、輸出,使進(jìn)程獨(dú)立測試、調(diào)試成為可能;

7.1消息隊(duì)列
7.1.1創(chuàng)建消息隊(duì)列
創(chuàng)建消息隊(duì)列,遵循如下步驟:
  • 聲明消息隊(duì)列ID,示例:osMessageQId Q_id;
  • 定義消息隊(duì)列結(jié)構(gòu)體,示例:osMessageQDef(Q1,16_Message_Slots,unsigned int);其中16_Message_Slots指示空間大小為16,unsigned int指示空間類型;
  • 在進(jìn)程中創(chuàng)建消息隊(duì)列,示例:Q_id = osMessageQCreate(osMessageQ(Q1),NULL);
  • 聲明解析消息隊(duì)列數(shù)據(jù)的osEvent類型數(shù)據(jù),示例:osEvent result;
  • 在進(jìn)程中發(fā)送數(shù)據(jù)到消息隊(duì)列,例程:osMessagePut(Q_id,Data,osWaitForever);
  • 在另一進(jìn)程中獲取消息隊(duì)列數(shù)據(jù),例程:result = osMessageGet(Q_id,osWaitForever);result.value.xxx;

其中,osEvent是個(gè)union結(jié)構(gòu)體,如下所示:
Union{
Uint32_t v;
Void *p;
Uint32_t signals;
}value
7.1.2例程
"Ex16 Message queue"
7.2內(nèi)存鏈
7.2.1功能描述
消息隊(duì)列中的數(shù)據(jù)類型可以是數(shù)據(jù)本身,也可以是指向數(shù)據(jù)的指針。
消息隊(duì)列中存儲(chǔ)的數(shù)據(jù)是指向特定內(nèi)存區(qū)域的指針,這樣的進(jìn)程間交換數(shù)據(jù)的方式成為內(nèi)存鏈。
結(jié)構(gòu)體可以達(dá)到規(guī)范化特定內(nèi)存區(qū)域的目的。
7.2.2創(chuàng)建內(nèi)存鏈
創(chuàng)建內(nèi)存鏈,遵循如下步驟:
  1.定義結(jié)構(gòu)體,用于規(guī)范內(nèi)存塊及初始化指針,示例:

Typedef    struct {
Uint8_t led1;
Uint8_t led2;
Uint8_t led3;
Uint8_t led4;
}memory_block_t;
  2.初始化內(nèi)存鏈,示例:

osPoolId pool_id;
osPoolDef(pool_t,ten_blocks,memory_block_t);
在進(jìn)程中創(chuàng)建pool_id = osPoolCreate(osPool(pool_t));
  3.初始化消息隊(duì)列,示例:
osMessageQDef(q1,ten_blocks,memory_block_t);
osMessageQid q_id;
在進(jìn)程中創(chuàng)建:q_id = osMessageQCreate(osMessageQ(q1),NULL);
  4.發(fā)送消息隊(duì)列,示例:
memory_block_t *led = (memory_block_t *)osPoolAlloc(pool_id);
led->led1 = xx;....
osMessagePut(q_id,led,osWaitForever);
  5.讀取消息隊(duì)列,示例:
osEvent evt;
evt =  osMessageGet(q_id,osWaitForever);
memory_block_t *temp = (memory_block_t *)evt.value.p;
  6.使用完畢釋放內(nèi)存鏈:示例:
osPoolFree(pool_id,temp);
7.2.3例程
"Ex16 MemoryPool"
7.3郵箱隊(duì)列

7.3.1功能描述

郵箱隊(duì)列是將內(nèi)存鏈融合到消息隊(duì)列中而成,郵箱隊(duì)列中存儲(chǔ)的同樣是指向特定內(nèi)存區(qū)域的指針。
7.3.2創(chuàng)建郵箱隊(duì)列

同樣采用7.2.2中的結(jié)構(gòu)體作為數(shù)據(jù)基礎(chǔ),Typedef    struct {
Uint8_t led1;
Uint8_t led2;
Uint8_t led3;
Uint8_t led4;
}memory_block_t;
創(chuàng)建郵箱隊(duì)列遵循如下步驟:
1.創(chuàng)建郵箱隊(duì)列,例程:


osMailQDef(MQ_1,ten_blocks,memory_block_t);
osMailQId mq_id;
進(jìn)程中創(chuàng)建,mq_id = osMailQCreate(osMailQ(MQ_1),NULL);
2.發(fā)送數(shù)據(jù),例程:

Memory_block_t *led = (memory_block_t *)osMailAlloc(mq_id);
led->led1 = xx;....
osMailQPut(mq_id,led);
3.讀取數(shù)據(jù),例程:

osEvent evt;
evt = osMailGet(mq_id,osWaitForever);
Memory_block_t *temp = (memory_block_t *)evt.value.p;
4.使用完畢釋放郵箱隊(duì)列,示例:

osMailFree(mq_id,temp);
7.3.3例程

"Ex17 MailQueue"
八.系統(tǒng)配置
掌握前面的內(nèi)容,對(duì)CMSIS-RTOS有了總體的認(rèn)識(shí)。CMSIS-RTOS包括進(jìn)程管理、時(shí)間管理、進(jìn)程間通訊等。
本章著力于討論如何配置系統(tǒng)。CMSIS-RTOS針對(duì)基于Cortex-M構(gòu)架的處理器,提供一個(gè)統(tǒng)一的配置文件,RTX_Conf_CM.c,如下圖:

8.1進(jìn)程參數(shù)

在討論進(jìn)程的相關(guān)章節(jié)中一經(jīng)接受了創(chuàng)建進(jìn)程的基礎(chǔ)知識(shí)。
每個(gè)進(jìn)程,系統(tǒng)分配一塊內(nèi)存空間用作進(jìn)程棧(默認(rèn)200bytes),?臻g在進(jìn)程創(chuàng)建時(shí)指定。
應(yīng)用中最多允許運(yùn)行的進(jìn)程數(shù)可配置。
由于進(jìn)程?臻g、進(jìn)程數(shù)可配置,應(yīng)用中的內(nèi)存需求也可很容易的計(jì)算出來。
8.2內(nèi)核調(diào)試支持
內(nèi)核可配置項(xiàng),包括:
8.2.1追蹤溢出
選擇該項(xiàng)"stack overflow checking",出現(xiàn)進(jìn)程棧溢出RTOS內(nèi)核調(diào)用os_error函數(shù)并進(jìn)入死循環(huán)。該項(xiàng)主要用于調(diào)試階段的問題追蹤,當(dāng)然也可自定義os_error函數(shù)用于最終的應(yīng)用中打印錯(cuò)誤信息,os_error代碼在RTX_CONF_CM.C文件中,源碼:
/* OS Error Codes */
#define OS_ERROR_STACK_OVF      1
#define OS_ERROR_FIFO_OVF       2
#define OS_ERROR_MBX_OVF        3
#define OS_ERROR_TIMER_OVF      4
extern osThreadId svcThreadGetId (void);
/// \brief Called when a runtime error is detected
/// \param[in]   error_code   actual error code that has been detected
void os_error (uint32_t error_code) {
  /* HERE: include optional code to be executed on runtime error. */
  switch (error_code) {
    case OS_ERROR_STACK_OVF:
      /* Stack overflow detected for the currently running task. */
      /* Thread can be identified by calling svcThreadGetId().   */
      break;
    case OS_ERROR_FIFO_OVF:
      /* ISR FIFO Queue buffer overflow detected. */
      break;
    case OS_ERROR_MBX_OVF:
      /* Mailbox overflow detected. */
      break;
    case OS_ERROR_TIMER_OVF:
      /* User Timer Callback Queue overflow detected. */
      break;
    default:
      break;
  }
  for (;;);
}
8.2.2監(jiān)控棧使用率
選擇"stack usage watermark"項(xiàng),oxcc樣式自動(dòng)寫入進(jìn)程棧。運(yùn)行時(shí),watermark用于計(jì)算最大棧內(nèi)存使用率,并在"system and Event viewer"窗口報(bào)告,如下圖所示:
8.2.3用戶定時(shí)器數(shù)
如用戶定時(shí)器數(shù)量與應(yīng)用中使用的虛擬定時(shí)器不符,會(huì)造成os_timer()函數(shù)失效。
8.2.4進(jìn)程運(yùn)行權(quán)限選擇
如"Ex9 interruter signal",進(jìn)程運(yùn)行權(quán)限可配置。
8.3系統(tǒng)時(shí)基
默認(rèn)的系統(tǒng)時(shí)基是Cortex-M中的SysTick定時(shí)器。但,也支持自定義使用其他定時(shí)器作為系統(tǒng)時(shí)基。
8.4時(shí)間片
默認(rèn)的時(shí)間片是5ms。
8.5調(diào)度選項(xiàng)
調(diào)度器支持如下三種調(diào)度模式:
1.搶占式
此模式下,系統(tǒng)中進(jìn)程擁有不同的優(yōu)先級(jí),當(dāng)擁有高優(yōu)先級(jí)的進(jìn)程進(jìn)入"ready"狀態(tài),調(diào)度器轉(zhuǎn)入高優(yōu)先級(jí)進(jìn)程運(yùn)行。
2.輪詢式
此模式下,系統(tǒng)根據(jù)時(shí)間片為每個(gè)進(jìn)程分配運(yùn)行時(shí)間,處于運(yùn)行態(tài)的進(jìn)程在時(shí)間片到來時(shí)觸發(fā)調(diào)度(注意,即使高優(yōu)先級(jí)的進(jìn)程進(jìn)入"ready"狀態(tài)也要等時(shí)間片結(jié)束)。
3.輪詢、搶占式(默認(rèn)狀態(tài))
此模式下,系統(tǒng)根據(jù)時(shí)間片為每個(gè)進(jìn)程分配運(yùn)行時(shí)間,處于運(yùn)行態(tài)的進(jìn)程在時(shí)間片到來時(shí)或高優(yōu)先級(jí)的進(jìn)程進(jìn)入"ready"態(tài)觸發(fā)調(diào)度(注意,高優(yōu)先級(jí)的進(jìn)程進(jìn)入"ready"狀態(tài)將馬上觸發(fā)調(diào)度)。
4.協(xié)同式
此模式下,進(jìn)程擁有相同的優(yōu)先級(jí),有且僅有運(yùn)行態(tài)的進(jìn)程主動(dòng)申請(qǐng)系統(tǒng)調(diào)度才會(huì)引起調(diào)度。
8.6源碼調(diào)試
如果用戶需要源碼級(jí)別的調(diào)試,遵循如下步驟:
  • 新建文本文件,命名為"xxx.ini";
  • "xxx.ini"中添加,SET SRC = <PATH>,其中<PATH>是RTX源碼的文件夾,默認(rèn)是C:\Keil\ARM\pack\arm\cmsis\<version>\cmsis\rots\rtx。
  • 在調(diào)試文件中導(dǎo)入"xxx.ini",如下圖所示:

注:"xxx.ini"中xxx代表任意長度滿足PC操作系統(tǒng)命名規(guī)格的字符串。

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

使用道具 舉報(bào)

沙發(fā)
ID:364014 發(fā)表于 2018-7-21 18:40 | 只看該作者
這是好東西,這個(gè)東東我用,很不錯(cuò)的
回復(fù)

使用道具 舉報(bào)

板凳
ID:364014 發(fā)表于 2018-7-21 18:42 | 只看該作者
應(yīng)該打包做下載點(diǎn)
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

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

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表