現在実用ガイドを読みながら編集中、、、書いて有る事を信用しない様に!
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
資源管理
例えば以下の様な例題を考えてみる。前提としてTaskAとTaskBの2つのタスクがあり、TaskBはTaskAより優先度が高く、通常は何かの待ちをしている。
(1) TaskAはLCDに「Hello world.」を出力しようとしている。
(2) TaskAは「Hello w」を書いたところでTaskBにプリエンプトされてしまう。
(3) TaskBは「Abort, Retry, Fail?」を出力。その後待ち状態へ。
(4) TaskAはディスパッチされて残りの「orld.」を出力。
(5) 結果、LCDに表示された文字は「Hello wAbort, Retry, Fail?orld.」となる。
LCDと言う1つの資源に、同時に2つのタスクがアクセスした、あるある例である。
また次のケースはもっと深刻で、例としてARM7のPORTAの特定のビットを変化させる様な処理について、
POARTA |= 0x01;のアセンブラ出力を見てみると、
![esp32_WDT_Message_003.png]()
一旦メモリから読み出した内容に、修正を加えて、またメモリに書き戻す Read Modify Writeが行われている事が判る。※専用のビット操作命令を持たないマイコンの場合は大概こうなっている。
アセンブラにすると5命令だが、この間に割り込みが入り、プリエンプトされてしまう可能性がある。
例えば以下の様な例題を考えてみる。前提としてTaskAとTaskBの2つのタスクがあり、TaskA、TaskB共にPORTAにアクセスする。TaskBはTaskAより優先度が高く、通常は何かの待ちをしている。
(1) TaskAが現在のPORTAの内容を読み出す処理を行う。例えば0x00が読み出されたとする。
(2) TaskAは変更が完了する前にTaskBにプリエンプトされてしまう。
(3) TaskBは現在のPORTAの内容を読み出し、0x00を0x02に変更し、PORTAに書き戻す。その後TaskBは待ち状態に入る。
(4) TaskAがディスパッチされて中断していた処理を再開する。つまり0x00を0x01に変更してPORTAに書き戻す。
(5) TaskBの努力は、無に帰してしまったようだ。
※上記問題はPORTAの様な汎用ポートだけでなく、大域変数でも起こる。
※Read Modify Write専用の命令があり、1命令で実行できる場合は上記問題は起きない。この辺はアキーテクチャ毎にCPU命令を調べておく必要がある。
※関数が複数のタスクや割り込みから呼び出されても安全に機能するとき、リエントラント性が有ると呼ばれる。全ての変数がスタック(自動変数)上やCPUレジスタ上で動いていれば、リエントラント性は確保できる。一次的にでも大域変数を使う関数は、リエントラント性を確保できない。関数ライブラリのマニュアルを読むとリエントラント性について書かれている。
ESP32のGPIOに関してはARM Cortex-Mと同様、GPIOの個別の端子の出力レジスタ値をSETするレジスタ(GPIO_OUT_W1TS_REG)またはRESETするレジスタ(GPIO_OUT_W1TC_REG)がある。このレジスタは書くだけなので、資源の競合が起きない。
http://hamayan.blog.so-net.ne.jp/2018-02-22 のコードを、以下の様に変更する事ができる。
※ESP32のesp32_hal_gpio.cの中で以下の様なコードとなっているので、マルチタスクでdigitalWriteを使っても大丈夫と思われる。
![ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発 ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発]()
![リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17)) リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))]()
http://www.profdong.com/elc4438_spring2016/USINGTHEFREERTOSREALTIMEKERNEL.pdf
ESP32はRTOSができるらしい、、、
資源管理
例えば以下の様な例題を考えてみる。前提としてTaskAとTaskBの2つのタスクがあり、TaskBはTaskAより優先度が高く、通常は何かの待ちをしている。
(1) TaskAはLCDに「Hello world.」を出力しようとしている。
(2) TaskAは「Hello w」を書いたところでTaskBにプリエンプトされてしまう。
(3) TaskBは「Abort, Retry, Fail?」を出力。その後待ち状態へ。
(4) TaskAはディスパッチされて残りの「orld.」を出力。
(5) 結果、LCDに表示された文字は「Hello wAbort, Retry, Fail?orld.」となる。
LCDと言う1つの資源に、同時に2つのタスクがアクセスした、あるある例である。
また次のケースはもっと深刻で、例としてARM7のPORTAの特定のビットを変化させる様な処理について、
POARTA |= 0x01;のアセンブラ出力を見てみると、

