いくつかのマシンで役に立つとわかったものの1つは、単純なスタックスイッチャーです。私は実際にPIC用に作成していませんが、両方/すべてのスレッドが合計31以下のスタックレベルを使用している場合、PIC18でこのアプローチがうまく機能すると期待しています。8051のメインルーチンは次のとおりです。
_taskswitch:
xch a、SP
xch a、_altSP
xch a、SP
ret
PICでは、スタックポインターの名前を忘れますが、ルーチンは次のようになります。
_taskswitch:
movlb _altSP >> 8
movf _altSP、w、b
movff _STKPTR、altSP
movwf _STKPTR、c
帰る
プログラムの開始時に、altSPに代替スタックのアドレスをロードするtask2()ルーチンを呼び出し(16はおそらくPIC18Fxxに適しています)、task2ループを実行します。このルーチンは決して戻ってはいけません。さもないと物事は痛みを伴う死で死にます。代わりに、プライマリタスクに制御を渡したい場合は、_taskswitchを呼び出す必要があります。プライマリタスクは、セカンダリタスクに譲りたいときはいつでも_taskswitchを呼び出す必要があります。多くの場合、次のようなかわいいルーチンがあります。
void delay_t1(unsigned short val)
{
行う
taskswitch();
while((unsigned short)(millisecond_clock-val)> 0xFF00);
}
タスクスイッチャーには、「条件待機」を行う手段がないことに注意してください。サポートするのはスピンウェイトだけです。一方、タスクスイッチは非常に高速であるため、他のタスクがタイマーの期限切れを待っている間にtaskswitch()を試行すると、他のタスクに切り替わり、タイマーをチェックし、通常のタスクスイッチャーよりも速く切り替えますタスクを切り替える必要がないと判断します。
協調型マルチタスクにはいくつかの制限がありますが、一時的に妨害された不変式をすぐに再確立できる場合、多くのロックやその他のミューテックス関連コードの必要性を回避することに注意してください。
(編集):自動変数などに関するいくつかの注意事項:
- タスク切り替えを使用するルーチンが両方のスレッドから呼び出される場合、通常、ルーチンの2つのコピーをコンパイルする必要があります(異なる#defineステートメントを使用して、同じソースファイルを2回含めるなど)。特定のソースファイルには、1つのスレッドのみのコードが含まれるか、スレッドごとに1回ずつ2回コンパイルされるコードが含まれるため、「#define delay(x)delay_t1(x)」などのマクロを使用できます。 #define delay(x)delay_tx(x) "使用しているスレッドに応じて。
- 呼び出されている関数を「見る」ことができないPICコンパイラは、そのような関数がすべてのCPUレジスタを破壊する可能性があると想定しているため、タスクスイッチルーチンにレジスタを保存する必要がありません[プリエンプティブマルチタスク]。他のCPUに対して同様のタスクスイッチャーを検討している人は、使用中のレジスタ規則を知っている必要があります。タスクを切り替える前にレジスタをプッシュし、その後ポップすることは、適切なスタックスペースが存在することを前提として、物事を処理する簡単な方法です。
協調マルチタスクでは、ロックなどの問題を完全に回避することはできませんが、実際には非常に単純化されます。たとえば、圧縮ガベージコレクターを備えたプリエンプティブRTOSでは、オブジェクトを固定できるようにする必要があります。協力的なスイッチャーを使用する場合、taskswitch()が呼び出されたときにGCオブジェクトが移動する可能性があるとコードが想定している限り、これは必要ありません。ピン留めされたオブジェクトを心配する必要のない圧縮コレクターは、それを行うコレクターよりもはるかに簡単です。