當(dāng)前位置:首頁 > 汽車電子1 > 糖果Autosar
[導(dǎo)讀]一.序言?本資料是TrevorMartin編寫的《TheDesignersGuidetotheCortex-MProcessorFamily》的摘要,并得到Elsevier的再版許可。查詢更多細(xì)節(jié),請到本資料尾部進階章節(jié)。本資料著力于介紹RTX,RTX可運行在基于Cortex-M...

一.序言

本資料是Trevor Martin編寫的《The Designers Guide to the Cortex-M Processor Family》的摘要,并得到Elsevier的再版許可。查詢更多細(xì)節(jié),請到本資料尾部進階章節(jié)。


本資料著力于介紹RTX,RTX可運行在基于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也為高級應(yīng)用(如Java虛擬機,UML)等提供標(biāo)準(zhǔn)接口。同時,CMSIS RTOS Api也是不依賴于硬件層的標(biāo)準(zhǔn)接口,支持代碼重復(fù)使用。


作為新手,適應(yīng)RTOS需要大量的練習(xí),但RTOS的便利性會使使用者再也不想回到裸板程序。


1.1.起步-安裝工具


要運行本資料中的示例代碼,必須安裝MDK-ARM工具鏈??稍谌缦碌刂废螺d最新版本,并運行安裝程序。


http://www.keil.com/mdk5/install 


該安裝程序是MDK-ARM的核心,包括IDE,編譯/鏈接工具和基本調(diào)試模塊。不包括對基于Cortex-M構(gòu)架微控制器的具體型號支持,要支持具體的型號需要下載"Device Family Pack"(集合了啟動文件,F(xiàn)lash編程算法和調(diào)試支持)。


本資料中的練習(xí),均基于STM32F103RB微控制器,所以需要安裝對應(yīng)的"Device Family Pack"。


初次安裝完畢,Pack Installer會自動啟動。也可通過工具欄啟動,如圖所示:



在Pack Installer選擇相應(yīng)的模塊安裝,Pack Installer自動下載并安裝。


1.2.安裝例程


本資料中涉及到的例程也被做成CMSIS pack下載。在Pack Installer中雙擊Hitex.CMSIS_RTOS_Turorial.xxxx.pack file即可自動安裝。


安裝完成后界面如圖:



1.3.硬件需求


不需要硬件支持!


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



二.綜述


學(xué)習(xí)本資料,分三個步驟:


  1. 首先建立一個基于Cortex-M構(gòu)架微控制器的RTOS工程,并運行起來;


  2. 進一步深入RTOS每個細(xì)節(jié),體驗RTOS對應(yīng)用程序的貢獻(xiàn)和閃光點;


  3. 對RTOS有個整體認(rèn)識后,深入探討如何配置RTOS選項;


對于沒有接觸過RTOS的新手來說,以下兩點需要克服:



  1. 進程(或者叫任務(wù)),重點在理解進程的運行原理;


  2. 進程見通訊,重點是理解進程間的同步通訊;


2.1.進入RTOS的第一步


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




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


  1. 添加頭文件 即可調(diào)用CMSIS_RTOS API,如下:


    #include


    該頭文件作為CMSIS-RTOS標(biāo)準(zhǔn)文件。對于符合CMSIS-RTOS標(biāo)準(zhǔn)的Keil 內(nèi)置RTX是默認(rèn)的API。其他RTOS應(yīng)該會包含其特有Api,但只要支持CMSIS_RTOS,即可通過此方式引入。


