Previous: 3.3. タスクにパラメータを渡す(文字列) |
Up: 3. タスク管理 |
Next: 3.5. タスク優先度 |
サンプルプログラム |
freertos_one_template |
FreeRTOSでは、一つのタスク用関数を使用して、複数のタスクを登録するということができます。まずは実際の実行結果とコードを見てみてください。実行結果は以下の通り、前項のものと同じです。
BbBbBbBbBbBbAaAaAaAaAaAaBbBbBbBbBbBAaAaAaAaAaAabBbBbBbBbBbBAaAa AaAaAaAbBbBbBbBbBbBaAaAaAaAaAabBbBbBbBbBbBAaAaAaAaAaAbBbBbBbBbB bBaAaAaAaAaAabBbBbBbBbBbBAaAaAaAaAaAbBbBbBbBbBbBaAaAaAaAaAabBbB ... |
コードですが、タスク用関数はこのprvTask関数一つだけです。
void prvTask(void *pvParameters) { ? int8_t* pcTaskName; ? pcTaskName = (int8_t *)pvParameters; ? while(1) ??? { ????? cprintf(pcTaskName); ??? } } |
そしてxTaskCreate関数を2回実行しているのですが、登録するタスク用関数として、両方ともprvTask関数を指定しています。ただし実行時に表示される内容を分けるために、パラメータとして違う文字列を渡しています。
xTaskCreate(prvTask, (signed portCHAR *)"TaskA", 192, &TaskA_symbol, 1, NULL); xTaskCreate(prvTask, (signed portCHAR *)"TaskB", 192, &TaskB_symbol, 1, NULL); |
これで一つのタスク用関数で複数のタスクが実行できました。
このようなことができるのは、FreeRTOSが、タスク毎にスタック領域を確保しているからです。カーネルにタスクを登録すると、カーネルはそのタスク用に一定のスタック領域を確保します。タスクが実行時に確保する自動変数はこのスタック領域に保存されます。さらにカーネルが他のタスクに処理を切り替える(この切り替え動作をコンテキストスイッチといいます)ときに、その時点でのCPUのレジスタやその他の実行状態すべてを先ほどのスタック領域に保存します。このタスクに再びカーネルの処理が戻ってきたときには、スタック領域に保存されているレジスタの内容をCPUレジスタに復帰させます。これにより、コンテキストスイッチが発生したときとまったく同じ状態で、タスクの処理を再開することができます。ある関数について、実行状態を丸ごと保存していますので、タスク用関数が一つしかない場合でも、複数のスタック領域が確保されれば、複数の独立したタスクとして実行できるのです。
このような仕組みですから、タスクを登録する際には、内容の重要性とは関係なくスタック領域を消費します。タスクが増えていくとメモリの消費量が比例して増加することになりますから、むやみにタスクを増やさない工夫も必要になります。
このスタック領域の大きさはxTaskCreate関数の実行時に、第3引数usStackDepthで指定します。この引数はワード単位です。STM32は32ビットマイコンなので、1ワード=4バイトです。スタック領域の容量を事前に正確に見積もるのはなかなか難しいのですが、筆者がいろいろと試したところでは、サンプルプログラムのような単純な処理であれば、192ワード(768バイト)を確保すると、スタックオーバーフロー(スタック領域が足らなくなってカーネルが暴走すること)が発生しないようです。スタック領域の確保については、詳しくは後述のメモリ管理の項を参照してください。
さてこれまでの説明の中で、タスクはカーネルに選択されて実行中のものと、コンテキストスイッチにより実行状態がスタック領域に保存されて停止中のものとがあることがわかったかと思います。FreeRTOSでは、現在実行中のタスクの状態をRunning状態と呼んでおり、停止中の状態をReady状態と呼んでいます。実際には停止中の状態としては他にもBlocked状態があるのですがこれについては後述します。
図 3?1 Ready状態とRunning状態の遷移
Previous: 3.3. タスクにパラメータを渡す(文字列) |
Up: 3. タスク管理 |
Next: 3.5. タスク優先度 |