組み込みシステムの設計を容易なものにするためのハードウェアに依存しない手法の活用:ドライバの実装

組み込みシステムの設計を容易なものにするためのハードウェアに依存しない手法の活用:ドライバの実装

著者の連絡先情報

Giacomo Paterniani

Giacomo Paterniani

要約

本稿では、ハードウェアに依存しないドライバをプロジェクトに実装する方法を説明します。プラグ・アンド・プレイ手法は、組み込みソフトウェアやファームウェアに関するどのような経験レベルの設計者にとっても、役立てることができます。組み込みシステムのソフトウェア構造と共に、ドライバに実装された基本関数を再確認するには、「組み込みシステムの設計を容易なものにするためのハードウェアに依存しない手法の活用:基本的事項」の記事を参照してください。

はじめに

組み込みシステムに取り組む設計者は、多くの場合、選択したセンサーが目的とする基本機能を確実に達成できるよう、ドライバ、ひいてはファームウェアのプログラミングの作業を行います。これは極めて時間のかかる作業です。これに対する1つの解決策は、ハードウェアをソフトウェアおよびファームウェアと組み合わせ、センサー選択とシステム統合に対するプラグ・アンド・プレイ手法を可能にすることです。ハードウェアに依存しないドライバは、センサー集積を容易なものにすることに加え、将来の設計にも適用できる、すぐに使えるソリューションという利点も提供します。本稿では、例として慣性計測ユニット(IMU)センサーを取り上げますが、この手法は他のセンサーや部品にも拡張可能です。ドライバは、Cプログラミング言語で設定され、一般的なマイクロコントローラでテストされます。

ドライバの実装

ここで述べる図は全て付録にあり、この図からコードを参照することができます。

  • adis16500_rd_error_flag

この関数の実装方法は、付録の図10に示されています。これは、ADIS16500_REG_DIAG_STATレジスタに含まれるエラー・フラグを読み出し、エラーが発生していなければ全てのビットは0になります。可能性のあるエラーは10通りなので、関数は10個のブーリアン・フィールドで構成されるADIS16500_ERROR_FLAGSという名の構造を返します。1つのフィールドが1つのエラーに対応します。この関数は単にADIS16500_REG_DIAG_STATレジスタを読み出し、特定のエラー・マスクを使ってそのビットをチェックします。そして、ロジック1が見つかると、必ず構造の特定のフィールドが真に設定されます。

  • adis16500_rd_temp

温度読出し関数は、加速度センサーおよびジャイロ・センサーの場合に用いられたものと同じ手法に従います(このシリーズの最初の記事を参照)。読出し値の単位はºCです。2進数値は16ビット・レジスタであるADIS16500_REG_TEMP_OUTに格納されます。その後、2進数から2の補数への変換が行われます。2の補数値が求まると、ºC/LSBを単位とする温度スケーリング・ファクタが乗じられ、ºC単位の最終値が、入力として渡されたポインタに登録されます。この関数の実装方法は、付録の図9に示されています。

  • adis16500_get_ts_usec

この関数は、IMUのタイムスタンプをμsを単位として取得するために用いられます。手法は、adis16500_rd_temp関数と全く同じです。実装方法は、付録の図9に示されています

  • adis16500_rd_data_cntr

このプロシージャでは、出力されたデータの数を読み出します。これを行うには、単にADIS16500_REG_DATA_CNTR というレジスタを読み出すだけです。レジスタが最大値に達すると、0から再開します。関数の実装方法は付録の図9にあります。

  • adis16500_wr_acc_calibr

これは、設計の際にカスタマイズされたオフセット・キャリブレーションを行う必要がある場合に呼び出される関数です。その方法で、x、y、zの各キャリブレーション値がx、y、zの加速度データに付加されるよう、出力データ・レジスタから読み出された値にオフセットが付加されます。関数の入力は、ADIS16500_XL_OUT 型の構造へのポインタです。この構造には、x、y、zの浮動小数点型フィールドが含まれています。この関数の目的は、浮動小数点から2の補数値に変換し、更に、2の補数から2進数値に変換することです。全てのステップを付録の図11に示します。この時点で、2進数値は、バイアス・レジスタに書き込まれる必要があります。そのため、例えば、x軸には書込み対象として、下位16ビット用にADIS16500_REG_X_ACCEL_BIAS_L と最上位16ビット用にADIS16500_REG_X_ACCEL_BIAS_H の、2つのレジスタがあります。各バイアス・レジスタに応じて、同じことがy軸とz軸にあてはまります。このプロシージャが正しく行われていることを確認するため、z軸が空に向かって垂直になるようIMUセンサーを配置しました。その条件では、x軸とy軸の加速度値はほぼ0で、z軸は約–9.81m/s2(–g)となります。キャリブレーション関数を呼び出して、それをx、y、zの各フィールドの全てが–9.81m/s2に等しいキャリブレーション構造に渡すと、読出し値は、x = –9.81; y = –9.81; z = 0となります。したがって、キャリブレーション・オフセット関数は機能しています。

  • adis16500_wr_gyro_calib