2.3進程Thread


  1. 標(biāo)準(zhǔn)C語言的最小程序塊是函數(shù)(函數(shù)被其他代碼并完成某些特定的運算)。在CMSIS-RTOS中,基本代碼單元是進程。進程與函數(shù)類似,但也有一些不同。


    函數(shù)示例:進程示例:


    Unsigned int fun(void) void thread(void)


    { {


    .... ......


    Return(ch); while(1){........}


    } }


    最大的區(qū)別是,函數(shù)總歸要返回到被調(diào)用處,而進程則是無限循環(huán),不會主動結(jié)束。


    RTOS程序有一定數(shù)量的進程組成,在調(diào)度器控制下運行。調(diào)度器使用Systick中斷生成時間片,并以時間片為單位分配各個進程的運行時間。如,進程1運行5ms,而后調(diào)度器將CPU分配給進程2一個相似的時間段,然后將cpu分別給進程3,......。宏觀來看,好像各個進程同步運行。


    在概念層面,程序包含數(shù)個同步運行的進程,而每個進程則是完成特定功能的獨立代碼段。進程代碼的獨立性,使設(shè)計代碼、測試可限制在進程內(nèi)進行,進而組合各個進程完成程序設(shè)計,這就使進程利于面向?qū)ο蟪绦蛟O(shè)計。同樣道理,進程可將調(diào)試局限在進程內(nèi),也更利于調(diào)試。后續(xù)可發(fā)現(xiàn),進程對提高代碼復(fù)用性同樣有利。


    進程在創(chuàng)建時,系統(tǒng)自動分配進程ID,用作進程的標(biāo)識,如下:


    osThreadId    id1,id2,id3


    完成進程切換,需要制定硬件定時器作為RTOS時間片參考,并消耗一定的代碼開銷。當(dāng)進程切換時,系統(tǒng)需要保存正在運行的進程信息,同時加載將要運行的進程的信息,統(tǒng)稱為"任務(wù)切換時間",它是評估RTOS的重要指標(biāo)。進程信息保存在進程控制塊中。




2.4創(chuàng)建進程


  1. 一個創(chuàng)建進程的示例:


    Void thread1(void const *parm);


    osThreadDef(thread1,osPriorityNormal, 1, 0);


    優(yōu)先級 實例數(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進程優(yōu)先級和進程管理


  1. 一經(jīng)創(chuàng)建,系統(tǒng)分配進程ID,進程ID是管理進程的標(biāo)識。


    進程有優(yōu)先級特性,如圖:




    管理進程包括設(shè)置優(yōu)先級,讀取優(yōu)先級,進程消亡等,Api如下:


    osStatus osThreadSetPriority(threadID,priority);


    osPriority osThreadGetPriority(threadID);


    osStatus osThreadTerminate(threadID);


2.6多實例進程


  1. 進程支持多實例,如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ù)是第一個運行中進程。一旦進入main函數(shù),首先調(diào)用osKernelInitialize()停用調(diào)度,以爭取時間完成硬件初始化(如GPIO配置,USART配置等)和創(chuàng)建需要的進程或其他RTOS組件,而后調(diào)用osKernelStart()將系統(tǒng)控制權(quán)交還給RTOS,如下代碼示例:


Void main(void)


{


osKernelInitialize();


//user code


Init_thread();


osKernelStart();


}


上例中,main進程在完成進程創(chuàng)建后運行到"}"時消亡,這在RTOS中是不推薦的。Main函數(shù)也可作為一個進程,并通過ID管理,如下:


osThreadId main_id;


Void main(void)


{


osKernelInitialize();


//user code


main_id = osThreadGetId(); //返回當(dāng)前進程ID


Init_thread();


osKernelStart();


While(1) //thread body


{


}


}


三.時間管理

RTOS提供基本的時間管理組件。


3.1延時函數(shù)

事件組件中最基本的服務(wù)就是延時函數(shù),在應(yīng)用程序中可直接調(diào)用延時函數(shù),非常方便。


插曲:盡管RTOS內(nèi)核約5K左右,相比于非RTOS系統(tǒng)中延時循環(huán)的無用代碼消耗,RTOS還是優(yōu)勢明顯,這就是RTOS出現(xiàn)的原因。


函數(shù)原型:void osDelay(uint32_t millisec)


調(diào)用延時函數(shù)的進程會進入WAIT_DELAY狀態(tài)并持續(xù)延時函數(shù)制定的時間(millisec),調(diào)度器轉(zhuǎn)向其他READY狀態(tài)進程。延時結(jié)束,進程進入READY狀態(tài),等待調(diào)度器調(diào)度。


3.2等待事件

除了等待制定的時間,osWait也可中斷進程并使進程進入等待狀態(tài)直至被重新觸發(fā)(觸發(fā)時間可以是信號、信號量、消息等),并且osWait同樣支持指定延時的周期。


函數(shù)原型:osStatus osWait(uint32_t millisec)


注意:keil RTX不支持此節(jié)內(nèi)容。


3.3虛擬定時器

