RTOSについての真実。 記事#24。 キュー:補助サービスとデータ構造



この記事では、引き続きキューについて検討します。

セカンダリキューサービス


Nucleus RTOSには、キューに関連する補助機能を提供する4つのAPIコールがあります。キューのリセット、キュー情報の取得、アプリケーション内のキューの数の取得、アプリケーション内のすべてのキューへのポインターの取得です。 最初の3つの機能はNucleus SEに実装されています。

シリーズの以前の記事:

記事#23。 キュー:紹介と基本サービス
記事#22。 メールボックス:補助サービスとデータ構造
記事#21。 メールボックス:紹介と基本サービス
記事20。 セマフォ:補助サービスとデータ構造
記事#19。 セマフォ:紹介と基本サービス
記事#18。 イベントフラググループ:ヘルパーサービスとデータ構造
記事#17。 イベントフラググループ:紹介と基本サービス
記事#16。 信号
記事#15。 メモリパーティション:サービスとデータ構造
記事#14。 メモリのセクション:紹介と基本サービス
記事#13。 タスクデータ構造とサポートされていないAPI呼び出し
記事#12。 タスクを操作するためのサービス
記事#11。 タスク:APIの構成と紹介
記事#10。 スケジューラ:高度な機能とコンテキストの保存
記事#9。 スケジューラ:実装
記事#8。 Nucleus SE:内部設計と展開
記事#7。 Nucleus SE:はじめに
記事#6。 その他のRTOSサービス
記事5。 タスクの相互作用と同期
記事4。 タスク、コンテキストの切り替え、および割り込み
記事#3。 タスクと計画
記事#2。 RTOS:構造とリアルタイム
記事#1。 RTOS:はじめに。

キューのリセット


このAPI呼び出しは、キューを元の未使用の状態にリセットします。 キューに保存されているメッセージはすべて失われます。 キューで一時停止されたタスクは、戻りコードNUSE_QUEUE_WAS_RESETで再開されます。

Nucleus RTOSのキューリセットコール
サービスコールのプロトタイプ:
STATUS NU_Reset_Queue(NU_QUEUE *キュー);

パラメータ:
queue-ユーザーが提供するキュー制御ブロックへのポインター。

戻り値:
NU_SUCCESS-呼び出しは正常に完了しました。
NU_INVALID_QUEUE-無効なキューポインター。

Nucleus SEのキューリセットコール
このユーティリティ呼び出しは、Nucleus RTOS APIのコア機能をサポートしています。

サービスコールのプロトタイプ:
STATUS NUSE_Queue_Reset(NUSE_QUEUEキュー);

パラメータ:
queue-ダンプされたキューのインデックス(ID)。

戻り値:
NUSE_SUCCESS-呼び出しは正常に完了しました。
NUSE_INVALID_QUEUE-無効なキューインデックス。

Nucleus SEでのキューリセットの実装
NUSE_Queue_Reset関数のコード(パラメーターをチェックした後)は非常に単純です。 キューの先頭と末尾のインデックス、およびキュー内のメッセージカウンターにはゼロ値が割り当てられます。

タスクのロックが有効になっている場合、中断されたタスクを復元するための追加のコードがあります。

