版本 b468c33864e20dec3a14f1fb7d745aff42ade6ab
Changes from b468c33864e20dec3a14f1fb7d745aff42ade6ab to 61cbd504d55e77df62440479c61710a2de12e4da
---
title: FreeRTOS
toc: no
...
組員
----
* 梁穎睿 / TheKK
* 李奇霖 / Shinshipower
* 方威迪 / waynew30777
* 陳盈伸 / shin21
共筆
----
* `Link<https://hackpad.com/FreeRTOSV8.0.0-PU3awKuzHz6#:h=%3Chardware-interfacing%3E>`_
FreeRTOS架構
-----------
FreeRTOS是一個相對較小的應用程序。最小化的FreeRTOS內核僅包括3個(.c)文件和少數頭文件,總共不到9000行代碼,還包括了註釋和空行。一個典型的編譯後(二進制)代碼映像小於10KB。
FreeRTOS的代碼可以分解為三個主要區塊:任務,通訊,和硬件接口。
●任務:大約有一半的FreeRTOS的核心代碼用來處理多數操作系統首要關注的問題:任務。任務是給定優先級的用戶定義的C函數。 task.c和task.h完成了所有有關創建,調度,和維護任務的繁重工作。
●通訊:任務很重要,不過任務間可以互相通訊則更為重要!它給我們帶來FreeRTOS的第二項任務:通訊。大約40%的FreeRTOS核心代碼是用來處理通訊的。 queue.c和queue.h是負責處理FreeRTOS的通訊的。任務和中斷使用隊列互相發送數據,並且使用信號燈和互斥來發送臨界資源的使用情況。
●硬件接口:接近9000行的代碼拼湊起基本的FreeRTOS,是硬件無關的;相同的代碼都能夠運行,不論FreeRTOS是運行在不起眼的8051,還是最新、最炫的ARM內核上。大約有6%的FreeRTOS的核心代碼,在硬件無關的FreeRTOS內核與硬件相關的代碼間扮演著墊片的角色。我們將在下個部分討論硬件相關的代碼。
硬體方面:
portmacro.h:
定義了硬體相關變數,如資料形態定義,以及硬體相關的函式呼叫的名稱定義(以portXXXXX爲名)等,統一各平臺呼叫函式的
port.c:
定義了包含和硬體相關的程式碼的實作
FreeRTOSConfig.h:
包含Clock speed, heap size, mutexes等等都在此定義
Task的狀態
.. image:: /Task狀態.png
* Ready : FreeRTOS將各種優先權的Task放在Readylist
* Running : 透過FreeRTOS的排程,依照優先權的高低依序給CPU執行
* Block :
* Suspended :
* Ready list的資料形態
<<命名規則>>
>>變數
char類型:以 c 為字首
short類型:以 s 為字首
long類型:以 l 為字首
float類型:以 f 為字首
double類型:以 d 為字首
Enum變數:以 e 為字首
其他(如struct):以 x 為字首
pointer有一个額外的字首 p , 例如short類型的pointer字首為 ps
unsigned類型的變數有一個額外的字首 u , 例如unsigned short類型的變數字首為 us
>>Functions
文件内:以 prv 為字首
API:以其return類型為字首,按照對變數的定義
名字:以其所在的文件名開頭。如vTaskDelete即在Task.c文件中名稱
- FreeRTOS使用ready list去管理待準備好要執行的tasks而ready list的資料儲存方式如下圖
.. image:: /freertos-figures-full-ready-list-2.png
* Context Switch 時選出下一個欲執行的task
下面是在ready list中依照優先度選取執行目標的程式其中,FreeRTOS的優先度排序最小優先權爲0,數字越大則優先權越高
.. code-block:: prettyprint
/* In file: Source/tasks.c */
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) )
{
configASSERT( uxTopReadyPriority );
--uxTopReadyPriority;
}
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) );
/*In file: Source/include.h */
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )
{
List_t * const pxConstList = ( pxList );
/* Increment the index to the next item and return the item, ensuring */
/* we don't return the marker used at the end of the list. */
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
{
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;
}
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;
}
* 創造全新task
TCB的資料結構:
.. code-block:: prettyprint
typedef struct tskTaskControlBlock
typedef struct tskTaskControlBlock
{
volatile portSTACK_TYPE *pxTopOfStack; /* Points to the location of
the last item placed on
the tasks stack. THIS
MUST BE THE FIRST MEMBER
OF THE STRUCT. */
xListItem xGenericListItem; /* List item used to place
the TCB in ready and
blocked queues. */
xListItem xEventListItem; /* List item used to place
the TCB in event lists.*/
unsigned portBASE_TYPE uxPriority; /* The priority of the task
where 0 is the lowest
priority. */
portSTACK_TYPE *pxStack; /* Points to the start of
the stack. */
signed char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* Descriptive name given
to the task when created.
Facilitates debugging
only. */
#if ( portSTACK_GROWTH > 0 )
portSTACK_TYPE *pxEndOfStack; /* Used for stack overflow
checking on architectures
where the stack grows up
from low memory. */
#endif
#if ( configUSE_MUTEXES == 1 )
unsigned portBASE_TYPE uxBasePriority; /* The priority last
assigned to the task -
used by the priority
inheritance mechanism. */
#endif
} tskTCB;
.. code-block:: prettyprint
pxTopOfStack , pxEndOfStack :紀錄Stack的大小
uxPriority , uxBasePriority :就路優先權
* 硬體驅動原理
-----------
* 以 `GPIO</embedded/GPIO>`_ 為例
- 參考 STM32Cube_FW_F4_V1.1.0/Projects/STM32F429I-Discovery/Examples/GPIO/GPIO_EXTI/readme.txt
創造全新task
TCB的資料結構:
效能表現
--------
●context switch
我們想得知FreeRTOS的context switch時間,並想出一個測試方法:
.. image:: /embedded/test1contextSwitch.jpg
1. 首先創建task1和task2,其中task2的priority大於task1的priority。task2先執行時,馬上就進行vTaskDelay使task2移至block狀態1秒,這時就會發生context switch,換成task1執行,這1秒的時間,task1不斷的進行i++,直到1秒結束後,回到task2執行,再由task2印出i值,並把i重新設0,此為一個週期。此動作可得到i在一秒時可跑至多少,設一秒可跑至k值。
2. 設定一個task3其priority高於task2,讓task3執行vTaskDelay 300秒,當300秒結束後,會中斷task1所執行的i++。再由task3印出i值,設其為final_i,k值與final_i值的差額,即為context switch的總時間。
下圖為隨機挑出45個i值做成圖表,其中平均i值為:4280015
.. image:: /embedded/test2contextSwitch.jpg
接著我們測出的final_i值,平均為:3913853,故可得到 (4280015 - 3913853)/ 4280015 = 0.0855 (秒)
0.0855秒代表在300秒的測試內的所有context switch時間之總和
而因為一個週期(第一個步驟)會經過2個context switch(上圖),我們測300內共有600個context switch,故我們測出每個context switch約為:0.0855 / 600 = 142.5(us)
●interrupt latency
我們的架構為是手動設定一個external interrupt,發生在BUTTON_USER按下時,下面程式是我們的實作:
.. code-block:: prettyprint
i = 0;
while( STM_EVAL_PBGetState( BUTTON_USER ) ){
i++;
}
.. code-block:: prettyprint
當BUTTON_USER按下後,會先執行i++直到interruptHandler處理interrupt,讀i值即可得知interrupt latency。
而實作結果發現i依舊為0,我們猜想是interruptHandler反應時間遠小於執行i++。
參考資料
--------
* `The Architecture of Open Source Applications: FreeRTOS<http://www.aosabook.org/en/freertos.html>`_
- `簡體中文翻譯<http://www.ituring.com.cn/article/4063>`_
* `Study of an operating system: FreeRTOS</embedded/FreeRTOS_Melot.pdf>`_
* `FreeRTOS 即時核心實用指南</embedded/FreeRTOS-manual-zh.pdf>`_