CPU アーキテクチャ (割り込みのネスティングと優先順位付け、ソフトウェア割り込みのサポートなど) に応じて、この猫の皮を剥ぐ方法は 100 通りありますが、比較的簡単に理解でき、競合状態やリソース共有のない非常に単純なアプローチを採用しましょう。プリエンプティブ カーネルの危険性。
(免責事項: 通常、最初に選択するのはプリエンプティブなリアルタイム カーネルです。それらの多くは、リソースが非常に制約されたシステムで実行できます... SecurityMatt の提案は適切ですが、独自のプリエンプティブル カーネル/タスク スイッチャー、特に 1 つを実装することに慣れていない場合非同期 (割り込みによってトリガーされる) プリエンプションを処理するカーネルを使用すると、かなり迅速に車軸に取り掛かることができます. したがって、以下で提案しているものは、プリエンプション ベースのカーネルほど応答性が高くありませんが、はるかに単純で、多くの場合適切です)。
3 つのイベント/ワーク キューを作成します。
- Q1 は優先度が最も低く、低速のバックグラウンド SD カード書き込みを処理します。
- Q2 は、着信 UART パケットを処理するための要求を保持します
- Q3 (最高の優先順位) は、UART RX FIFO 読み取り要求を保持します。
UART RX FIFO の読み取りと読み取りパケットの処理を分割して、FIFO の読み取りが常にパケット処理の前に処理されるようにします。多分あなたはそれらを一緒に保ちたいと思うでしょう、あなたの選択。
これを機能させるには、大規模な (〜 100 ミリ秒) SD カード書き込みプロセスを、一連の小さな個別の完了ステップに分割します。
たとえば、5 つのブロックをそれぞれ 20 ミリ秒で書き込むには、最初のブロックを書き込み、次に「次のブロックを書き込む」を Q1 にエンキューします。各ステップの最後にスケジューラに戻り、Q3 から始まる優先順位でキューをスキャンします。Q2 と Q3 が空の場合、Q1 から次のイベントを引き出し (「次のブロックを書き込む」)、そのコマンドをさらに 20 ミリ秒実行してから、戻ってキューを再度スキャンします。20 ミリ秒では応答が不十分な場合は、各 20 ミリ秒のブロック書き込みをより細かい一連のステップに分割し、次の作業ステップを継続的に Q1 に送信します。
次に、受信する UART について説明します。UART RX ISR では、Q3 で「UART FIFO の読み取り」コマンドを単純にエンキューし、割り込みから、中断された 20ms の「ブロックの書き込み」ステップに戻ります。CPU が書き込みを終了するとすぐに、優先順位に従ってキューをスキャンします (ブロック書き込みが割り込み時に開始された場合、最悪の場合の応答は 20 ミリ秒になります)。キュー スキャナー (スケジューラー) は、Q3 に実行すべき作業があることを認識し、そのコマンドを実行してから、戻って再度スキャンします。
最悪の場合、システムの応答性は、優先度に関係なく、システム内の実行から完了までの最長ステップによって決まります。小規模で個別の実行から完了までのステップで作業を行うことにより、システムの応答性を非常に高く保ちます。
ここでは一般論で話さなければならないことに注意してください。ISR で UART RX FIFO を読み取り、データをバッファーに入れ、FIFO の実際の読み取りではなく、パケット処理のみを延期したい場合があります (その場合、キューは 2 つしかありません)。これは自分で解決する必要があります。しかし、私はアプローチが理にかなっていることを願っています。
キューに優先順位を付けたこのイベント ドリブン アプローチは、まさにQuantum Platform (QP) イベント ドリブン フレームワークで使用されるアプローチです。QP は実際には、ここで説明したような基本的な非プリエンプティブ (協調) スケジューラ、またはイベントがキューに入れられるたびにスケジューラを実行するプリエンプティブ スケジューラ (SecurityMatt によって提案されたアプローチと同様) をサポートします。QP の Web サイトで、QP の協調スケジューラのコード/実装を確認できます。