while (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this queue */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { NUSE_Task_Blocking_Return[index] = NUSE_QUEUE_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Queue_Blocking_Count[queue]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

キュー内の中断された各タスクには、戻りコードNUSE_QUEUE_WAS_RESETでステータス「準備完了」が割り当てられます。 このプロセスが完了した後、優先度スケジューラーが使用されている場合、 NUSE_Reschedule()関数が呼び出されます。優先度の高い1つ以上のタスクを実行する準備ができている可能性があるためです。

キュー情報を取得する


このサービスコールは、キュー情報を提供します。 Nucleus SEでのこの呼び出しの実装は、オブジェクトの命名、可変メッセージ長、タスクの一時停止順序がサポートされておらず、タスクのロックを無効にできるため、返される情報が少ないという点でNucleus RTOSと異なります。

Nucleus RTOSでのキュー情報の呼び出し
サービスコールのプロトタイプ:

STATUS NU_Queue_Information(NU_QUEUE * queue、CHAR * name、VOID ** start_address、UNSIGNED * queue_size、UNSIGNED * available、UNSIGNED * messages、OPTION * message_type、UNSIGNED * message_size、OPTION * suspend_type、UNSIGNED * first_task_task_task

パラメータ:

queue-ユーザーが提供するキュー制御ブロックへのポインター。
name-キュー内のメッセージ名の8文字領域へのポインター。
start_address-キューデータ領域の先頭のアドレスが書き込まれるポインターへのポインター。
queue_size-キュー内のUNSIGNED要素の総数を格納するための変数へのポインター。
available-キュー内の使用可能なUNSIGNED要素の数を格納する変数へのポインター。
messages-キュー内の現在のメッセージ数を保存する変数へのポインタ。
message_type-キューでサポートされているメッセージのタイプを保存するための変数へのポインター。 有効な値はNU_FIXED_SIZEおよびNU_VARIABLEです。
message_size-キューの各メッセージのUNSIGNEDデータ要素の数を格納する変数へのポインター。 キューが可変長メッセージをサポートする場合、この数値はメッセージの最大長を示します。
suspend_type-タスクの一時停止のタイプを格納するための変数へのポインター。 有効な値はNU_FIFOおよびNU_PRIORITYです。
tasks_waiting-このキューで中断されたタスクの数を格納する変数へのポインター。
first_task-タスクポインターへのポインター。最初の一時停止タスクのポインターが配置されます。

戻り値:

NU_SUCCESS-呼び出しは正常に完了しました。
NU_INVALID_QUEUE-無効なキューポインター。

Nucleus SEでのキュー情報の呼び出し
このAPIコールは、Nucleus RTOS APIのコア機能をサポートしています。

サービスコールのプロトタイプ:

STATUS NUSE_Queue_Information(NUSE_QUEUEキュー、ADDR * start_address、U8 * queue_size、U8 * available、U8 * messages、U8 * tasks_waiting、NUSE_TASK * first_task);

パラメータ:

queue-情報が要求されるキューのインデックス。
start_address-タイプADDRの変数へのポインタ。キューのデータ領域の先頭のアドレスを格納します。
queue_size-キューに収まるメッセージの総数を格納するU8型の変数へのポインター。
available-キュー内の空き場所の数を格納するU8型の変数へのポインター。
messages-現在のメッセージ数をキューに格納するU8型の変数へのポインター。
tasks_waiting-このキューで中断されたタスクの数が格納される変数へのポインター(タスクのロックが無効になっている場合は何も返されません);
first_task - NUSE_TASKタイプの変数へのポインター。最初の一時停止タスクのインデックスが格納されます(タスクロックが無効になっている場合は何も返されません)。

戻り値:

NUSE_SUCCESS-呼び出しは正常に完了しました。
NUSE_INVALID_QUEUE-無効なキューインデックス。
NUSE_INVALID_POINTER -1つ以上のポインターパラメーターが正しくありません。

Nucleus SEでのキュー情報表示の実装

このAPI呼び出しの実装は非常に簡単です。

 *start_address = NUSE_Queue_Data[queue]; *queue_size = NUSE_Queue_Size[queue]; *available = NUSE_Queue_Size[queue] - NUSE_Queue_Items[queue]; *messages = NUSE_Queue_Items[queue]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Queue_Blocking_Count[queue]; if (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif 

この関数は、キューのステータスを返します。 次に、タスクのロックが有効になっている場合、保留中のタスクの数と最初のタスクのインデックスが返されます(それ以外の場合、両方のパラメーターが0に設定されます)。

キューの数を取得する


このユーティリティ呼び出しは、アプリケーションで設定されたキューの数を返します。 Nucleus RTOSでは、それらの数は時間とともに変化する可能性があり、戻り値は現在のキューの数を示します。 Nucleus SEでは、ビルド段階で戻り値が設定され、変更できません。

Nucleus RTOSでキューカウンターを呼び出す
サービスコールのプロトタイプ:
UNSIGNED NU_Established_Queues(VOID);

パラメータ:
欠席しています。

戻り値:
システムで作成されたキューの数。

Nucleus SEでキューカウンターを呼び出す
このAPIコールは、Nucleus RTOS APIのコア機能をサポートしています。

サービスコールのプロトタイプ:
U8 NUSE_Queue_Count(void);

パラメータ:
欠席しています。

戻り値:
アプリケーションで構成されたキューの数。

Nucleus SEでのキューカウンターの実装
このAPI呼び出しの実装は非常に簡単です。 #define NUSE_QUEUE_NUMBERシンボルの値が返されます。

データ構造


キューは5つまたは6つのデータ構造(RAMまたはROM内)を使用します。これはテーブルのセットであり(他のNucleus SEオブジェクトと同様)、その数とサイズはアプリケーション内のキューの数と選択したパラメーターに対応します。

RAMのカーネルデータ


このデータの構造は次のとおりです。

NUSE_Queue_Head []は、タイプU8のポインターの配列で、構成されたキューごとに1つのエントリがあり、メッセージキューの先頭を指します。 NUSE_Queue_Data []でアドレスインデックスとして使用されます(以下を参照)。
NUSE_Queue_Tail []はタイプU8の配列で、アプリケーションで設定された各キューに1つのエントリがあり、メッセージキューの末尾を指します。 NUSE_Queue_Data []でアドレスインデックスとして使用されます(以下を参照)。
NUSE_Queue_Items []はタイプU8の配列であり、設定されたキューごとに1つのエントリがあり、キュー内のメッセージカウンターです。 これらの値はキューの最初と最後のインデックスから取得できるため、このデータは冗長であると見なすことができますが、カウンターを保存するとコードが簡素化されます。
NUSE_Queue_Blocking_Count [] -タイプU8のこの配列には、各キューで中断されたタスクの数のカウンターが含まれます。 この配列は、タスクロックのサポートが有効になっている場合にのみ作成されます。

これらのデータ構造は、Nucleus SEの起動時にNUSE_Init_Queue()関数によってゼロに初期化されます。 すべてのキューが空(作成されていない)で作成されるため、これは論理的です。

以下は、 nuse_init.cファイルのこれらの構造の定義です。

 RAM U8 NUSE_Queue_Head[NUSE_QUEUE_NUMBER]; RAM U8 NUSE_Queue_Tail[NUSE_QUEUE_NUMBER]; RAM U8 NUSE_Queue_Items[NUSE_QUEUE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Queue_Blocking_Count[NUSE_QUEUE_NUMBER]; #endif 

RAMユーザーデータ


ユーザーは、各キューを格納するためのRAM領域を提供する責任があります。 この領域のサイズには、各レコードがキュー内の1つのメッセージに対応するタイプADDRの配列が含まれている必要があります

ROMデータ


このデータの構造は次のとおりです。

NUSE_Queue_Data [] -タイプADDRの配列は、構成されたキューごとに1つのエントリを持ち、キューのデータ領域を指します(ユーザーRAMデータを参照)。
NUSE_Queue_Size []はタイプU8の配列であり、設定されたキューごとに1つのエントリがあり、各キューが受信できるメッセージの最大数を示します。

これらのデータ構造は、 nuse_config.cファイルで(静的に)宣言および初期化されます。

 ROM ADDR *NUSE_Queue_Data[NUSE_QUEUE_NUMBER] = { /* addresses of queue data areas ------ */ }; ROM U8 NUSE_Queue_Size[NUSE_QUEUE_NUMBER] = { /* queue sizes ------ */ }; 

キューのメモリ量


すべてのNucleus SEカーネルオブジェクトと同様に、キューに必要なメモリ量は簡単に予測できます。

アプリケーション内のすべてのキューのROM内のデータ量(バイト単位)は、次のように計算できます。
NUSE_QUEUE_NUMBER *(sizeof(ADDR)+ 1)

タスクロックが有効になっているアプリケーション内のすべてのキューのRAM内のカーネルデータの量(バイト単位)は、次のように計算されます。
NUSE_QUEUE_NUMBER * 3

ロックが無効になっている場合:
NUSE_QUEUE_NUMBER * 4

キューインデックスを持つキューの RAM内のユーザーデータの量(バイト単位):
NUSE_Queue_Size [queue] * sizeof(ADDR)

未実現のAPI呼び出し


Nucleus RTOSにある4つのAPI呼び出しは、Nucleus SEには実装されていません。

キュー作成


このAPIコールはキューを作成します; Nucleus SEでは、キューは静的に作成されるため、これは必要ありません。

サービスコールのプロトタイプ:
STATUS NU_Create_Queue(NU_QUEUE * queue、char * name、VOID * start_address、UNSIGNED queue_size、OPTION message_type、UNSIGNED message_size、OPTION suspend_type);

パラメータ:

queue-ユーザーが提供するコントロールユニットへのポインター;他のAPI呼び出しでキューを管理するために使用されます。
name-終了バイトがnullの7文字のキュー名へのポインタ。
start_address-キューの先頭のアドレス。
message_typeは、キューでサポートされているメッセージタイプです。 NU_FIXED_SIZEまたはNU_VARIABLE_SIZEの値を取ることができます。
message_size-キューが固定長のメッセージをサポートする場合、このパラメーターは各メッセージの正確な長さを設定します。それ以外の場合、キューが可変長のメッセージをサポートする場合、この値は最大メッセージ長です。
suspend_type-キュー内の一時停止タスクのタイプを決定します。 値NU_FIFOおよびNU_PRIORITYをとることができます。これは、それぞれFIFO(先入れ先出し)の原則またはタスクの中断の優先順位の原則を意味します。

戻り値:

NU_SUCCESS-呼び出しは正常に完了しました。
NU_INVALID_QUEUE-キュー制御ブロックへのNULLポインター( NULL )、またはポインターは既に使用中です。
NU_INVALID_MEMORY - start_addressで指定された無効なメモリ領域。
NU_INVALID_MESSAGE - message_typeパラメーターが無効です。
NU_INVALID_SIZE-キューはこの長さのメッセージをサポートしていないか、キューのサイズおよび/またはメッセージの長さは0です。
NU_INVALID_SUSPEND-無効なsuspend_typeパラメーター。

キューを削除


このAPI呼び出しは、以前に作成されたキューを削除します。 キューは静的に作成され、削除できないため、Nucleus SEはこれを必要としません。

サービスコールのプロトタイプ:
STATUS NU_Delete_Queue(NU_QUEUE *キュー);

パラメータ:
queue-キュー制御ブロックへのポインター。

戻り値:
NU_SUCCESS-呼び出しは正常に完了しました。
NU_INVALID_QUEUE-無効なキューポインター。

キューポインター


このAPI呼び出しは、システム内のすべてのキューへのポインターの順次リストを作成します。 Nucleus SEでは、キューはポインターではなく単純なインデックスを使用して識別されるため、これは必要ありません。

サービスコールのプロトタイプ:
UNSIGNED NU_Queue_Pointers(NU_QUEUE ** pointer_list、UNSIGNED maximum_pointers);

パラメータ:
pointer_list-ポインターの配列NU_QUEUEへのポインター。 この配列には、システムで作成されたキューへのポインターが格納されます。
maximum_pointers-配列内のポインターの最大数。

戻り値:
配列内のNU_QUEUEポインターの数。

キューイング(キューへのブロードキャスト)


このAPI呼び出しは、指定されたキューからのメッセージを待機しているキューで中断されているすべてのタスクにメッセージを渡します。 この機能は冗長性を追加するため、Nucleus SEには実装されていません。

サービスコールのプロトタイプ:
STATUS NU_Broadcast_To_Queue(NU_QUEUE *キュー、VOID *メッセージ、UNSIGNEDサイズ、UNSIGNEDサスペンド);

パラメータ:

queue-キュー制御ブロックへのポインター。
message-送信されたメッセージへのポインター。
size-メッセージ内のUNSIGNED要素の数。 キューが可変長メッセージをサポートする場合、このパラメーターはキューでサポートされるメッセージの長さ以下でなければなりません。 キューが固定長のメッセージをサポートする場合、このパラメーターはキューがサポートするメッセージの長さと等しくなければなりません。
suspen-キューがすでにいっぱいになっている場合、呼び出しタスクを中断するかどうかを示します。 NU_NO_SUSPENDNU_SUSPEND、またはタイムアウト値になります。

戻り値:

NU_SUCCESS-呼び出しは正常に完了しました。
NU_INVALID_QUEUE-無効なキューポインター。
NU_INVALID_POINTER-メッセージへのNULLポインター( NULL );
NU_INVALID_SIZE-指定されたメッセージの長さは、キューの作成時に指定された長さと互換性がありません。
NU_INVALID_SUSPEND-タスクに関連付けられていないスレッドからタスクを一時停止しようとします。
NU_QUEUE_FULL-メッセージ用のキューに十分なスペースがありません。
NU_TIMEOUT-タイムアウトが経過した後もキューはまだいっぱいです。
NU_QUEUE_DELETED-タスクの中断中にキューが削除されました。
NU_QUEUE_RESET-タスクの中断中にキューがリセットされました。

Nucleus RTOS互換


他のすべてのNucleus SEオブジェクトと同様に、私の目標はアプリケーションコードとNucleus RTOSとの互換性を最大限にすることでした。 キューも例外ではなく、ユーザーの観点からは、Nucleus RTOSと同じ方法で実装されます。 また、結果としてコードが必要なメモリ量の点でより理解しやすく効率的になることを考えると、許容できると考えた特定の非互換性もあります。 それ以外の場合、Nucleus RTOS APIコールはほぼ直接Nucleus SEに移植できます。

オブジェクト識別子


Nucleus RTOSでは、すべてのオブジェクトは特定のデータタイプを持つデータ構造(コントロールユニット)によって記述されます。 この制御ユニットへのポインタは、キューの識別子として機能します。 Nucleus SEでは、メモリを効率的に使用するために別のアプローチが必要であると判断しました。すべてのカーネルオブジェクトは、RAMおよび/またはROMのテーブルのセットによって記述されます。 これらのテーブルのサイズは、各タイプの設定済みオブジェクトの数によって決まります。 特定のオブジェクトの識別子は、このテーブルのインデックスです。 そこで、 NUSE_QUEUEU8に相当するものとして定義しました 。このタイプの変数(ポインターではない)がキュー識別子として機能します。 コードをNucleus SEからNucleus RTOSに、またはその逆に移植する場合、このわずかな非互換性は簡単に処理できます。 通常、移動と保存以外のオブジェクト識別子に対する操作は実行されません。

Nucleus RTOSはキューの命名もサポートしています。 これらの名前は、デバッグにのみ使用されます。 メモリを節約するために、Nucleus SEからそれらを除外しました。

メッセージのサイズとタイプ


Nucleus RTOSでは、任意の数の未署名要素で構成されるメッセージを処理するようにキューを設定できます。 Nucleus SEでは、キューは単純化され、単一のADDRタイプのメッセージのみをサポートします。 Nucleus SEのデータチャネルはもう少し柔軟性があり、場合によってはキューの便利な代替手段になります。 チャンネルについては、このシリーズの次の2つの記事で説明します。

Nucleus SEは、可変長メッセージキューもサポートしています。これは、作成時に最大メッセージ長のみを指定します。 Nucleus SEは可変メッセージ長をサポートしていません

キューサイズ


Nucleus SEでは、すべての変数と定数はU8型であるため、キュー内のメッセージの最大数は256です。 Nucleus RTOSにはそのような制限はありません。

未実現のAPI呼び出し


Nucleus RTOSは10個のキュー管理呼び出しをサポートしています。 これらのうち、4つはNucleus SEには実装されていません。 これらの呼び出しの詳細、およびこの決定の理由は、上記の「未実現API呼び出し」セクションの記事に記載されています。

次の記事では、データ伝送チャネルについて説明します。

著者について: Colin Wallsは電子業界で30年以上働いており、ほとんどの時間をファームウェアに費やしています。 現在、Mentor Embedded(Mentor Graphicsの一部門)のファームウェアエンジニアです。 Colin Wallsは、カンファレンスやセミナー、多くの技術記事の著者、ファームウェアに関する2冊の本でよく話します。 英国に住んでいます。 Colinのプロフェッショナルブログ 、電子メール:colin_walls@mentor.com。

Source: https://habr.com/ru/post/J432804/


All Articles