CMSIS-RTOS支持虛擬定時器,虛擬定時器向下計數(shù),溢出時運行用戶定義的call-back函數(shù)。虛擬定時器可以定位為單次和循環(huán)模式,步驟如下:


  1. 定義回調(diào)函數(shù)(call-back);


  2. 定義定時器結(jié)構(gòu)體;


  3. 創(chuàng)建定時器;


  4. 啟動定時器;


如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微秒延時

借用系統(tǒng)中的Systick原始計數(shù)值可實現(xiàn)微妙延時。微妙延時不觸發(fā)調(diào)度,它僅僅暫停執(zhí)行指定時間段,遵循如下步驟:


  1. 獲取Systick原始計數(shù)值;


  2. 定義延時時間段;


  3. 等待延時結(jié)束;


示例代碼如下:


Uint32_t tick,delayPeriod;


tick = osKernelSysTick(); //1


delayPeriod = osKernelTickMicroSec(100); //2


do{


....


}while((osKernelSysTick()-tick) < delayPeriod); //3


3.5空閑進程

空閑進程在系統(tǒng)沒有可用進程時被調(diào)用,以防止系統(tǒng)沒有進程可用。


空閑進程定義在RTX_Conf_CM.c中,并允許用戶自定義代碼。通常情況下,空閑進程中配置CPU進入低功耗狀態(tài)。如此一來,當(dāng)Systick中斷或其他中斷時喚醒調(diào)度,如有可運行進程則運行進程,否則繼續(xù)進入低功耗狀態(tài)。


示例代碼:Ex 7 Idle


四.信號Signal


4.1功能描述


CMSIS-RTOS keil RTX的進程支持多達(dá)16個信號,信號保存在進程控制塊中。當(dāng)進程中存在等待信號時(不管是單個等待信號還是多個等待信號),進程暫停執(zhí)行直至其他進程發(fā)出了被等待的信號。



調(diào)用信號等待函數(shù),將觸發(fā)當(dāng)前進程中止運行并進入等待狀態(tài)。處于等待狀態(tài)的進程滿足以下兩個條件時退出等待,進入可被調(diào)度狀態(tài):


  1. 等待的信號被置位;


  2. 等待的時間溢出;


信號等待函數(shù)原型:osEvent osSignalWait(uint32_t signals,uint32_t millisec)


等待時間設(shè)置為0fffff,表示始終不溢出;


等待時間設(shè)置為0時,表示任一信號置位即可引起中止等待;


其他進程可置位或清除等待信號:


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)前被置位信號。


4.2例程


"Ex8 Signals"


4.3中斷進程ISR


CMSIS-RTOS使用Systick中斷作為系統(tǒng)時鐘。因Systick中斷的服務(wù)等級設(shè)定為最低級,當(dāng)中斷服務(wù)程序(ISR)的運行時間超出一個Systick中斷時,系統(tǒng)中斷將受到影響。