一旦メモリから読み出した内容に、修正を加えて、またメモリに書き戻す Read Modify Writeが行われている事が判る。※専用のビット操作命令を持たないマイコンの場合は大概こうなっている。
アセンブラにすると5命令だが、この間に割り込みが入り、プリエンプトされてしまう可能性がある。
例えば以下の様な例題を考えてみる。前提としてTaskAとTaskBの2つのタスクがあり、TaskA、TaskB共にPORTAにアクセスする。TaskBはTaskAより優先度が高く、通常は何かの待ちをしている。
(1) TaskAが現在のPORTAの内容を読み出す処理を行う。例えば0x00が読み出されたとする。
(2) TaskAは変更が完了する前にTaskBにプリエンプトされてしまう。
(3) TaskBは現在のPORTAの内容を読み出し、0x00を0x02に変更し、PORTAに書き戻す。その後TaskBは待ち状態に入る。
(4) TaskAがディスパッチされて中断していた処理を再開する。つまり0x00を0x01に変更してPORTAに書き戻す。
(5) TaskBの努力は、無に帰してしまったようだ。
※上記問題はPORTAの様な汎用ポートだけでなく、大域変数でも起こる。
※Read Modify Write専用の命令があり、1命令で実行できる場合は上記問題は起きない。この辺はアキーテクチャ毎にCPU命令を調べておく必要がある。
※関数が複数のタスクや割り込みから呼び出されても安全に機能するとき、リエントラント性が有ると呼ばれる。全ての変数がスタック(自動変数)上やCPUレジスタ上で動いていれば、リエントラント性は確保できる。一次的にでも大域変数を使う関数は、リエントラント性を確保できない。関数ライブラリのマニュアルを読むとリエントラント性について書かれている。
ESP32のGPIOに関してはARM Cortex-Mと同様、GPIOの個別の端子の出力レジスタ値をSETするレジスタ(GPIO_OUT_W1TS_REG)またはRESETするレジスタ(GPIO_OUT_W1TC_REG)がある。このレジスタは書くだけなので、資源の競合が起きない。
http://hamayan.blog.so-net.ne.jp/2018-02-22 のコードを、以下の様に変更する事ができる。
const int ledPin = 2; hw_timer_t * timer = NULL; volatile SemaphoreHandle_t timerSemaphore; #define GPIObit0To31OutReg *((volatile unsigned long *)GPIO_OUT_REG) #define GPIObit0To31Set *((volatile unsigned long *)GPIO_OUT_W1TS_REG) #define GPIObit0To31Reset *((volatile unsigned long *)GPIO_OUT_W1TC_REG) void IRAM_ATTR onTimer() { // Give a semaphore that we can check in the loop xSemaphoreGiveFromISR( timerSemaphore, NULL ); } void setup() { Serial.begin( 115200 ); Serial.println( "FreRTOS Test." ); pinMode( ledPin, OUTPUT ); digitalWrite( ledPin, LOW ); /* turn off led. */ // Create semaphore to inform us when the timer has fired timerSemaphore = xSemaphoreCreateBinary(); // Use 1st timer of 4 (counted from zero). // Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more // info). timer = timerBegin( 0, 80, true ); // Attach onTimer function to our timer. timerAttachInterrupt( timer, &onTimer, true ); // Set alarm to call onTimer function every second (value in microseconds). // Repeat the alarm (third parameter) timerAlarmWrite( timer, 100 * 1000UL, true ); // Start an alarm timerAlarmEnable( timer ); /* configure led blink task. */ xTaskCreatePinnedToCore( task1, /* 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 task1( void *execParam ) { while( 1 ) { if( xSemaphoreTake(timerSemaphore, 0) == pdTRUE ) { // digitalWrite( ledPin, ( digitalRead(ledPin) ) ? LOW : HIGH ); /* toggle led. */ if( GPIObit0To31OutReg & (1UL << ledPin) ) GPIObit0To31Reset = (1UL << ledPin); else GPIObit0To31Set = (1UL << ledPin); } } }
※ESP32のesp32_hal_gpio.cの中で以下の様なコードとなっているので、マルチタスクでdigitalWriteを使っても大丈夫と思われる。
extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { if(val) { if(pin < 32) { GPIO.out_w1ts = ((uint32_t)1 << pin); } else if(pin < 34) { GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32)); } } else { if(pin < 32) { GPIO.out_w1tc = ((uint32_t)1 << pin); } else if(pin < 34) { GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32)); } } }

ITRONプログラミング入門 H8マイコンとHOSで始める組み込み開発
- 出版社/メーカー: オーム社
- 発売日: 2005/04/23
- メディア: Kindle版

リアルタイムOSと組み込み技術の基礎―実践μITRONプログラミング (TECHI (Vol.17))
- 作者: 高田 広章
- 出版社/メーカー: CQ出版
- 発売日: 2004/02
- メディア: 単行本