Chapter3 タスク管理（FreeRTOS チュートリアル）

# この記事について
[Mastering the FreeRTOS Real Time Kernel-A Hands-On Tutorial Guildの日本語訳](https://qiita.com/azuki_bar/items/91e12949737f460dbc11)

# Chapter 3 タスク管理
## 3.1 イントロダクションスコープ
このチャプターでは以下の理解を目的とする。
- FreeRTOSがどのようにして、それぞれのタスクに実行時間を割り当てるか
- FreeRTOSがどのようにして、どのタスクが動くべきか選ぶか
- プライオリティの関係がどのように、システムの振る舞いに影響を与えるか
- タスクが存在できる状態の種類

読者は以下を理解できる。
- タスク実装方法
- 1つまたは1つ以上のタスク生成方法
- タスクパラメータの使い方
- すでに生成したタスクの優先度変更方法
- タスク破棄方法
- 周期的な処理の実装方法
- アイドルタスクがいつ実行され、どのように使うことができるか

このチャプターのコンセプトは、FreeRTOSの基本的な理解とFreeRTOSアプリケーションがどのように振る舞うかを理解することにある。
そのためこの本で最も詳細なチャプターとなる。

## 3.1 タスク関数
タスクはCの関数で実装される。特別な実装はvoidをリターンして、void pointerを取ることだ。

```c:Listing11
void ATaskFunction(void *pvParameters);
```

各タスクはそれ自体が小さなプログラムになる。エントリーポイントを持ち、無限ループで実行され終了しない。
典型的なタスクをListing12で示す。

```c:Listing12
void ATaskFunction( void *pvParameters ) 
{ 
/* Variables can be declared just as per a normal function.  Each instance of a task 
created using this example function will have its own copy of the lVariableExample 
variable.  This would not be true if the variable was declared static – in which case 
only one copy of the variable would exist, and this copy would be shared by each 
created instance of the task. (The prefixes added to variable names are described in 
section 1.5, Data Types and Coding Style Guide.) */ 
int32_t lVariableExample = 0; 
 
    /* A task will normally be implemented as an infinite loop. */ 
    for( ;; ) 
    { 
        /* The code to implement the task functionality will go here. */ 
    } 
 
    /* Should the task implementation ever break out of the above loop, then the task  
    must be deleted before reaching the end of its implementing function.  The NULL  
    parameter passed to the vTaskDelete() API function indicates that the task to be  
    deleted is the calling (this) task.  The convention used to name API functions is  
    described in section 0, Projects that use a FreeRTOS version older than V9.0.0 
must build one of the heap_n.c files.  From FreeRTOS V9.0.0 a heap_n.c file is only 
required if configSUPPORT_DYNAMIC_ALLOCATION is set to 1 in FreeRTOSConfig.h or if 
configSUPPORT_DYNAMIC_ALLOCATION is left undefined.  Refer to Chapter 2, Heap Memory 
Management, for more information. 
Data Types and Coding Style Guide. */ 
    vTaskDelete( NULL ); 
} 
```

<font color="red">FreeRTOSタスクはreturnすることが許されていない。タスクはreturnを含んではならない。また関数の最後まで実行されてはならない。
タスクが不要になったら、代わりに明示的にdeleteする必要がある。</font>

<font color="red">1つのタスク定義で何個でもタスクは生成できる。各タスクは別個のスタック、ローカル変数で別のインスタンスで実行される。<font>

## 3.3 最上位のタスク状態
1つのアプリケーションは複数のタスクから構成される。プロセッサがシングルコアであれば、どの時間でも1つのタスクだけが実行される。
これは1つのタスクが2つのうちの1つの状態で存在できることを示唆する。この単純なモデルを最初に考えるが、これはかなり単純化した例であることに注意。
このチャプターの後で、Running状態でない状態は複数あることを示す。

1つのタスクがRunnning状態のとき、プロセッサはそのタスクのコードを実行している。Runnning状態でない時、そのタスクは休止状態である。
スケジューラーが実行状態にすることを決め、再度実行するまでReady状態が維持される。タスクが再開されたとき、Running状態から抜けたときに実行しようとしていた命令から実行される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/6b32a9b8-c40c-77ac-8f9b-36e35b2d8f79.png)
<font color="red">タスクがNot Running状態からRunning状態へ遷移することを、"switched in" または "swapped in" と呼ぶ
逆にタスクがRunning状態からNot Running状態へ遷移することを、"switched out" または "swapped out" と呼ぶ
FreeRTOSスケジューラーはタスクをin, outできる唯一の実態である。</font>