ジャイロ・センサーに関するオフセット・キャリブレーション関数は、加速度センサーのプロシージャと全く同じです。もちろん、データシートのジャイロ・センサー・バイアス・レジスタに対応して行われます。

本稿では、IMUセンサー・ドライバに焦点を合わせていますが、ソフトウェア/ファームウェア構造は、他のタイプのセンサーについても同じです。そのため、実装を全てのセンサーに対して一般化するために言えることは、マイクロコントローラをセンサーにリンクする通信プロトコル(SPI、I2C、UARTなど)が唯一の違いであるということです。センサーを初期化する方法は引き続き有効です。初期化フェーズでは通信プロトコルを通じて送信および受信の関数を記録するためです。

ドライバをプロジェクトに取り込んで使用する方法

センサーとマイクロコントローラ・ユニット(MCU)の間のハードウェア接続に関する簡単な指示とは別に、ソフトウェアおよびファームウェアの観点からドライバを取り込む方法に関するガイドラインもあります。

複数のセンサー・ドライバをまとめ上げるのに万能な手法はありません。提案する手法を図1に示します。全てのセンサー・ドライバを含むuserlib というフォルダがあります。この例では、IMUセンサー・ドライバは1つだけですが、プロジェクトに複数のセンサーが含まれる場合もプロシージャは同様です。userlibの中には、includeとsrcの2つのフォルダがあります。includeフォルダにはドライバのヘッダ・ファイルが含まれ、これは本稿の例ではadis16500.h となります。一方、srcには、ソース・ファイル、つまりadis16500.c が含まれます。userlib 内には、メイクファイルもあります。これは、図2に示すようにincludeのディレクティブを規定します。

図1 プロジェクトのフォルダ構造

図1 プロジェクトのフォルダ構造

図2 userlibのメイクファイル

図2 userlibのメイクファイル

図3に上位メイクファイルを示します。これは、アプリケーション層のmain.c の近くにあります。このメイクファイルには、図3の赤色の下線を施した部分(コードの行115)に示されるように、user.mk が含まれています。

図4 センサー出力の読出し例

図3 上位メイクファイル

メイクファイル(.mk)のおかげで、設計者はドライバのインターフェースをアプリケーション層、例えばmain.c 内に含めることができ、センサーのドライバのパブリック関数を全て呼び出すことができます。その方法では、アプリケーション層とセンサー・ドライバ層との間にリンクがあります。アプリケーション層は、センサーのドライバ・インターフェースの情報がある層です(adis16500.h)。そのため、センサー・ドライバ層とペリフェラル・ドライバ層との間のリンクは、既述の初期化プロシージャを通じてアプリケーション層で発生します。IMUセンサーに特有の場合では、SPIトランスミッタ関数、SPIレシーバ関数、システム遅延関数は、付録の図2に示すように、main.cファイルで定義されます。ここではこれら3つの関数はドライバのヘッダ・ファイルでのプロトタイプ、つまり付録の図3の冒頭に示したプロトタイプに正確に従います。これら3つの関数内には、spiSelect、spiSend、spiReceive、spiUnselect、chThdSleepMicroseconds という関数がペリフェラル・ドライバ層によって提供されます。そのため、SPIレシーバ関数、SPIトランスミッタ関数、システム遅延関数は、ペリフェラル・ドライバ層とセンサー・ドライバ層との間のリンクを表し、これらが付録の図2に示すように、初期化構造に割り当てられる関数となります。これが、ドライバをプロジェクトに含めるために必要な全てです。

設計者がセンサーから出力を取得するには、adis16500_rd_accのセクションとadis16500_rd_gyroのセクションで説明した関数を用いることができます。センサーを読み出すための万能の手法はありませんが、一例を図4に示します。

図4 センサー出力の読出し例

図4 センサー出力の読出し例

この例では、main.c に無限ループがあります。このループでは、_adis16500_data_ready という名のブーリアン型静的変数が常にチェックされます。この変数はコールバック関数に関連しており、DRピンがハイになると、つまり、新しいデータの準備が整うと、TRUEにトグルされます。この状況の下で、メイン関数はadis16500_rd_acc 関数とadis16500_rd_gyro 関数を呼び出します。IMUセンサーをフル・スピードで動作させることで、設計者は2kHzの出力データ・レート(ODR)でデータを取得できます。

まとめ

本稿では、ドライバの諸機能と、センサーの集積化を容易なものにするハードウェアに依存しない手法を説明しました。ハードウェアに依存しないドライバは、将来の設計にも適用できる、すぐに使えるソリューションという利点を提供します。