第 4 章 より高度な処理を行う 29
4.5 AdStartSamplingを使った永続的なサンプリングの解説
4.5.2 RTLinuxモジュールの動き
ここでは、RTLinuxモジュール(module3.o)の動きを解説します。
RTLinuxモジュールでは、サンプリングを実現するためにinit_module関数内で幾つかリソースを生
成しています。それを下表に示します。項 目 内 容
RT-FIFO(FIFO_COMMAND) Linuxプロセスからの指示を受けるためのRT-FIFOです。
指示は、ハンドラmy_handlerに渡されます。
RT-FIFO(FIFO_THRU_CMD)
ハンドラにて、Linuxプロセスから受け取った情報を、 RTLinuxス
レッド(my_task)に渡すためのRT-FIFOです。
RT-FIFO(FIFO_RESULT) ADデバイスドライバにてサンプリングしたデータを、Linuxプロ
セスに渡すための
RT-FIFOです。
RT-FIFO(FIFO_STATUS) ADドライバモジュールにてサンプリング中の状態をRTLinuxス
レッドで監視し、そのデータをLinuxプロセスに渡すための
RT-FIFOです。
ハンドラ
(my_handler)
Linuxプロセスからの指示を受け取るための処理の入り口です。
RT-FIFO経由で送られた情報は、一旦このハンドラが受け取り、
然る処理に回されます。
RTLinuxスレッド (my_task)
ADドライバモジュールに対して、サンプリングのスタート,スト
ップを指示するRTLinuxスレッドです。サンプリング中は状態を監視し、情報をLinuxプロセスに渡しま す。
コールバック関数
(my_smp_callback)
ADドライバモジュールにてサンプリング中に、指定件数のデー
タが溜まると呼び出されるコールバック関数です。サンプリングデータは、ここで抜き出され、Linuxプロセスに渡 されます。
上表にて、ほとんどのリソース生成は、RTLinuxに用意されたAPIを用いて作成されますが、コー ルバック関数のみADドライバモジュールの提供するAPIを使って生成されます。
次に、生成された各リソースの相互関係と処理の流れを下図に示します。
ハンドラ(my_handler)
RTLinuxスレッド(my_task) FFO_COMMAND
FIFO_THRU_CMD
FIFO_STATUS Linuxプロセス
ADドライバモジュール
ID_START ID_STOP
サンプリング開始
サンプリング停止
周期呼び出し 周期監視
AdGetStatus関数
AdStartSampling関数
AdStopSampling関数
コールバック関数 (my_smp_callback)
データ抜き取り 指定件数サンプリング
AdGetSamplingData関数 FIFO_RESULT コマンド指示
サンプリング状態
サンプリング データ
『37ページ
4.2.2 RTLinuxモジュールの動き』の図と比較してください。
先のサンプルでは、ADデータを取り込むサンプリング処理は、RTLinuxスレッド(my_task)にて行 っていましたが、ここでのサンプリング処理は、ADドライバモジュール内で行われます。
代わりに、RTLinuxスレッドでは、ADドライバモジュールのサンプリング状態を送るようにして います。
実際のサンプリング処理を行っているのは、ADドライバモジュールです。
このサンプルでは、トリガを指定せずにサンプリングしているので、ストップ指令を送らない限 りサンプリングを続けるようになっています。
このままだと、内部サンプリングバッファが固定長なので、サンプリングデータを上書きしてし まうことが考えられます。
サンプリングデータを上書きするのを避けるため、一定件数のデータが集まると、
ADドライバモ
ジュールからコールバックされる関数(my_smp_callback)を登録しています。
サンプルでは、このコールバック関数が呼び出されたら内部サンプリングバッファからデータを 抜き取り、LinuxプロセスにRT-FIFO経由で送っています。
これにより、永続的なサンプリングを実現しています。
LinuxプロセスからRTLinuxスレッドへの指示は、ハンドラ(my_handler)を経由して行われます。
このサンプルのハンドラは、Linuxプロセスからの指示をそのまま横流しする単純なものです。
(157〜171行目:RT-FIFOおよびハンドラの生成)
init_module関数の最初の段階では、RTLinuxモジュールおよびLinuxプロセスで使用するRT-FIFO
および、Linuxプロセスからの指示を受け取るハンドラを生成しています。(174行目:AdOpenEx関数)
次に、AD製品の制御を開始するためにAdOpenEx関数を使ってオープンしています。
ここで取得するデバイス番号は、他の関数で共通して使われるため、グローバル変数g_device_no に格納されています。
(183行目:コールバック関数の登録)
AdSetBoardConfig関数は、指定件数をサンプリングしたらコールバックされる関数を登録してい
ます。登録すべきコールバック関数のプロトタイプ形式は、以下の通りです。void CallbackFunc(int userArg);
第3引数でコールバック関数の関数ポインタ
(上の例では、CallbackFunc)を、第4引数でコールバッ
ク関数の第1引数のパラメータを渡します。例えば、第4引数に123を渡すと、上の例ではuserArg変数に123が渡されます。
(190〜211行目:サンプリング条件の設定)
AdSetSamplingConfig関数は、サンプリング条件の設定を行います。
「どのチャンネルをサンプリングするか?」「どんな入力方式を使うか?」「サンプリング周波 数はいくらでサンプリングするか?」等の設定をここで行います。
サンプルでは、AdGetSamplingConfig関数にてADドライバモジュールが保持するデフォルトのサ ンプリング条件の設定パラメータを取得し、これに修正を加える形式を取っています。
デフォルトのパラメータは、ADドライバモジュールがオープン時に初期値として持っています。
この値は、「その製品でサンプリングできる、理想的と思われる値」がセットされています。
ここでは、デフォルト値に修正を加える形式にすることで、サンプリング条件の諸設定を簡易に しています。
設定しているサンプリング条件の中で、重要なパラメータの一つが、サンプリングのデータ件数 を指定するulSmplNum変数とコールバック関数を呼び出すデータ件数を指定するulSmplEventNum 変数, トリガ条件を指定するulTrigMode変数です。
ulSmplNum変数は、 ulTrigMode変数のパラメータ値がAD_ETERNITYかそうでないかによって、若
干意味合いが変化します。
下表に比較を示します。
ulTrigMode変数 AD_ETERNITY AD_ETERNITY以外 ulSmplNum変数
内部サンプリングバッファの大きさを意味します。
上位が指示しない限りサンプリング が続けられるので、データ件数は無 制限であると言えます。
(ただし、何もしないと古いデータが
上書きされるので、何らかの処理を 必要とします)サンプリングするデータ件数を意味 します。
(実際には、ここで設定した件数を保
持できるだけのバッファが内部に確 保され、サンプリングが行われます)サンプルでは、サンプリング中にデータを抜き出す処理を行っています。
これを行うため、指定件数のサンプリングデータが溜まるとコールバックが行われ、コールバッ ク関数内でデータを抜き取る処理を行っています。
ulSmplEventNum変数は、このコールバックを呼びだすための指定件数の値を設定しています。
(218行目:AdSetSamplingBuffer関数)
AdSetSamplingBuffer関数では、お客様が用意したADドライバモジュール用の固定長のサンプリン
グバッファを渡しています。ADドライバモジュールが使用するサンプリングバッファは、お客様がRTLinuxモジュール組み込
み時に確保し、ADドライバモジュールに指示してやる必要があります。確保するバッファの大きさは、以下の式で求めることができます。
バッファのサイズ = 1データのサイズ × チャンネル数 × データ件数 1データのサイズ:
AD製品の分解能により決定します。
分解能 データサイズ
8ビット 1バイト
12ビット 2バイト
16ビット 2バイト
24ビット 4バイト
チャンネル数:
AdSetSamplingConfig関数の第2引数ADSMPLREQ構造体のulChCount変数と同値。
データ件数:
AdSetSamplingConfig関数の第2引数ADSMPLREQ構造体のulSmplNum変数と同値。
サンプルでは、グローバル変数としてバッファ領域を確保し、そのポインタをAdSetSamplingBuffer 関数にて渡しています。
★何故、バッファの指定にグローバル変数を使用しているか?
大きめのバッファを取っているので、普通ならkmallocもしくはvmalloc関数を使う所でしょう。
何故動的なメモリ確保関数を使わないのでしょうか?これは、メモリの確保時に、同期性の問題が生じる からです。
例えば、AとBの2つのRTLinuxスレッドを想定します。ここでAのスレッドがメモリ確保の関数を呼び出し ている間、Bのスレッドに処理がプリエンプトされ、同じくメモリ確保の関数を呼び出そうとした時、どう なるでしょうか?
Aのスレッドでは、メモリ確保が完了してないので、Bのスレッドで、メモリ確保を行うことはできません。
何故なら、確保されるメモリ領域が重なることは許されないからです。
もし、メモリ確保を動的に行いたいのであれば、シビアな処理を要求しない個所、例えば、RTLinuxモジュー ルを組み込むinit_module関数内で呼び出すと良いでしょう。
(225行目:pthread_create関数)
pthread_create関数では、周期サンプリングを実行するRTLinuxスレッド(my_task)を生成しています。
(133〜141行目:ハンドラの処理)
ハンドラ(my_handler)の役目は、LinuxプロセスからRT-FIFOを経由して送られる指示を、RTLinux スレッド(my_task)に渡すことにあります。
この処理は、ほとんど定型的なものです。
RT-FIFO RT-FIFO
my_handler ハンドラ
rtf_get rtf_put
Linuxプロセス側 RTLinuxスレッド側
LinuxプロセスからRTLinuxスレッドへのデータの流れ
(74〜121行目:RTLinuxスレッドの処理)
RTLinuxスレッド(my_task)の処理の中心は、この74〜121行のwhileループです。
ここで、
Linuxプロセスから与えられた指示によって、サンプリングの開始と停止を行い(83〜109
行の処理)、周期実行ごとにサンプリング状態の取得を行い(112行目のAdGetStatus関数)、結果を
Linuxプロセスに渡しています(119行目のrtf_put関数)。
周期実行の開始と停止は、先に述べたCMD_IDS列挙体の定数値により決定されます。
列挙定数値 内 容
ID_START AdStartSampling関数を呼び出し、ADドライバモジュールに対してサンプリン
グを開始させる。smp_period_msメンバ変数の値をms単位の実行周期として、
pthread_make_periodic_np関数を呼び出し、自身の実行周期の間隔を指定してい
ます。ID_STOP AdStopSampling関数を呼び出し、ADドライバモジュールに対してサンプリン
グを停止させる。pthread_suspend_np関数を呼び出し、自身のスレッドを スリープ状態にしています。
周期実行の間隔は、smp_period_msの値により決定されます。
例えばここで5の値が指定されると、5msごとにサンプリング状態の取得が行われます。
★AdStartSampling関数の同期/非同期指定について
90行目のAdStartSampling関数では、第2引数にFLAG_ASYNCを指定しています。
これは、サンプリングを非同期に行うよう指示するものです。この引数を指定して関数を呼び出すと、サンプ リング処理が完了するのを待たず、すぐ関数の呼び出しから戻ります。
FLAG_SYNCを指定すると、サンプリングを同期で行うよう指示することになります。
100件のサンプリングを行うよう指示していた場合、この関数の呼び出しから戻った時、100件のサンプリング
が終了しています。
FLAG_ASYNCを指定した場合、サンプリングが終了している保証はありませんので、AdGetStatus関数やコー ルバック関数を用いて、サンプリングが終了したことを監視する必要があります。
サンプルでは、トリガ条件をAD_ETERNITYとしているので、FLAG_SYNCを指定すると、関数の呼び出しか ら戻らず、永遠にサンプリングを続けてしまいます。
従って、ここではFLAG_ASYNCを指定しています。