ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 26タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
今回は実験、Tick hookはESP32 Arduinoで使えるか?
Tick hookは、Tickを供給している割り込みから、専用の関数をコールバックする機能。これを使う為にはconfigUSE_TICK_HOOKを1にしてからコンパイルする必要がある。
configUSE_TICK_HOOKはFreeRTOSConfig.hの中で以下の様に定義されている。
#define configUSE_TICK_HOOK ( CONFIG_FREERTOS_LEGACY_TICK_HOOK )
CONFIG_FREERTOS_LEGACY_TICK_HOOKは、、、えー!どこぉ???orz
またしても怪しい感じだが、一応チャレンジしてみる。
この辺とかねー http://hamayan.blog.so-net.ne.jp/2018-02-19
まずコールバックされる関数だが、例によってユーザー独自の関数と言う訳ではなく、関数名やプロトタイプは決まっている。
void vApplicationTickHook( void ) { // user codes. }
そこで以下の様なデモプログラムを作成してみた。
volatile int count = 0; void vApplicationTickHook( void ) { count++; } void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); } void loop() { while( 1 ) { delay( 1000 ); Serial.print( "count is = " ); Serial.println( count, DEC ); } }
その結果は!、、、orz
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 25タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 ミューテックス(とバイナリーセマフォ)、、、4
● デッドロック
デッドロックは排他制御をした時の他の落とし穴である。デッドロックは別名”Deadly Embrace”(死の抱擁)とも呼ばれる、、、らしい。日本なら「5人の哲学者」であろう。
デッドロックは資源を共有している2つ以上のタスクが、自分でトークンを持ちながら相手のトークンを待つ様な状況の時に発生する。
以下の様な状況を考えてみよう。タスクAとタスクBはそれぞれmutexXとmutexYを待っている。
1.タスクAはmutexXのTakeに成功する。
2.タスクAはタスクBによりプリエンプトされる。
3.タスクBはmutexXのTakeを試みる前に、mutexYのTakeに成功する。しかしmutexXはタスクAが持ったままであり、タスクBはmutexXのTakeは叶わない。この為タスクBはmutexXの待ち状態に入る。
4.タスクAの実行が続けられる。タスクAはmutexYのTakeを試みる、が、mutexYはタスクBが持ったままであり、タスクAはmutexYのTakeは叶わない。この為タスクAはmutexYの待ち状態に入る。
5.全員寝たままで誰も起こさない。
デッドロックを避ける最良の方法は、設計時に充分考慮する事である。また問題が起きないようにシステムをシンプルにすることである。実際には小規模な組込みシステムではデッドロックは大きな問題ではない。何故ならシステム設計者はアプリケーション全体をよりよく知り、デッドロックを起こす様な領域を認識し、避ける事ができるからである。
※大きな組込みシステムでは問題にならない!とは言っていない?
※言うまでも無いだろうけれど、セマフォなりミューテックスをTakeする際に待ち時間の上限時間を設定できるので、かならず上限設定を行って、戻って来た時に戻り値を確認すべき。そうすればデッドロックは避けられる。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 24タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 ミューテックス(とバイナリーセマフォ)、、、3
● 優先度の継承
FreeRTOSのミューテックスとバイナリーセマフォはすごく似ている。唯一の違いはミューテックスは優先度継承の機構を持つ点である。優先度継承は、優先度の逆転の負の側面を最小にする枠組みである。固定された優先度の逆転ではなく、単にその影響を軽減する。
ただ、優先度継承はシステムの振舞いの数学的な解析をより複雑にする。その為優先度継承の利用を避けられない状況以外での利用は薦められない。
優先度継承はトークン・ホルダーの優先度を一時的に、資源の利用を待つタスクの中でもっとも高い優先度と同じレベルに上げる。資源の利用が終わり、ミューテックスをGiveすれば、自動的に元の優先度に戻る。
以下のデモプログラムはバイナリーセマフォを使って優先度が低、中、高の3つのタスクを動かした時の出力。文字列に続く数値はmillis()の出力であり、図の1行目と2行目に100msの時間差が発生している。通常は優先度の高いタスクは10ms周期で出力するが、優先度の逆転が起きて優先度が中間のタスクに邪魔をされている。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_024
volatile SemaphoreHandle_t sema; void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); // Create binary semaphore sema = xSemaphoreCreateBinary(); xSemaphoreGive( sema ); /* give one semaphore */ /* configure take task1. */ xTaskCreatePinnedToCore( lowerPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( middlePriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 3, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task3. */ xTaskCreatePinnedToCore( upperPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 4, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void lowerPriorityTask( void *execParam ) { while( 1 ) { xSemaphoreTake( sema, portMAX_DELAY ); Serial.print( "lowerPriorityTask time = " ); Serial.println( millis(), DEC ); xSemaphoreGive( sema ); vTaskDelay( pdMS_TO_TICKS( 1 ) ); } } void middlePriorityTask( void *execParam ) { while( 1 ) { vTaskDelay( pdMS_TO_TICKS( 20 ) ); // Serial.print( "middlePriorityTask time = " ); Serial.println( millis(), DEC ); unsigned long baseMillis = millis(); while( (millis() - baseMillis) < 100UL ) {} /* never enter block state. */ } } void upperPriorityTask( void *execParam ) { int count = 1; while( 1 ) { xSemaphoreTake( sema, portMAX_DELAY ); Serial.print( "upperPriorityTask time = " ); Serial.println( millis() ,DEC ); xSemaphoreGive( sema ); vTaskDelay( pdMS_TO_TICKS( 10 ) ); } }
以下のデモプログラムは、ミューテックスを使って優先度が低、中、高の3つのタスクを動かした時の出力。上のデモプログラムでは40ms近辺で優先度の逆転が起きているが、今回は1行目辺りから見ていくと、およそ100ms間低い優先度のタスクの出力は行われていないが、優先度の高いタスクは10ms周期で出力している。低い優先度のタスクに、優先度の高いタスクの優先度が継承されたと思われる。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_024_02
volatile xSemaphoreHandle mux; void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); // Create mutex mux = xSemaphoreCreateMutex(); /* configure take task1. */ xTaskCreatePinnedToCore( lowerPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( middlePriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 3, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task3. */ xTaskCreatePinnedToCore( upperPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 4, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void lowerPriorityTask( void *execParam ) { while( 1 ) { xSemaphoreTake( mux, portMAX_DELAY ); Serial.print( "lowerPriorityTask time = " ); Serial.println( millis(), DEC ); xSemaphoreGive( mux ); vTaskDelay( pdMS_TO_TICKS( 1 ) ); } } void middlePriorityTask( void *execParam ) { while( 1 ) { vTaskDelay( pdMS_TO_TICKS( 20 ) ); // Serial.print( "middlePriorityTask time = " ); Serial.println( millis(), DEC ); unsigned long baseMillis = millis(); while( (millis() - baseMillis) < 100UL ) {} /* never enter block state. */ } } void upperPriorityTask( void *execParam ) { int count = 1; while( 1 ) { xSemaphoreTake( mux, portMAX_DELAY ); Serial.print( "upperPriorityTask time = " ); Serial.println( millis() ,DEC ); xSemaphoreGive( mux ); vTaskDelay( pdMS_TO_TICKS( 10 ) ); } }
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 23タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 ミューテックス(とバイナリーセマフォ)、、、2
● 優先度の逆転
前回のデモプログラム http://hamayan.blog.so-net.ne.jp/2018-03-07-1 は、排他制御を行う場合の潜在的な落とし穴がある。デモプログラムを実行すると優先度の高いタスクが優先度の低いタスクに実行を待たされる場面がある。この現象を「優先度の逆転」と呼ぶ。
この望ましくない動作は特に中間の優先度を持つタスクが存在したときに問題になる。
優先度の低いタスクが優先度が中間のタスクにプリエンプトされて待ち状態に入った場合、優先度が中間のタスクが待ち状態に入るなどして優先度の低いタスクが再開されない限り優先度の高いタスクも待ち状態から再開されない。つまり本来は優先度の低いタスクと優先度の高いタスクの間で資源の排他制御を行っていたものが、優先度が中間のタスクが存在する為に優先度の高いタスクまで優先度の中間のタスクの動作に影響を受ける。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 22タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 ミューテックス(とバイナリーセマフォ)、、、1?
ミューテックスはバイナリーセマフォの特殊なタイプで、資源を共有する複数のタスク間で資源へのアクセスを制御する。ミューテックスの語源は排他制御の”MUTual EXclusion”、、、そうだったのか!気にした事無かった。
排他制御として使われるミューテックスは、共有される資源に付随するトークンとして考える事ができる。正式に資源にアクセスする為にトークンを"Take"したタスクを、「トークン・ホルダーになる」と言う。トークン・ホルダーが資源の利用を終えたら、"Give"してトークンを戻さなければならない。トークンが戻された場合に限り他のタスクはトークンを得る事ができ、安全に共有資源にアクセスできる。言い換えれば、トークンを得ないタスクは資源にアクセスする事を許可されない。
● xSemaphoreCreateMutex関数
ミューテックスはセマフォのタイプの一つである。xSemaphoreHandleタイプの変数に保存されるFreeRTOSセマフォの全てのタイプを取り扱う 。
ミューテックスは使われる前に生成されなければならない。ミューテックスを生成する為にはxSemaphoreCreateMutex関数を使用する。
プロトタイプは以下となる。
xSemaphoreHandle xSemaphoreCreateMutex( void );
(1) 戻り値:もしNULLが返って来たら、ミューテックスの生成に失敗している。失敗する原因は、HEAP領域の不足である。NULL以外が返って来た時はミューテックスの生成に成功した。戻ってきた値は、生成したミューテックスへのハンドルである。
例のコードを今度はミューテックスで置き換えてみた。2つのタスク間の排他制御なので、両方のタスクにミューテックスのTakeとGiveを使っている。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_022
xSemaphoreHandle mux; void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); mux = xSemaphoreCreateMutex(); /* configure take task1. */ xTaskCreatePinnedToCore( lowerPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( upperPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 3, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void lowerPriorityTask( void *execParam ) { int count = 1; while( 1 ) { xSemaphoreTake( mux, portMAX_DELAY ); Serial.print( "lowerPriorityTask count = " ); Serial.println( count++,DEC ); xSemaphoreGive( mux ); vTaskDelay( pdMS_TO_TICKS( 1 ) ); } } void upperPriorityTask( void *execParam ) { int count = 1; while( 1 ) { xSemaphoreTake( mux, portMAX_DELAY ); Serial.print( "upperPriorityTask count = " ); Serial.println( count++,DEC ); xSemaphoreGive( mux ); vTaskDelay( pdMS_TO_TICKS( 10 ) ); } }
実行した結果は以下となった。割込み禁止で行った結果と一致する。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 21タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 クリティカルセクションとスケジューラーの一時停止、、、2
● スケジューラーの一時停止
クリティカルセクションはスケジューラーの一時停止でも実現できる。スケジューラーの一時停止はスケジューラーの”Locking”とも知られている、、、?
基本的にクリティカルセクションは、割り込みや他のタスクからコードがアクセスされる領域を保護する。クリティカルセクションの実装をスケジューラーの一時停止で行った場合、他のタスクからのみ領域を保護する。この事はつまり割り込みからのアクセスは有効のままである。
単純に割り込み禁止で実現するクリティカルセクションの実装でその処理の実行時間が掛かる場合、スケジューラーの一時停止で実現するクリティカルセクションに置き換える事ができる。しかしスケジューラーの一時停止ではスケジューラーの再開に時間が掛かるので、それぞれの方法についてどちらを選ぶかは十分考慮する必要がある。
スケジューラーはvTaskSuspendAll関数の呼び出しで一時停止させられる。その時点からコンテキストスイッチ(タスクの切り替え)は停止するが、割り込みは許可されたままである。スケジューラーが一時停止中に割り込みがコンテキストスイッチを要求したなら、コンテキストスイッチの要求はペンディングされ、スケジューラの再開時に実行される。
FreeRTOSの関数はスケジューラーの一時停止中に使われるべきではない。
※一時停止の解除の関数は別だろうけれど。
スケジューラーの再開はxTaskResumeAll関数を呼ぶことで行われる。
● vTaskSuspendAll関数
プロトタイプは以下になる。
void vTaskSuspendAll( void );
● xTaskResumeAll関数
プロトタイプは以下になる。
portBASE_TYPE xTaskResumeAll( void );
(1) 戻り値:スケジューラーが一時停止中に受けたコンテキストスイッチの要求はペンディングされ、スケジューラーが再開されたタイミングで行われる。事前にペンディングされたコンテキストスイッチはxTaskResumeAll関数の終了直前に実行され pdTRUEを返す。それ以外のケースでは pdFALSEを返す。
vTaskSuspendAllの呼び出しとxTaskResumeAllの呼び出しはカーネルによってカウントされている。カウントが0に戻る事で、スケジューラーは再開される。
使い方は以下の様になる。
vTaskSuspendAll(); /* suspend scheduler */ PORTA |= 0x01; xTaskResumeAll(); /* resume scheduler */
※μiTRONで言うところのdis_dsp、ena_dspに該当すると思う。
前回の例題 http://hamayan.blog.so-net.ne.jp/2018-03-06 を作りかえてtaskENTER_CRITICALをvTaskSuspendAllに、taskEXIT_CRITICALをxTaskResumeAllとしたものを動かしてみた。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_021
void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); /* configure take task1. */ xTaskCreatePinnedToCore( lowerPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( upperPriorityTask, /* task name */ "", /* task name string */ configMINIMAL_STACK_SIZE, /* stack size */ NULL, /* execute parameter */ 3, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void lowerPriorityTask( void *execParam ) { int count = 1; while( 1 ) { vTaskSuspendAll(); Serial.print( "lowerPriorityTask count = " ); Serial.println( count++,DEC ); xTaskResumeAll(); vTaskDelay( pdMS_TO_TICKS( 1 ) ); } } void upperPriorityTask( void *execParam ) { int count = 1; while( 1 ) { Serial.print( "upperPriorityTask count = " ); Serial.println( count++,DEC ); vTaskDelay( pdMS_TO_TICKS( 10 ) ); } }
が、リセットを繰り返し正常に動かない。
Serial.print( "lowerPriorityTask count = " ); Serial.println( count++,DEC );
をコメントアウトするとリセットされないので、Serial.printと何かあるのかもしれない。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 20タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
〇 クリティカルセクションとスケジューラーの一時停止、、、1
● クリティカルセクションの基本
※ESP32 Arduinoと、FreeRTOSのVersion.10のリファレンスマニュアルでは、クリティカルセクションを扱うマクロまたは関数に大きな違いがある。ESP32 Arduinoではマクロで定義され、引数を持つが、Version.10のリファレンスマニュアルではでは以下の様に関数となっているので、注意が必要。
void taskENTER_CRITICAL( void ); void taskEXIT_CRITICAL( void );
上記の通りESP32 Arduinoと、FreeRTOSのVersion.10のリファレンスマニュアルで食い違いがあるので、ここではESP32 Arduinoを元にする。
クリティカルセクションの範囲は、taskENTER_CRITICAL()マクロとtaskEXIT_CRITICAL()マクロのそれぞれの呼び出しで囲まれた領域となる。
使い方は以下の様になる。
taskENTER_CRITICAL( &mux ); PORTA |= 0x01; taskEXIT_CRITICAL( &mux );
※μiTRONで言うところのloc_cpu、unl_cpuに該当すると思う。
taskENTER_CRITICALマクロ、taskEXIT_CRITICALマクロはtask.hの中で以下の様に定義されている。
#define taskENTER_CRITICAL(mux) portENTER_CRITICAL(mux) #define taskEXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) #define taskENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) #define taskEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux)
また、portENTER_CRITICALマクロ、portEXIT_CRITICALマクロはportmacro.hの中で以下の様に定義されている。
#define portENTER_CRITICAL(mux) vTaskEnterCritical(mux, __FUNCTION__, __LINE__) #define portEXIT_CRITICAL(mux) vTaskExitCritical(mux, __FUNCTION__, __LINE__) #define portENTER_CRITICAL_ISR(mux) vTaskEnterCritical(mux, __FUNCTION__, __LINE__) #define portEXIT_CRITICAL_ISR(mux) vTaskExitCritical(mux, __FUNCTION__, __LINE__)
ただしvTaskEnterCriticalは検索しても見つからないので、ライブラリ化されている?
注意書きとして以下の様に書かれている。
Remark: For the ESP32, portENTER_CRITICAL and portENTER_CRITICAL_ISR both alias vTaskEnterCritical, meaning that either function can be called both from ISR as well as task context. This is not standard FreeRTOS behaviour; please keep this in mind if you need any compatibility with other FreeRTOS implementations.
結局portENTER_CRITICALが割り込み禁止なのか、ディスパッチ禁止なのか、いやまぁ割り込みの中からも同等のマクロを使う点から割り込み禁止の様な気がしないでもないが、明確には判らなかった。
※割り込み禁止の様だ!
また、以下の様な記述もあるので、
Interrupts may still execute on FreeRTOS ports that allow interrupt nesting, but only interrupts whose priority is above the value assigned to the configMAX_SYSCALL_INTERRUPT_PRIORITY constant – and those interrupts are not permitted to call FreeRTOS API functions.
多重割り込みのところ http://hamayan.blog.so-net.ne.jp/2018-02-26 でも説明したように、カーネル管理外の割り込みは発生する。この割り込みの中ではFreeRTOSの関数は呼ばないように。
Serial.printの様に複数のタスクから呼び出される可能性のある関数を使う時はクリティカルセクションを設定すれば、優先度の低いタスクが文字を出力中に、優先度の高いタスクに邪魔されずに済む。
taskENTER_CRITICAL( &mux ) ; Serial.println( "hello world." ); taskEXIT_CRITICAL( &mux );
サンプルコードは以下の様になる。2つのタスクを生成し、優先度の高いタスクが低いタスクのシリアル出力を邪魔をする。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_020
void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); /* configure take task1. */ xTaskCreatePinnedToCore( lowerPriorityTask, /* task name */ "", /* task name string */ 1024, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( upperPriorityTask, /* task name */ "", /* task name string */ 1024, /* stack size */ NULL, /* execute parameter */ 3, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void lowerPriorityTask( void *execParam ) { int count = 1; portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; while( 1 ) { taskENTER_CRITICAL( &mux ) ; Serial.print( "lowerPriorityTask count = " ); Serial.println( count++,DEC ); taskEXIT_CRITICAL( &mux ); vTaskDelay( pdMS_TO_TICKS( 1 ) ); } } void upperPriorityTask( void *execParam ) { int count = 1; while( 1 ) { Serial.print( "upperPriorityTask count = " ); Serial.println( count++,DEC ); vTaskDelay( pdMS_TO_TICKS( 10 ) ); } }
以下は排他制御を行わなかった場合
以下は排他制御を行った場合
とまぁ、クリティカルセクションを設定するとカーネル管理の割り込みを停止してしまうので、可能な限りクリティカルセクションに入っている時間を短くする必要がある。また、必ずtaskENTER_CRITICALとtaskEXIT_CRITICALはペアで使う。
クリティカルセクションはカーネルによって呼び出し回数がカウントされている。taskENTER_CRITICAL毎に1カウントされ、これが0に戻るまでクリティカルセクションから抜けない。
※試しにtaskENTER_CRITICALを1回多くしたコードを実行すると、ESP32はある程度走ったところでリセットで再起動してしまう。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 19タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
排他制御について
※この内容は、資源管理 http://hamayan.blog.so-net.ne.jp/2018-02-26-1 の続きとなる。
タスク間や、タスクと割り込みの間で共有資源にアクセスする時、排他制御が必要となる。
FreeRTOSには排他制御を利用する為のいくつかの機能が提供されている。だがもっとも良い排他制御の方法は、アプリケーションが資源を共有しない、または1つのタスクのみ資源にアクセスする、、、おぃ!
この章は、読者により良い理解してもらう事を目的としている。
● 何時?何故?資源の管理と制御が必要となるのか?
● クリティカルセクションとは?
● 排他制御とは?
● スケジューラーを一時停止する意味とは?
● どのようにミューテックスを使うのか?
● どの様にゲートキーパータスクを生成するのか?
● 優先度逆転とは?どの様に優先度継承の影響を減らすか?
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 18タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
カウンティングセマフォの2つの例
1つはカウンターの初期値が0の場合。
これは既に http://hamayan.blog.so-net.ne.jp/2018-02-26-3 の、発生したイベントの回数を取りこぼさない為に使った例。
1つはカウンターの初期値が0よりも大きい場合。
イナバの物置の例題の様に、資源の数の管理などに使用する。
以下の例は100の資源に対して同一優先度のタスクがそれぞれ資源を1つずつ取得、最後は共に50で終わる。
https://github.com/hamayanShowa-ele/ArduinoShare/tree/main/ESP32freeRTOS_TASK_ID_018
volatile SemaphoreHandle_t xSemaphore; void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); xSemaphore = xSemaphoreCreateCounting( 100, 100 ); /* configure take task1. */ xTaskCreatePinnedToCore( takeTask1, /* task name */ "", /* task name string */ 1024, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); /* configure take task2. */ xTaskCreatePinnedToCore( takeTask2, /* task name */ "", /* task name string */ 1024, /* stack size */ NULL, /* execute parameter */ 2, /* task priority : 0 to 24. 0 is lowest priority. */ NULL, /* task handle pointer */ 1 /* core ID */ ); vTaskDelete( NULL ); /* delete loopTask. */ } void loop() { } void takeTask1( void *execParam ) { int count = 1; while( 1 ) { if( xSemaphoreTake( xSemaphore, 0 ) == pdTRUE ) { Serial.print( "take semaphore 1 : " ); Serial.println( count++,DEC ); } vTaskDelay( pdMS_TO_TICKS( 100 ) ); } } void takeTask2( void *execParam ) { int count = 1; while( 1 ) { if( xSemaphoreTake( xSemaphore, 0 ) == pdTRUE ) { Serial.print( "take semaphore 2 : " ); Serial.println( count++,DEC ); } vTaskDelay( pdMS_TO_TICKS( 100 ) ); } }
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本
ESP32でマルチタスクを行う為の、とりあえずここまで判った事 by freeRTOS 17タスク目 [ESP32]
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
カウンティングセマフォで使用する関数。
カウンティングセマフォでも、生成、Give、Takeそれぞれの関数を必要とするが、GiveとTakeに関してはバイナリーセマフォと同一の為、生成について解説する。
例によって2つの生成関数があり、1つは必要な領域を動的に生成するxSemaphoreCreateCounting関数。
1つは必要な領域をユーザーが事前に(静的に)生成しておくxSemaphoreCreateCountingStatic関数。
プロトタイプは以下になる。
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount );
(1) uxMaxCountは最大カウント数
(2) uxInitialCountはセマフォ生成時のカウント数の初期値
(3) 戻り値:NULLが返る時はセマフォの生成に失敗している。原因はHEAP領域の不足。それ以外なら成功している。
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t pxSempahoreBuffer );
(1) uxMaxCountは最大カウント数
(2) uxInitialCountはセマフォ生成時のカウント数の初期値
(3) pxSempahoreBufferはユーザーが事前に生成した変数StaticSemaphoreのポインター
(4) 戻り値:NULLが返る時はセマフォの生成に失敗している。原因はpxSempahoreBufferがNULLであった。それ以外なら成功している。
ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版
リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本