## 3.4 タスク生成
### xTaskCreate() API 関数
FreeRTOS V9.0.0はメモリをコンパイル時にstaticに取るxTaskCreateStatic関数を含む。
タスクはxTaskCreateを使って生成される。この関数はAPIの中で最も複雑だが、マルチタスクシステムにおいてタスクは最も基本的なコンポーネントなので理解する必要がある。
この本に付属するすべてのexampleはxTaskCreateを利用しているので、多くの参照するexampleがある。

1.5


## Tutorial 3.5 タスク優先度
プライオリティの最大値はconfigMAX_PRIORITIES(FreeRTOSConfig.h)で定義される。
プライオリティは0が一番低い。そのためconfigMAX_PRIORITIES-1が最大のプライオリティとなる。
FreeRTOSスケジューラーは実行状態のタスクを決めるために、２つの方法がとれる
### ①Generic Method
configMAX_PRIORITIESの値に上限がない。
ただし必要最小限の値がおすすめ。大きくするとRAMの消費量が増えるから。また大きくすると***実行時間が増える***。
使う時は [configUSE_PORT_OPTIMISED_TASK_SELECTION](## configUSE_PORT_OPTIMISED_TASK_SELECTION)を0にする、または定義しない。
### ②Architecture Optimized Method
アセンブリコードを使って最適化をしているので、***Generic Methodよりも早い***。
***configMAX_PRIORITIESの大きさは性能に影響しない***。
ただし32より大きくできない。Generic Methodと同様にRAM使用量を考えてできるだけ少なくするのがおすすめ。
使う時は[configUSE_PORT_OPTIMISED_TASK_SELECTION](## configUSE_PORT_OPTIMISED_TASK_SELECTION)を1にする

FreeRTOSスケジューラは最も優先度の高いタスクが実行されていることを保証できる。
同じ優先度のタスクが複数あった場合には順番に実行状態にする
## Tutorial 3.12 Scheduling Algorithm
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/c784b55b-91c5-dd98-66b5-27abe5f732e7.png" width=40%>
スケジューラはReady状態からタスクを選んで、実行状態とする。
スケジューラは常にReady状態のタスクで優先度が一番高いタスクを選択する。
Blocked状態のタスクが、解消するEventを受けると自動的にReady状態に遷移する。

scheduling algorithmとは、どのようにReady状態のタスクをRunning状態の遷移するかを決めることである。
アルゴリズムはFreeRTOSConfig.hに記載される [configUSE_PREEMPTION](## configUSE_PREEMPTION), [configUSE_TIME_SLICING](## configUSE_TIME_SLICING) で決まる。
3つ目の設定として[configUSE_TICKLESS_IDLE](## configUSE_TICKLESS_IDLE)があり、これもアルゴリズムに影響する。
これを使うとtick割り込みがextended period(ってなんだ？)の時にoffされる。configUSE_TICKLESS_IDLEはadvanced optionで省電力用に使われる

全ての設定可能なパターンでFreeRTOSスケジューラは優先度の同じ複数のタスクは***順番にRunnning状態に入ることを保証する***。
同じ時間実行されることを保証するのではなく、「順番」に実行されること保証している。

### Prioritized Pre-emptive Scheduling with Time Slicing
以下の設定のアルゴリズム

|Config|設定値|
|:--|:--|
|configUSE_PREEMPTION  |1|
|configUSE_TIME_SLICING|1|

"Fixed Priority Pre-emptive Scheduling with Time Slicing" と呼ばれ、規模の小さいRTOS Applicationで使われる。
#### Fixed Priority
スケジューリングアルゴリズムはスケジューリングされたタスクのPriorityは変えない。
が、タスク自身やほかのタスクによる優先度の変更は妨げない
#### Pre-emptive
Runnnig状態のタスクより優先度の高いタスクがReady状態になったらすぐに、優先度の高いタスクにRunning状態を譲る。
Pre-emptiveされたとは、ほかのタスクに譲るために「選択の余地なく（明示的にではなく）」Running状態からReady状態に遷移すること。
#### Time Slicing
Time Slicingとは、明示的にBlockedやRunning状態を譲ることなしで、同じ優先度のタスクが実行状態を共有するための仕組み。
time sliceごとに、実行するタスクを切り替えていく。
time sliceは2つのtick interruptの間で発生する。
#### 優先度の異なるタスクの動作例
これは分かりやすいので解説省略。解説読みたい場合はチュートリアル参照。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/fdb5f608-fdfd-64e7-8816-d98bb2daa50d.png" width=70%>
#### 優先度の同じタスクの動作例
tickの割り込みが "t1, t2, t3, t4, t5, t8, t9, t10 and t11" で入る。これがtime sliceになる。
time slice間で同じ優先度を持つIdle taskとTask2は切り替わる。
優先度の高いTask1がReadyになると、IDLEタスクをpre emptsして、Task1が動く。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/0cc1337c-3f6b-48d0-b88b-93e65ab0fa4c.png" width=70%>
#### configIDLE_SHOULD_YIELDの役割
Figure.27ではidleタスクとTask2は実行時間をシェアしているが、開発者はTask2に実行時間を多めに割り当てたいことがある。
configIDLE_SHOULD_YIELDはidleタスクのスケジューリングを設定できる。
- configIDLE_SHOULD_YIELDが0の場合、idleタスクはtime sliceを全部使いきる。※Figure 27がこれ
- configIDLE_SHOULD_YIELDが1の場合、ほかにidleタスクと同じpriorityのタスクがいれば、各loopごとに実行状態を譲る。※↓がこれ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/0fa31247-26d0-8c33-e9f9-95c4c0852cee.png" width=70%>

### Prioritized Pre-emptive Scheduling (without Time Slicing) 
[Prioritized Pre-emptive Scheduling with Time Slicing](### Prioritized Pre-emptive Scheduling with Time Slicing) のタイムスライスなしバージョン。
以下の設定のアルゴリズム

|Config|設定値|
|:--|:--|
|configUSE_PREEMPTION  |1|
|configUSE_TIME_SLICING|0|

time sliceがないので以下の場合にRunnnig状態にするタスクを選ぶ
・優先度の高いタスクがReadyになった場合
・優先度の同じタスクがblocked or suspended状態になった場合
タスクスイッチによるオーバーヘッドがないのが利点だが、同じ優先度のタスクの実行時間が大きく違ってしまうので熟練者向け。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540259/3e52a285-a438-bebc-e01a-6f4099ca5626.png" width=70%>
t1で動き出したidleタスクはタイムスライスがないのでTask1が来るまではRunnning状態が続く。
※明示的に記載なかったけど、同じ優先度のタスクはキューされていてFIFOで切り替わるのだる。当たり前か。。

### Co-operative Scheduling 
以下の設定のアルゴリズム

|Config|設定値|
|:--|:--|
|configUSE_PREEMPTION  |0|
|configUSE_TIME_SLICING|any value|

基本チュートリアルはpre-emptiveにフォーカスしているが、協調スケジューリングもできる。
コンテキストスイッチはRunnnig->Blockedへの遷移または、taskYEILDで明示的に譲った時に発生する。
タスクがpre-emptedされることは絶対にない。タイムスライスも使用されない。
あまり使わなそうなのでこれ以上書くのやめた。。

# FreeRTOSConfig.hの仕様
## configUSE_PORT_OPTIMISED_TASK_SELECTION
https://www.freertos.org/a00110.html
Tutorialの説明以上のことはあまり書いていない
"Is more efficient than the generic method."くらい。
あとはkernelのtasks.cを読むしかなさそう。

## configUSE_PREEMPTION, configUSE_TIME_SLICING
[Tutorial 3.12 Scheduling Algorithm](## Tutorial 3.12 Scheduling Algorithm) に記載
## configUSE_TICKLESS_IDLE