于是,在CMSIS-RTOS中,正確的處理方式是將中斷服務(wù)定義為一個進程,進程中等待信號。而在中斷服務(wù)中,僅僅給中斷進程發(fā)送信號。從而極大縮短中斷服務(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

ARM處理器有兩種處理器模式:


主應(yīng)用程序(Thread mode 線程模式),用于執(zhí)行應(yīng)用程序。既可以使用特權(quán)級,也可以使用用戶級;
異常服務(wù)例程(Handler mode 處理者模式),用于執(zhí)行異常。必須在特權(quán)級下執(zhí)行。


通過引入特權(quán)級和用戶級,就能夠在硬件水平上限制某些不受信任的或者還沒有調(diào)試好的程序,不讓它們隨便地配置涉及要害的寄存器,因而系統(tǒng)的可靠性得到了提高。



控制寄存器(CONTROL)
CONTROL[
0]=1,用戶級un-privilege的線程模式
CONTROL[
0]=0特權(quán)級privilege的線程模式
CONTROL[
1]=1,選擇使用PSP,推薦OS系統(tǒng)中的threads使用這個.
CONTROL[
1]=0,選擇使用MSP,推薦kernel and exception handles use it.



CMSIS-RTOS運行在unprivilege模式下,當(dāng)需要在進程中訪問privilege資源時,有兩種方式:


  1. 在配置文件中提升進程的權(quán)限至privilege狀態(tài)(如下圖所示),但會造成所有的進程運行在privilege模式下,影響系統(tǒng)安全。



參考例程"Ex9 interrupt signal"


  1. 在需要privilege權(quán)限時運行"系統(tǒng)級"代碼。


4.4.1SVC


遵循如下步驟:


  1. 新建"系統(tǒng)級"代碼列表(.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 //第一個"系統(tǒng)級"代碼


    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)級"用戶代碼函數(shù)。


  2. 建立"系統(tǒng)級"代碼與"進程級"代碼接口,示例:


    void __svc(1) init_ADC (void);


    __svc(1)代表SVC_Tables.s中第一個"系統(tǒng)級"代碼,同樣如果有多個應(yīng)用,依次遞增即可。


  3. 編寫"系統(tǒng)級"代碼,如下圖:


    Void __SVC_1 (void)


    {


    ......


    }


    在完成定義后,進程中調(diào)用init_ADC()將自動執(zhí)行__SVC_1()中的代碼。


五.信號量Semaphore


5.1功能描述

與信號類似,信號量是兩個或多個進程同步的方法。


信號量項是一個包含多個信號的容器。當(dāng)進程執(zhí)行到需要信號量的代碼段(進程申請信號量),如果信號量中有信號可用(包含不少于一個的信號),則進程繼續(xù)執(zhí)行,并且信號量中的信號數(shù)自減一。相反,如信號量中無信號可用(包含0個信號),則進程中止執(zhí)行并等待信號量中的信號可用。


同時,進程中可向信號量添加信號數(shù)目,從而引起信號量中的可用信號數(shù)增一。



如上圖。假定信號量初始化為只有一個可用信號,當(dāng)任務(wù)1提出申請時,信號量中含有1個可用信號,則任務(wù)1繼續(xù)執(zhí)行并引起信號量中的可用信號為0。此時任務(wù)2若提出申請因信號量中無可用信號,任務(wù)2進入信號量等待狀態(tài),直至任務(wù)1釋放信號量。


可見,進程可以釋放信號給信號量。


5.2創(chuàng)建信號量

示例代碼:


osSemaphoreId sem1;


osSemaphoreDef(sem1);


sem1 = osSemaphoreCreate(osSemaphore(sem1),SIX_TOKENS);


定義了一個含有6個可用信號的信號量sem1。


信號量初始化后,進程中即可申請信號量,使用函數(shù):


osSemaphoreWait(osSemaphoreId sem_id,uint_32 millisec)


Millisec = 0xffff wait for ever


信號量使用結(jié)束后,釋放信號量,使用函數(shù):


osSemaphoreRelease(osSemaphoreId sem_id);


5.3例程

Ex11 Interrupt Signals


5.4使用場景

5.4.1信號

兩個線程間同步執(zhí)行是信號量最基本的應(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);


}


}


在這個案例中,F(xiàn)un(A)始終先于Fun(B)執(zhí)行。


5.4.2限額

限額用于限制某些資源的配額。例如,某個指定的內(nèi)存塊只運行指定數(shù)目的應(yīng)用訪問。


如下例程,信號量初始化為5個信號,每個申請信號量的線程造成信號自減,當(dāng)獲取信號量的進程為5個時,后續(xù)申請信號量的進程進入等待狀態(tài),直至已獲取配額的進程釋放信號量,代碼例程:


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個線程同步)

互鎖是兩個線程同步的另一種通用模式?;ユi確保兩個線程得到同一互鎖點。如下例程:


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屏障(多個線程同步)

屏障是多個進程同步的有效模式,它的總體思路:設(shè)置一個初始化為0的信號量作為屏障,并在所有進程達(dá)到同步點時依次釋放屏障中的信號量,達(dá)到同步執(zhí)行的目的。


例程"Ex14 Barrier"


5.5注意事項

信號量是RTOS中極端有效的模式。然而,因信號量可在進程中增減甚至銷毀,信號量中可用配額數(shù)比較難把控,使用時必須實時把控可用配額數(shù)。


六.互斥量Mutex

6.1功能描述

單從功能來講,互斥量可以看做只含有一個可用配額且不可被創(chuàng)建和銷毀的特殊信號量?;コ饬恐饕糜诜乐箤τ布脑L問沖突,比如同一時刻只能有一個應(yīng)用訪問串口,否則將造成數(shù)據(jù)混亂。


