目次
例証します:
内容について:
1) 以下の内容は主に概念的な理解とステップ分析です。
2) 個人的なサンプルコードはまだありませんので、FreeRTOS の公式サンプルコードを使用します。
3) テスト用コードを移植したい場合は、以下のコンテンツに個人的なテスト用サンプルコードはありませんので、他を探してください。
その他について:
1) オペレーティングシステム: win10
2) プラットフォーム: keil 5 mdk
3) 言語: C 言語
4) ボード:STM32シリーズをFreeRTOSに移植
1. キューの紹介
1.1. キューとは何ですか?
キューは、タスクからタスクへ、タスクから割り込みへ、そして割り込みからタスクへデータをやり取りするためのメカニズム (メッセージ メカニズム)です。
1.2. キューの利点
1) ベアメタルで一般的に使用されるグローバル変数と比較して、FreeRTOS のキューはデータのセキュリティを保証します。
2) 複数のタスクが同時に変数を操作する場合、以下の図 1 および 2 に示すように、変数の読み取りおよび書き込みデータは安全ではありません。
図1 図2
1.3. キュー実装機能
1) FreeRTOS はキューに基づいており、キュー セット、相互排他セマフォ、カウント セマフォ、バイナリ セマフォ、再帰的相互排他セマフォなどを含むさまざまな機能を実装しています。
2) 読み取りキューと書き込みキューはマルチタスク干渉を防ぐために保護されており、これらを使用する場合は、以下の図 3 と図 4 に示すように、関連する API 関数を呼び出すだけで済みます。
図3 図4
1.4. キューの使用状況を理解する
1) 固定サイズの限られた量のデータをキューに格納できます。キュー内の各データは「キュー アイテム」と呼ばれ、キューに格納できる「キュー アイテム」の最大数はキューの長さと呼ばれます。
2) キューを作成するときは、以下の図 5 に示すように、キューの長さとキュー項目のサイズを指定する必要があります (値は固定されていません)。
図5
1.5. キューの特性
1) データのデキュー方法は通常、「先入れ先出し」(FIFO) データ保存バッファー機構を採用し、最初にキューに入れられたデータが最初に読み取られます。もちろん、「後入れ後出し」(LIFO) 方式も採用されます。設定することもできます。
2) データ転送方法、実際の値転送を使用し、値を転送用のキューに直接入れます。転送ポインタも使用できます。一般に、より大きなデータを転送する場合は、ポインタ転送が使用されます。
3) マルチタスクアクセス。キューは特定のタスクに属していません。どのタスクや割り込みでもキューにメッセージを送信 (エンキュー)/読み取り (デキュー) できます。
4) デキューおよびエンキューのブロック: タスクがメッセージをキューに送信 (エンキュー) するとき、ブロック時間を指定できます。キューがいっぱいでキューに入れることができない場合は、次の 3 つの状況が考えられます。
1. ブロック時間は 0 で、待機を継続せずに直接戻ります。
2. ブロック時間は 0-port_Max_DELAY です。設定されたブロック時間まで待機しますが、この時間内にキューに参加できなかった場合は、戻ります。
3. キューに参加できるまでのブロック時間は port_Max_DELAY です。
注: デキューはチームに参加することと同じであり、重複はありません。
1.6. キュー閉塞処理
1) キューのブロックキューがいっぱいで、キューに参加するタスク X がまだある場合、現時点ではキューに参加できません。まず、タスクをマウントします。
2) デキュー ブロッキング。キューが空で、キューに参加するタスク Y がまだある場合、現時点では (データがないため) デキューできません。まず、タスク Y のステータス リスト項目を pxDelayedTaskList にマウントします。タスク X イベント リストを追加 xTaskWaitingToReceive にマウントされた項目
複数のタスクが同時に「フルキュー」にキューイングされると、これらのタスクはブロッキング状態になります。つまり、複数のタスクが同じキューのスペースを待っています。スペースが出現すると、どのタスクがレディ状態になります。まず状態?
1) 複数のタスクの中で最も優先度の高いタスク
2) 複数のタスクの優先度が同じ場合、待ち時間が最も長いタスクが実行可能状態になります。
1.7. キューのエンキューとエンキューのプロセス
1) 以下の図 6 に示すように、キューを作成します。
図6
2) 以下の図 7 と図 8 に示すように、チームに参加します (ポジション補充)。
図7
図8
3) 以下の図 9 および 10 に示すように、デキュー (ポジション充填)
図9
図10
2. キューの構造
2.1. 構造を理解する
typedef 構造体キュー定義
{ int8_ _t* pcHead /*記憶領域の開始アドレス*/ int8_ _t* pcWriteTo; /*次の書き込み位置*/ Union /*Union*/
{ QueuePointers_ _t xQueue; SemaphoreData_ _t xSemaphore; }u; List_ .t xTasksWaitingToSend; /*リストの送信待ち*/ List_ _t xTasksWaitingToReceive; /*リストの受信待ち*/ volatile UBaseType_ _t uxMessagesWaiting; /*アイドルでないキューの数items*/ UBaseType_ .t uxLength; /*キューの長さ*/ UBaseType_ .t uxltemSize; /*キュー項目のサイズ*/ volatile int8_ _t cRxLock; /*読み取りロック カウンタ*/ volatile int8_ _t cTxLock; /*書き込みロック カウンタ * /
/*その他の条件付きコンパイル*/
}xQUEUE;
2.2. コミュニティの理解
キューで使用する場合:
typedef struct QueuePointers
{ int8_ _t* pcTail; /*記憶領域の終了アドレス*/ int8_ _t * pcReadFrom; /*最後の読み取りキューのアドレス*/ } QueuePointers_ _t;
ミューテックス セマフォおよび再帰的ミューテックス セマフォとともに使用する場合:
typedef struct SemaphoreData
{ TaskHandle_ t xMutexHolder; /* ミューテックス セマフォ ホルダー*/ UBaseType_ t uxRecursiveCallCount; /* 再帰的ミューテックス セマフォ取得カウンター*/ } SemaphoreData_ t;
2.3. キュー構造体格納領域
以下の図 11 に示すように:
図11
3. キューAPI機能
キューを使用する主なプロセス: プロセスの作成 --> 書き込みキュー --> 読み取りキュー
3.1. キュー作成機能
1) 関数名、xQueueCreate()、機能:キューを動的に作成
2) 関数名、xQueueCreateStatic()、機能:静的にキューを作成
3) 2 つの違い: 動的に作成されたキュー メモリは FreeRTOS によって管理されるメモリによって動的に割り当てられますが、静的に作成される場合はユーザーが自分でメモリを割り当てる必要があります。
コード部分:
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#endif
パラメータの説明:
uxQueueLength、意味: キューの長さ
uxItemSize、意味: キュー項目のサイズ
queueQUEUE_TYPE_BASE、意味: キューはどのような機能を実装しますか?
オプションのパラメータを図 12 に示します。
図12
戻り値の説明:
戻り値: NULL、意味: キューの作成に失敗しました
戻り値: その他の値、意味: キューが正常に作成されました
3.2. エンキュー機能
以下の図 13 に示すように:
図13
コード部分:
以下の図 14 に示すように:
図14
結合位置は次の図 15 に示すとおりです。
図15
エンキューエントリ関数:
BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition );
パラメータの説明:
xQueue、意味: 書き込まれるキュー
pvItemToQueue、意味: 書き込まれるメッセージ
xTicksToWait、意味: タイムアウトのブロック
xCopyPosition、意味: メッセージ書き込み位置
戻り値の説明:
戻り値: pdTRUE、意味: キューの書き込みが成功した
戻り値: errQUEUE_FULL、意味: キューの書き込みに失敗しました
3.3. デキュー機能
以下の図 16 に示すように:
図16
コード部分:
BaseType_t xQueueReceive( QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
パラメータの説明:
xQueue、意味: 読み出されるキュー
pvBuffer、意味: メッセージ読み取りバッファ領域
xTicksToWait、意味: タイムアウトのブロック
戻り値の説明:
戻り値: pdTRUE、意味: キューの書き込みが成功した
戻り値: pdFALSE、意味: キューの書き込みに失敗しました
例証します:
メッセージが正常に読み取られた後、この関数は読み取ったメッセージを削除しますが、xQueuePeek 関数は読み取ったメッセージを削除しません。
4. キューAPI機能の実装手順
4.1. キュー作成API関数
名前: xQueueCreate
実装プロセス:
1) 実際に実行されるのは xQueueGenericCreate( )
2) xQueueGenericCreate( ( uxQueueLength ). ( uxltemSize ), (
queueQUEUE_ TYPE. BASE))
3) キューに必要なメモリ量を計算 xQueueSizeInBytes = ( size. t)( uxQueueLength *
uxltemSize )
4) as キューはメモリに適用され、アプリケーション サイズは次のとおりです: sizeof(Queue, t) + xQueueSizeInBytes. 前部には
構造体のメンバーが格納され、後部にはキュー項目が格納されます。
5) メモリ アプリケーションが成功したかどうかを確認します。成功すると、キューアイテム格納領域の先頭アドレスが計算されます
6) prvlinitialiseNewQueue()を呼び出し、新しいキュー pxNewQueue を初期化します1. キュー構造体のメンバー変数を初期化します。
2. xQueueGenericReset() を呼び出してキューをリセットします。
4.2. キュー書き込みデータAPI関数
名前: xQueueSend
実装プロセス:
1) 実際に実行される内容は次のとおりです: xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition );2) クリティカルセクションに入る (割り込みをオフにする)
3) キューがいっぱいかどうかを確認します
4) キューに空きスペースがある場合は、
1. メッセージは、キューに空き領域がある場合、または上書きされる場合にのみ書き込めます。
2. 空き領域または上書きがある場合、指定された書き込み方法に従って書き込みメッセージをキューにコピーします。
3. メッセージを読み取れないためにブロックされているタスクがあるかどうかを確認し、タスクがある場合は、次の手順でブロックを解除します。 xTaskRemoveFromEventList() 関数 --> スケジューラが一時停止されているかどうかを確認します。
1. 一時停止されていない: 対応するイベント リスト項目とステータス リスト項目が削除され、タスクが準備完了リストに追加されます。
2. 一時停止: イベント リスト項目が削除され、イベント リスト項目が待機準備リスト項目に追加されます: xPendingReadyList. 回復タスク スケジューラ xTaskResumeALL() が呼び出されると、xPendingReadyList 内のタスクが処理されます。
4. クリティカルセクションを終了する
5) キューがいっぱいの場合
1. 現時点ではメッセージを書き込むことができないため、タスクをブロックする必要があります。
2. ブロック時間が 0 の場合は、ブロックがないことを意味し、キューがいっぱいであるというエラーが直接返されます。
3. ブロック時間が 0 でない場合、タスクをブロックする必要があります。このときのシステム ビート カウンタの値とオーバーフロー数を記録します。これは、以下のブロック時間を補正するために使用されます。
4.3. キューデータ読み込みAPI関数
名前: xQueueReceive
実装プロセス:
1) クリティカルセクションに入る
2) キューが空かどうかを確認します。
3) データがあれば、
1. 関数 prvCopyDataFromQueue() を使用してデータをコピーします
2. キュー項目の数を 1 つ減らします。
3. キュー項目が以前に減算されているため、キューに空きがあります。xTaskWaitingToSend 待機リストにタスクがある場合は、状態のブロックを解除し、xTaskRemoveFromEventList() 関数を使用してスケジューラが一時停止されているかどうかを判断します。
1. 一時停止されていない: 対応するイベント リスト項目とステータス リスト項目が削除され、タスクが準備完了リストに追加されます。
2. 一時停止: イベント リスト項目が削除され、イベント リスト項目が待機準備リスト項目に追加されます: xPendingReadyList. 回復タスク スケジューラ xTaskResumeALL() が呼び出されると、xPendingReadyList 内のタスクが処理されます。
4. クリティカルセクションを終了します (割り込みを有効にします)
4) データなし
1. 現時点ではメッセージを読み取ることができないため、タスクはブロックされています。
2. ブロック時間が 0 の場合は、ブロックがないことを意味し、キューが空であるというエラーが直接返されます。
3. ブロック時間が 0 でない場合、タスクをブロックする必要があります。このときのシステム ビート カウンタの値とオーバーフロー数を記録します。これは、以下のブロック時間を補正するために使用されます。
4. ブロッキング時間を補正した後もブロッキングを継続する必要があるかどうかを判断し、
1. 必須: タスクのイベント リストを受信待ちリストに追加し、タスク ステータス リストの項目をブロック対象リストに追加し、キューのロックを解除し、スケジューラを復元します。
2. 不要: キューのロックを解除し、スケジューラを復元し、キューが空であるというエラーを返します。