プリエンプティブ ラウンド ロビン スケジューラを使用する KEIL RTX RTOS を使用しています。データを表示するための LCD があり、いくつかのタスクがこの LCD にアクセスできます (他にもいくつかのタスクがあります)。たとえば、最初のタスク ハンドル LCD はデータを 50 秒間表示し、50 秒後、2 番目のタスクはデータを 10 秒間処理して表示します)。LCD へのアクセスを管理するためにミューテックスを使用する必要があることはわかっていますが、一定時間管理する方法がわかりません。LCD タスクは優先度が最も低く、他に実行するタスクがない場合、これらのタスクはメッセージを表示するために実行されます。 .
3 に答える
複数のスレッドがミューテックスによって調停される単一のリソースにアクセスするのではなく、単一のスレッドがリソースを処理する方が簡単です。
この場合、私はディスプレイ マネージャー スレッドを提案します。他のスレッドは、おそらくディスプレイ バッファーへのポインターと必要な表示期間を提供するディスプレイ マネージャーに登録できます。次に、ディスプレイ マネージャは、次のスレッドに切り替える前に、必要な期間、バッファを表示する登録済みの各スレッドを単純に循環します。
例(疑似コード):
static struct
{
const char* frame_buffer ;
int display_seconds ;
OS_MUTEX mutex ;
} display_registry[MAX_DISPLAY_THREADS] = {0,0} ;
void displayLock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_lock( display_registry[handle].mutex ) ;
}
}
void displayUnock( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
os_mutex_unlock( display_registry[handle].mutex ) ;
}
}
void lcd_thread
{
int display = 0 ;
for(;;)
{
int t = 0 ;
while( t < display_registry[display].display_seconds &&
display_registry[display].frame_buffer != 0 )
{
displayLock( display ) ;
render_display( display_registry[display].frame_buffer ) ;
displayUnlock( display ) ;
delay( ONE_SECOND ) ;
}
display = (display + 1) % MAX_DISPLAY_THREADS ;
}
}
int displayRegister( const char* frame_buffer, int display_seconds )
{
for( int i = MAX_DISPLAY_THREADS - 1;
frame_buffer[i] != 0 &&
i >= 0; i-- )
{
// do nothing
}
if( i >= 0 )
{
display_registry[i].display_seconds = display_seconds ;
display_registry[i].frame_buffer = frame_buffer ;
}
return i ; // handle for de-registering/locking
}
void displayDeregister( int handle )
{
if( handle >= 0 && handle < MAX_DISPLAY_THREADS )
{
display_registry[handle].frame_buffer = 0 ;
}
}
ミューテックスは LCD リソースをロックするのではなく、共有メモリ フレーム バッファ リソースをロックすることに注意してください。
次に、他のスレッドは、次の例のように、そのデータの表示とは完全に非同期で、表示用のデータを独自のフレーム バッファーに配置するだけです。
displayLock( my_display_handle ) ;
update_display_buffer() ;
displayUnlock( my_display_handle ) ;
最初にあなたの質問に答えようとしますが、それから別のデザインを提案します。
現実世界の時間、特に秒単位で測定されるこれらのような比較的長い期間に基づいて何かを行うには、Timer を使用する必要があります。2 つの Timer オブジェクトを作成します。1 つは期間が 50 秒で、もう 1 つは期間が 10 秒です。両方のタイマーはワンショット タイプである必要があります。また、2 つの Signal オブジェクトを作成します。1 つは 50 秒の期間が終了したことを示し、もう 1 つは 10 秒の期間が終了したことを示します。2 つの Timer オブジェクトは、有効期限が切れたときにそれぞれ別個のコールバック関数を呼び出します。50 秒のコールバック関数は、50 秒の有効期限シグナルを設定してから、10 秒のタイマーを開始する必要があります。10 秒のコールバック関数は、10 秒の有効期限シグナルを設定してから、50 秒のタイマーを再起動する必要があります。タイマーは 2 つの信号を交互に設定しながら、前後にピンポンします。
タスクを使用するリソースは、適切な有効期限シグナルを定期的にチェックする必要があります。タスクは、シグナルが設定されていることを確認すると、リソースを放棄してシグナルをクリアできます。他のタスクは、他のシグナルで同じことを行います。このようにして、2 つのタスクはリソースを放棄するタイミングを認識し、他のタスクがそれを取得できるようにします。
あなたの設計について気になることの 1 つは、リソースを保護する 2 つの同期メカニズムがあることです。ミューテックスは同期メカニズムです。タスクがリソースを非同期に (つまり、ランダムな時間に) 使用するように結合している場合、ミューテックスを使用してそれらの使用を同期し、常に 1 つのタスクだけがリソースを使用するようにすることができます。すでに別の同期メカニズムがある場合は、おそらくミューテックスは必要ありません。つまり、タスクにリソースを使用する個別のタイム スロットがある場合、タスクは既に同期しています。リソースを無作為に使用しようとしない場合は、ミューテックスは必要ないかもしれません。
あなたのデザインも複雑に見えますが、この別のデザインはもっとシンプルではないかと思います。
LCD ディスプレイ インターフェイスを担当する単一のタスクを作成することを検討してください。この LDC タスクは、ディスプレイと連動する唯一のタスクです。データ タスクは、表示するデータを生成するときに LCD タスクにメッセージを送信します。LCD タスクは単にこれらのメッセージを待ち、メッセージが到着すると適切にデータを表示します。データの複雑さと多様性に応じて、このメッセージ サービスにメッセージ キューまたはメール キューのいずれかを使用できます。ミューテックスを使用するタスクは 1 つしかないため、LCD ディスプレイ用のミューテックスは必要ありません。そして、このデザインで 50/10 秒のタイム スプリットが必要ですか? その要件のソースが何であったかがわからないため、わかりません。
前の回答で述べたように、最初に LCD ディスプレイ用の単一のタスクを作成し、次にタイマー イベントを使用してタイム スライスを追跡します。
最後に、タイマー イベント ハンドラでタスクを Yield します (タイム スライスの後に呼び出されます)。
yield について知らない場合、yield は、スケジューラが次のタスクに移動できるようにするために、タスクの実行を放棄する方法です。