申請互斥量的進程,必須等待互斥量中存在有效配額,否則進入等待狀態(tài)。


6.2創(chuàng)建互斥量

創(chuàng)建互斥量與創(chuàng)建信號量類似,示例代碼如下:


osMutexId    uart_mutex;


osMutexDef(Mutex1);


進程中創(chuàng)建互斥量:uart_mutex = osMutexCreate(osMutex(Mutex1));


其他進程申請互斥量:osMutexWait(uart_mutex);


使用完畢釋放互斥量:osMutexRelease(uart_mutex);


6.3例程

例程"Ex15 Mutex"


6.4注意事項

互斥量的使用限制多,也更安全,但扔要注意以下內(nèi)容:


  1. 使用完畢必須及時釋放互斥量,否則將造成后續(xù)進程無法使用該資源;


  2. 調(diào)用ThreadTerminate()函數(shù)消亡進程時,必須確保該進程沒有占用互斥量,否則將造成后續(xù)進程無法使用該資源;


七.數(shù)據(jù)交換

信號、信號量、互斥量只用于進程之間的觸發(fā),但對進程間的數(shù)據(jù)交換無能為力。進程間數(shù)據(jù)交換最簡單的方式是全局變量,但即使在簡單的系統(tǒng)中,把握和靈活應(yīng)用全局變量也是不小的挑戰(zhàn),因為全局變量會引起一系列不可預(yù)知錯誤。


在RTOS中,消息隊列郵箱隊列是進程間數(shù)據(jù)交互最為有效、安全的方式。


消息隊列和郵箱隊列的工作方式基本一樣,唯一的區(qū)別是消息隊列中傳輸?shù)氖谴粨Q數(shù)據(jù),而郵箱隊列中傳輸是指向待交換數(shù)據(jù)的指針,如下圖所示:



使用消息隊列和郵箱隊列進行數(shù)據(jù)交換有如下好處:


  1. 規(guī)范進程間數(shù)據(jù)交換的接口和緩存,為設(shè)計子系統(tǒng)提供可能;


  2. 規(guī)范進程的輸入、輸出,使進程獨立測試、調(diào)試成為可能;


7.1消息隊列MessageQueue

7.1.1創(chuàng)建消息隊列

創(chuàng)建消息隊列,遵循如下步驟:


  1. 聲明消息隊列ID,示例:osMessageQId Q_id;


  2. 定義消息隊列結(jié)構(gòu)體,示例:osMessageQDef(Q1,16_Message_Slots,unsigned int);其中16_Message_Slots指示空間大小為16,unsigned int指示空間類型;


  3. 在進程中創(chuàng)建消息隊列,示例:Q_id = osMessageQCreate(osMessageQ(Q1),NULL);


  4. 聲明解析消息隊列數(shù)據(jù)的osEvent類型數(shù)據(jù),示例:osEvent result;


  5. 在進程中發(fā)送數(shù)據(jù)到消息隊列,例程:osMessagePut(Q_id,Data,osWaitForever);


  6. 在另一進程中獲取消息隊列數(shù)據(jù),例程:result = osMessageGet(Q_id,osWaitForever);result.value.xxx;


其中,osEvent是個union結(jié)構(gòu)體,如下所示:


Union{


Uint32_t v;


Void *p;


Uint32_t signals;


}value


7.1.2例程

"Ex16 Message queue"


7.2內(nèi)存鏈Memory Pool

7.2.1功能描述

消息隊列中的數(shù)據(jù)類型可以是數(shù)據(jù)本身,也可以是指向數(shù)據(jù)的指針。


消息隊列中存儲的數(shù)據(jù)是指向特定內(nèi)存區(qū)域的指針,這樣的進程間交換數(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);


在進程中創(chuàng)建pool_id = osPoolCreate(osPool(pool_t));


3.初始化消息隊列,示例:


osMessageQDef(q1,ten_blocks,memory_block_t);


osMessageQid q_id;


在進程中創(chuàng)建:q_id = osMessageQCreate(osMessageQ(q1),NULL);


4.發(fā)送消息隊列,示例:


memory_block_t *led = (memory_block_t *)osPoolAlloc(pool_id);


led->led1 = xx;....


osMessagePut(q_id,led,osWaitForever);


