我們知道,(單核)單片機某一時刻只能干一件事,會造成單片機資源的浪費,而且還有可能回應不夠及時,所以,在比較龐大的程式或者是要求實時性比較高的情況下,我們可以移植作業系統,因為這種情況下作業系統比裸機方便很多,效率也高,下面,杰杰將帶你們走進FreeRTOS的世界隨便看看,
下面正式開始本文內容,
在沒有用到作業系統之前,單片機的運行是順序執行,就是說,很多時候,單片機在執行這件事的時候,無法切換到另一件事,這就造成了資源的浪費,以及錯過了突發的信號,那么,用上了作業系統的時候,很容易避免了這樣的問題,
很簡單,從感覺上,單片機像是同時在干多件事,為什么說像呢,因為單片機的執行速度很快,快到我們根本沒辦法感覺出來,但是同時做兩件事是不可能的,在(單核)單片機中,因為它的硬體結構決定了CPU只能在一個時間段做一件事如:

如這張圖,都是按照順序來執行這些事的,假設每個任務(事件)的time無限小,小到我們根本沒法分辨出來,那么我們也會感覺單片機在同時做這六件事,
真相就是:所有任務都好像在執行,但實際上在任何一個時刻都只有一個任務在執行
如是加上了中斷系統的話,就可以將上圖理解為下圖:

通常把程式分為兩部分:前臺系統和后臺系統, 簡單的小系統通常是前后臺系統,這樣的程式包括一個死回圈和若干個中斷服務程式:應用程式是一個無限回圈,回圈中呼叫API函式完成所需的操作,這個大回圈就叫做后臺系統,中斷服務程式用于處理系統的異步事件,也就是前臺系統,前臺是中斷級,后臺是任務級,簡單來說就是程式一直按順序執行,有中斷來了就做中斷(前臺)的事情,處理完中斷(前臺)的事情,就回到大回圈(后臺)繼續按順序執行,
那么問題來了,這樣子的系統肯定不是好的系統,我在做第一個任務的時候想做第四個任務,根本做不到啊,其實也能做到,讓程式執行的指標cp指向第四個任務就行了,但是任務一旦復雜,那么整個工程的代碼的結構,可移植性,及可讀性,肯定會差啦,
FreeRTOS
那么作業系統的移植就是不可或缺的了,什么叫RTOS?:Real Time OS,實時作業系統,強調的是實時性,就是要規定什么時間該做什么任務,那么假如同一個時刻,需要執行兩個或者多個任務怎么辦,那么我們可以人為地把任務劃分優先級,哪個任務重要,就先做,因為前面一直強調,單片機無法同時做兩件事,在某一個時刻只能做一件事,
那么FreeRTOS是怎么操作的呢?先看看FreeRTOS的內核吧:
FreeRTOS是一個可裁剪、可剝奪型的多任務內核,而且沒有任務數限制,FreeRTOS提供了實時作業系統所需的所有功能,包括資源管理、同步、任務通信等, FreeRTOS是用C和匯編來寫的,其中絕大部分都是用C語言撰寫的,只有極少數的與處理器密切相關的部分代碼才是用匯撰寫的,FreeRTOS結構簡潔,可讀性很強!RTOS的內核負責管理所有的任務,內核決定了運行哪個任務,何時停止當前任務切換到其他任務,這個是內核的多任務管理能力,
可剝奪內核顧名思義就是可以剝奪其他任務的CPU使用權,它總是運行就緒任務中的優先級最高的那個任務,

在FreeRTOS中,每個任務都是無限回圈的,一般來說任務是不會結束運行的,也不允許有回傳值,任務的結構一般都是
While(1)
{
/****一直在回圈執行*****/
}
如果不需要這個任務了,那就把它洗掉,
移植的教程我就不寫了,超級簡單的,按照已有的大把教程來做就行了,(如果沒有資源,可以在后臺找我,我給一份移植的教程/原始碼)
其實FreeRTOS的運用及其簡單,移植成功按照自己的意愿來配置即可,而且FreeRTOS有很多手冊,雖然作者英語很差,但是我有谷歌翻譯!!!哈哈哈
既然一直都說任務任務,那肯定要有任務啊,創建任務:
// task. h task.c
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
const char * const pcName,
uint16_t usStackDepth,
void *pvParameters,
UBaseType_t uxPriority,
TaskHandle_t *pvCreatedTask
);
函式的原型都有,按照字面的理解
TaskFunction_t pvTaskCode //傳遞進來的是任務函式
const char * const pcName //傳遞進來的是任務Name
uint16_t usStackDepth //傳入的是堆疊的大小
在這里要說明一下,在裸機中開發,我們不管區域變數還是全域變數,反正定義了就能用,中斷發生時,函式回傳地址發哪里,我們也不管,但是在作業系統中,我們必須弄清楚我們的引數是怎么儲存的,他們的大小是多大,就需要我們去定義這個堆疊的大小,它就是用來存放我們的這些東西的,太小,導致堆疊溢位,發生例外,(堆疊是單片機 RAM 里面一段連續的記憶體空間)
因為在多任務系統中,每個任務都是獨立的,互不干擾的,所以要為每個任務都分配獨立的堆疊空間,
void *pvParameters //傳遞給任務函式的引數
UBaseType_t uxPriority //任務優先級
TaskHandle_t *pvCreatedTask //任務句柄
任務句柄也是很重要的東西,我們怎么洗掉任務也是要用到任務句柄,其實說白了,我作業系統怎么知道你是什么任務,靠的就是任務句柄的判斷,才知道哪個任務在執行,哪個任務被掛起,下一個要執行的任務是哪個等等,靠的都是任務句柄,
那么要使用這些東西,我們肯定要實作啦,下面就是實作的定義,要定義優先級,堆疊大小,任務句柄,任務函式等,
//任務優先級
#define LED_TASK_PRIO 2
//任務堆疊大小
#define LED_STK_SIZE 50
//任務句柄
TaskHandle_t LED_Task_Handler;
//任務函式
void LED_Task(void *pvParameters);
創建任務后,可以開啟任務調度了,然后系統就開始運行,
xTaskCreate((TaskFunction_t )LED_Task, //任務函式
(const char* )"led_task", //任務名稱
(uint16_t )LED_STK_SIZE, //任務堆疊大小
(void* )NULL, //傳遞給任務函式的引數
(UBaseType_t )START_TASK_PRIO, //任務優先級
(TaskHandle_t* )&LED_Task_Handler);//任務句柄
vTaskStartScheduler(); //開啟任務調度
這個創建任務的函式 xTaskCreate 是有回傳值的,其回傳值的型別是BaseType_t,
我們在描述中看看:
// @return pdPASS if the task was successfully created and added to a readylist, otherwise an error code defined in the file projdefs.h
我們其實可以在任務調度的時候判斷一下回傳值是否為pdPASS從而知道任務創是否建成功,并且列印一個資訊作為除錯,因為后面使用信號量這些的時候都要知道信號量是否創建成功,使得代碼健壯一些,免得有隱藏的bug,
然后就是具體實作我們的任務LED_Task是在做什么的
當然可以實作多個任務,還是很簡單的,
//LED任務函式
void LED_Task(void *pvParameters)
{
while(1)
{
LED0 = !LED0;
vTaskDelay(1000);
}
}
這就是一個簡單的作業系統的概述,
下一篇,應該是講述開啟任務調度與任務切換的具體程序,
這個可以參考野火的書籍《從 0 到 1 教你寫 uCOS-III》

更多資料歡迎關注“物聯網IoT開發”公眾號!
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/33388.html
標籤:嵌入式