5.讀取消息隊列,示例:


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郵箱隊列MailQueue


7.3.1功能描述


郵箱隊列是將內(nèi)存鏈融合到消息隊列中而成,郵箱隊列中存儲的同樣是指向特定內(nèi)存區(qū)域的指針。


7.3.2創(chuàng)建郵箱隊列


同樣采用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)建郵箱隊列遵循如下步驟:


1.創(chuàng)建郵箱隊列,例程:



osMailQDef(MQ_1,ten_blocks,memory_block_t);


osMailQId mq_id;


進程中創(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.使用完畢釋放郵箱隊列,示例:



osMailFree(mq_id,temp);


7.3.3例程


"Ex17 MailQueue"


八.系統(tǒng)配置


掌握前面的內(nèi)容,對CMSIS-RTOS有了總體的認(rèn)識。CMSIS-RTOS包括進程管理、時間管理、進程間通訊等。


本章著力于討論如何配置系統(tǒng)。CMSIS-RTOS針對基于Cortex-M構(gòu)架的處理器,提供一個統(tǒng)一的配置文件,RTX_Conf_CM.c包含三個部分:


#include "cmsis_os.h"


RTX user configuration part


#include "RTX_CM_lib.h"


如下圖:



8.1進程參數(shù)


在討論進程的相關(guān)章節(jié)中一經(jīng)接受了創(chuàng)建進程的基礎(chǔ)知識。


每個進程,系統(tǒng)分配一塊內(nèi)存空間用作進程棧(默認(rèn)200bytes),棧空間在進程創(chuàng)建時指定。


應(yīng)用中最多允許運行的進程數(shù)可配置。


由于進程棧空間、進程數(shù)可配置,應(yīng)用中的內(nèi)存需求也可很容易的計算出來。


8.2內(nèi)核調(diào)試支持

內(nèi)核可配置項,包括:


8.2.1追蹤溢出


選擇該項"stack overflow checking",出現(xiàn)進程棧溢出RTOS內(nèi)核調(diào)用os_error函數(shù)并進入死循環(huán)。該項主要用于調(diào)試階段的問題追蹤,當(dāng)然也可自定義os_error函數(shù)用于最終的應(yīng)用中打印錯誤信息,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"項,oxcc樣式自動寫入進程棧。運行時,watermark用于計算最大棧內(nèi)存使用率,并在"system and Event viewer"窗口報告,如下圖所示:



8.2.3用戶定時器數(shù)


如用戶定時器數(shù)量與應(yīng)用中使用的虛擬定時器不符,會造成os_timer()函數(shù)失效。


8.2.4進程運行權(quán)限選擇


如"Ex9 interruter signal",進程運行權(quán)限可配置。


8.3系統(tǒng)時基

默認(rèn)的系統(tǒng)時基是Cortex-M中的SysTick定時器。但,也支持自定義使用其他定時器作為系統(tǒng)時基。


8.4時間片

默認(rèn)的時間片是5ms。


8.5調(diào)度選項

調(diào)度器支持如下三種調(diào)度模式:


1.搶占式


此模式下,系統(tǒng)中進程擁有不同的優(yōu)先級,當(dāng)擁有高優(yōu)先級的進程進入"ready"狀態(tài),調(diào)度器轉(zhuǎn)入高優(yōu)先級進程運行。


2.輪詢式


此模式下,系統(tǒng)根據(jù)時間片為每個進程分配運行時間,處于運行態(tài)的進程在時間片到來時觸發(fā)調(diào)度(注意,即使高優(yōu)先級的進程進入"ready"狀態(tài)也要等時間片結(jié)束)。


3.輪詢、搶占式(默認(rèn)狀態(tài))


此模式下,系統(tǒng)根據(jù)時間片為每個進程分配運行時間,處于運行態(tài)的進程在時間片到來時或高優(yōu)先級的進程進入"ready"態(tài)觸發(fā)調(diào)度(注意,高優(yōu)先級的進程進入"ready"狀態(tài)將馬上觸發(fā)調(diào)度)。


4.協(xié)同式


此模式下,進程擁有相同的優(yōu)先級,有且僅有運行態(tài)的進程主動申請系統(tǒng)調(diào)度才會引起調(diào)度。


8.6源碼調(diào)試

如果用戶需要源碼級別的調(diào)試,遵循如下步驟:


  1. 新建文本文件,命名為"xxx.ini";


  2. "xxx.ini"中添加,SET SRC =,其中是RTX源碼的文件夾,默認(rèn)是C:\Keil\ARM\pack\arm\cmsis\\cmsis\rots\rtx。


  3. 在調(diào)試文件中導(dǎo)入"xxx.ini",如下圖所示:



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




本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

這次我成功將妹子約到了公司附近的咖啡館,繼續(xù)探討RTOS的heap的技術(shù)特點。當(dāng)我把準(zhǔn)備好的數(shù)據(jù)和動圖展示在她面前,她立馬激動起來了。

關(guān)鍵字: RTOS heap FreeRTOS

很多RTOS之所以可以用于資源很少的單片機,是因為它們可以配置,一般可以使用宏定義來選擇需要的功能,而裁剪掉不必要的功能,以減少對硬件的資源占用。

關(guān)鍵字: RTOS 單片機

處理特定任務(wù)的單片機可減輕主單片機或微處理器的任務(wù)和工作負(fù)荷,從而有助于簡化各種應(yīng)用的設(shè)計流程。

關(guān)鍵字: Microchip 單片機 RTOS

關(guān)注星標(biāo)公眾號,不錯過精彩內(nèi)容作者|strongerHuang微信公眾號?|?嵌入式專欄不知道大家有沒有做過低功耗產(chǎn)品?低功耗產(chǎn)品看似很簡單,其實,要做好一款低功耗產(chǎn)品,特別是做到超低功耗,難度相對更高。今天就來講講在R...

關(guān)鍵字: RTOS 低功耗設(shè)計 單片機 CPU

關(guān)注星標(biāo)公眾號,不錯過精彩內(nèi)容作者|strongerHuang微信公眾號?|?嵌入式專欄不知道大家有沒有做過低功耗產(chǎn)品?低功耗產(chǎn)品看似很簡單,其實,要做好一款低功耗產(chǎn)品,特別是做到超低功耗,難度相對更高。今天就來講講在R...

關(guān)鍵字: RTOS 低功耗設(shè)計

星標(biāo)「嵌入式大雜燴」,一起進步!作者|strongerHuang微信公眾號|嵌入式專欄經(jīng)常在交流群都會看到有些小伙伴在問:**資料、**文檔、**源碼在哪里下載?資料、文檔、源碼在哪里找?很多初學(xué)小白,找資料、文檔、源碼...

關(guān)鍵字: RTOS 開源

AzureRTOS使資源受限的設(shè)備能夠連接到微軟的AzureIoT,這是全球領(lǐng)先的IoT生態(tài)系統(tǒng)之一。AzureRTOS為企業(yè)級應(yīng)用程序(如航空電子設(shè)備、醫(yī)療設(shè)備、運輸和工業(yè)控制)提供理想的軟件開發(fā)平臺而聞名,然而,其設(shè)...

關(guān)鍵字: RTOS 硬件 物聯(lián)網(wǎng)設(shè)備

星標(biāo)「嵌入式大雜燴」,一起進步!來源|?小麥大叔概述嵌入式物聯(lián)網(wǎng)開發(fā)平臺是一個系統(tǒng),是微控制器物聯(lián)網(wǎng)開發(fā)平臺的系統(tǒng)組合。微控制器:是嵌入式控制的核心物:智能化的電子產(chǎn)品聯(lián):電子產(chǎn)品通訊或?qū)υ挼耐ǖ谰W(wǎng):互聯(lián)網(wǎng)、移動互聯(lián)網(wǎng)開...

關(guān)鍵字: MCU RTOS 物聯(lián)網(wǎng)

越來越多的嵌入式系統(tǒng)依賴于實時操作系統(tǒng)(RTOS)的使用,以滿足實時需求,減少上市時間,簡化開發(fā),增加代碼可移植性。盡管RTOS有許多好處,但它也有其缺點,如可能引入分配不當(dāng)?shù)娜蝿?wù)優(yōu)先級、堆棧溢出、饑餓、死鎖、優(yōu)先級反轉(zhuǎn)...

關(guān)鍵字: RTOS 實時系統(tǒng)

糖果Autosar

96 篇文章

關(guān)注

發(fā)布文章

編輯精選

技術(shù)子站

關(guān)閉