気圧センサー(LPS331AP/LPS25H)で大気圧を測定して見ます

〔MPL115A1〕 〔MPL115A2〕   〔Arduinoの動かせ方入門に戻る〕


前回の記事でMPL115A2を搭載した大気圧センサーモジュールで気圧と高度を測定して見ました。
がぁ、精度や分解能等の性能が今一つでした。今回は、LPS331AP/LPS25Hで測定を行って見ます、
これは精度±0.2hpa(25℃)と良さげな感じです、それに、温度(精度±2℃)も読み出せます。
LPS25HはLPS331APの後継機の様です、FIFO機能が付いているので移動平均が行えそうですね。
また、接続はI2CとSPIの好きな方が選べます。

LPS25Hは秋月電子のこちらから買っています、ピンの半田付けはしないとダメです。
アプリケーション・ノートはこちら、データシートはこちらからダウンロードしましょ。

LPS331APは秋月電子ではLPS25Hに切り替わっているので、 スイッチサイエンスのこちらが電圧の
レベルコンバータが付いているので便利かもね、または、ストロベリー・リナックスのこちら辺りをどうぞ。
下の実験では以前に秋月電子から購入したモジュールで行っています。
データシートは、こちらからダウンロードしましょ。
或いは、O-Familさんのこちらで個人的に翻訳された物が置いて有ります。
このサイトでは、LPS331APは敏感だからモジュールのLEDは外した方が良い等の注意事項等が
書いて有るので一読した方が良いかもね。(私はそこまでの実験は行っていません)

《I2C》

配線図(I2C)
 左は秋月のLPS331APモジュールでの配線図
 他のモジュールも端子名称を合わせればOKです
 LPS25Hの秋月モジュールも同じ配線(そのまま
 差替えOK)です。
 但し、LPS25HはINT2ピンが有りません。

 モジュールの電源は3.3Vで、ArduinoのI/Oは
 5Vなので電圧レベル変換が必要です。
 左図では左側モジュールが、
 "I2C用電圧レベル変換モジュール"です。
 左図の変換モジュール上がVREF1側となります。

 I2Cレベル変換モジュール

 3.3Vマイコンを使う場合、変換は必要なく直接
 接続出来ますが、I2C用プルアップ抵抗は必要に
 なります。
 この変換モジュールは1KΩのプルアップ抵抗を
 内蔵しているのでそれを利用しています。
 外付けプルアップ抵抗を使う場合はモジュール裏
 のジャンパーパターンをカットすれば良いです。


 尚、VPU端子は外部から通信可能不可能切り替えをする為の物ですが、今回利用していません。
 また、VREF2側に電圧の高い方(5V側)を接続する様にしましょう。

 あ、それとぉ、Arduino UNOの場合はデジタル端子側の方にSCL/SDA専用端子が有ります、
 そちらに接続しても良いでしょう。

LPS331AP/LPS25Hモジュールピンについて

SCL(2)/SDA(3)がI2C用接続ピンです、電圧レベル変換モジュールを間に入れてArduinoと接続します。

SAO(4)ピン
 I2Cデバイス・アドレスの下位ビットを決めるピンです。
 VDDに配線すると7ビットアドレスで"1011101:0x5D"となります、
 GNDに配線すると7ビットアドレスで"1011100:0x5C"となります、
 なので同じI2Cバス上には2個まで接続可能です。

アドレス設定  アドレスは7ビットで表します、左図の1〜7ビットです。
 0ビット目はR/Wでこれはデバイスに対する読書き指示ビットです。
 R/W=0 : 書き込み要求です(デバイスは受信モード)
 R/W=1 : 読み込み要求です(デバイスは送信モード)

 今回はVDDに接続していますのでアドレスは、"1011101"(0x5D)です。("1011101"+R/W)
 R/W=0書き込みなら"1011101"+"0"で0xBA、R/W=1読み込みなら"1011101"+"1"で0xBBとなります。
 よってここでは
 #define SENSOR_ADRS  0x5D  // デバイスのI2Cアドレス(SDO/SAO is HIGH)
 となっています。

CS(5)ピン
 このピンをVDDに接続するとI2Cモードでの通信となります。

INT1(7)/INT2(6)ピン
 デバイスから割り込みを出力するピンです、今回は使っていません。
 この割り込みは、設定閾値の圧力より高い/低い場合に出力する時と
 デバイスが新しい圧力データを測定しレジスタに書き込んだ事を知らせる時の場合が設定可能です。
 尚、LPS25HはINT1のみとなっています。

ダウンロードスケッチについて

↓ここからArduino用サンプルスケッチファイルをダウンロードして下さい。
skLPSxxI2C.lzh(2015/01/05)

解凍すると下の様に展開されます。
[skLPSxxI2C]─┬─[examples]───[Pressure]--- Pressure.ino
        ├ skLPSxxI2C.cpp
        ├ skLPSxxI2C.h
        └ keywords.txt
ArduinoIDE 1.0.5-r2 がインストールされているフォルダー、インストール先を変更していないなら
[C:\Program Files\arduino\libraries]です。
この場所に上記解凍ファイルを[skLPSxxI2C]ディレクトリ丸ごと移動します。
また、ライブラリの登録や利用方法などの話はこちらを参考にして下さい。

Pressure.ino  ・・・・・・本体のサンプルスケッチ(LPS331APでのI2Cバージョンです)
skLPSxxI2C.cpp ・・・・LPS331AP/LPS25H(I2C)関数ライブラリソース
skLPSxxI2C.h ・・・・・・ヘッダファイル
keywords.txt ・・・・・・・キーワードファイル

Pressure.ino(I2C)

Pressure.inoを開くには、
IDEを起動して、メニューバーの「ファイル」→「スケッチの例」→「skLPSxxI2C」→[Pressure]
をクリック操作すればファイルが開かれます。
次に、IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。

実行結果1  IDEの[シリアルモニタ]画面を表示させましょう。
 こんな感じ(左図)に1秒毎に表示されると成功です。
 "Initialization abnormal ans=?"と表示されたら失敗です。
 配線などをよ〜くぅ確かめてみましょう。

 "℃"が表示出来ないので"'C"としています。
 温度は約17℃程の室内、±0.2hpa位には収まっている様ですね。
 すばらしぃ!


skLPSxxI2C.h

LPS331AP/LPS25H関数のインクルードファイルです。
関数ライブラリを利用する場合は、メニューバーの「スケッチ」→「ライブラリを使用」→「skLPSxxI2C」を
クリック操作すれば、"#include <skLPSxxI2C.h>"がスケッチに追加されます。
まぁ、手動でキー入力しても良いんですけどね。

skLPSxxI2C.cpp

まず利用する場合は下記2行をスケッチの最初に記述します。
#include <Wire.h>
#include <skLPSxxI2C.h>

setup( )関数内で下の様にI2Cを使用する為の初期化処理を記述します。
Wire.begin() ;   // マスターとする

※ デバイスのI2C通信速度は100/400KHzまでの様ですがArduinoは100KHzで初期化されます。

LPS331AP/LPS25H用関数の使い方を説明します。

skLPSxxx
 LPS331AP/LPS25H用関数ライブラリを使用する為に必要な宣言(初期化)を行います。
 skLPSxxx LPS(id,address) ;
  id         :デバイスの識別IDを指定します(LPS331AP=0xBB)(LPS25H=0xBD)
  address :デバイス(スレーブ)のI2Cアドレスを指定します(0x5D or 0x5C)
 "LPS"の名前は任意に変更可能です。
 例)
 #define LPS25H_ADRS 0x5C // LPS25HのI2Cアドレス(SDO/SAO is LOW)  skLPSxxx LPS2(LPS25H,LPS25H_ADRS) ; // デバイスはLPS25Hを指定

 #define SENSOR_ADRS  0x5D        // デバイスのI2Cアドレス(SDO/SAO is HIGH)
 skLPSxxx LPS(LPS331AP,SENSOR_ADRS) ; 以下はこの宣言例で記述します。

ans = LPS.PressureInit( )
 デバイスの初期値設定を行う処理です。
 デバイスの動作を確かめる為にデバイスIDのチェックを行い、エラーならans=6を返します。
 分解能のモード(内部平均の数)設定は、LPS331APとLPS25Hで少々異なるのでデフォルト動作です。
 制御レジスタ1は"動作中モード","1Hzの更新速度",Bit3-0=0 で初期化しています。
 初期化データを変更する場合は、skLPSxxI2C.hのRES_CONF_DATA/CTRL_REG1_DATAを書き
 換えて下さい。(詳しい設定はデータシートを見ましょ)
 尚、エラー時は制御レジスタの設定は行っていません。

  ans   :戻り値   0=正常終了、それ以外はI2C通信エラーです
             1=送ろうとしたデータが送信バッファのサイズを超えた(32バイトMAX)
             2=スレーブ・アドレスを送信し、NACKを受信した
             3=データ・バイトを送信し、NACKを受信した
             4=その他のエラー
             5=データ受信エラー
             6=デバイスのIDチェックエラー

 ※ "1Hzの更新速度"に設定されているので圧力・温度値データは1秒毎に更新されます。

 ※ LPS331APとLPS25Hで少々レジスターの設定が異なっていますので、
   もう少し細かく設定を行いたい人はデータシートを見て下さい。

ans = LPS.PressureReceive(reg_adrs,*data,kosu)
 デバイスから指定個数のデータを受信する処理です。
  reg_adrs:読出すデータのレジスターアドレスを指定する
           連続的に読出す場合は、読出すレジスターの先頭アドレスを指定
  *data    :読出したデータの格納先を指定する(格納先配列の個数はkosu分確保する事)
  kosu     :読出すデータのバイト数を指定する
  ans       :戻り値、0=正常終了 それ以外PressureInit( )のans値を参照

ans = LPS.PressureSend(reg_adrs,*data,kosu)
 デバイスに指定個数のデータを送信する処理です。
  reg_adrs:書出すデータのレジスターアドレスを指定する
           連続的に書出す場合は、書出すレジスターの先頭アドレスを指定
  *data    :書出すデータの格納先を指定する
  kosu     :書出すデータのバイト数を指定する
  ans       :戻り値、0=正常終了 それ以外PressureInit( )のans値を参照

ans = LPS.PressurePD(mode)
 デバイスの"パワーダウン"と"動作中"を切り替える処理です。
 パワーダウン状態から動作中モードへの待機時間は、1msec必要です。
 電源ON時はパワーダウン状態です、
 尚、パワーダウン状態時に圧力・温度値を読み出しても内容は不定です。
  mode     :0=パワーダウンモード , 1=動作中モード の何れかを指定する
  ans       :戻り値、0=正常終了 それ以外PressureInit( )のans値を参照

ans = LPS.PressureRead( )
 圧力・温度をデバイスから読込み大気圧値と温度値を計算する処理です。
 計算された大気圧値(hPa)はPress変数に、温度値(℃)はTemp変数に其々格納されます。
 尚、Press/Temp変数はfloat変数で宣言されています。
  ans       :戻り値、0=正常終了 それ以外PressureInit( )のans値を参照

ans = LPS.AltitudeCalc(float pressure,int Difference)
 気圧値(hPa)から高度を計算する処理。
 この関数についての説明は前回の記事を参照下さい。

《SPI》

配線図(SPI)  左は秋月のLPS331APモジュールですが、
 LPS25Hの秋月モジュールも同じ配線(そのまま
 差替えOK)です。

 回路はSPIの4線式で配線されています、
 SPIの3線式も出来る様ですが、
 ArduinoのSPIライブラリが対応していません。

 この回路も電圧レベル変換が必要でここでは、
 "8ビット双方向ロジックレベル変換モジュール"を
 使いました。
 変換モジュールの使い方等はこちらです。

 LPS331APモジュールの裏にジャンパーパターンが
 有りますが、これはI2Cのプルアップ抵抗用です、
 SPIの場合はジャンパーしません。

 LPS331APでSPIの実験を行ったのですが、
 レジスタの読出しは出来るのですが、レジスタの
 書込みが上手く行きません、色々行っている時に
 LPS25Hが配送されて来たので入換えて見た所
 即動作しちゃいました。
 MPL115A1(SPI)もそうなのですがぁ、
 どうも微妙にタイミングがズレたりしている感じで、
 I2Cと異なりSPIは規約が厳密でないのでデバイス
 により、又はマスターにより上手く動作しない場合
 が有りそうです。
 なので、以下の実験はLPS25Hにて行っています。

ダウンロードスケッチについて

LPS331APのデータシートにはSPIの速度が記載されていない様なのでLPS25Hを見たら
"SPI clock frequency 10MHz"となっていますのですが、ここでは4MHzで動作させています。
LPS25HのデータシートはSPI通信時のタイミング波形とかが記載されているが、LPS331APでは
書かれていない、SPI通信お勧めでは無いんでしょうか?。

↓ここからArduino用サンプルスケッチファイルをダウンロードして下さい。
skLPSxxSPI.lzh(2015/01/07)

解凍すると下の様に展開されます。
[skLPSxxSPI]─┬─[examples]───[Pressure]--- Pressure.ino
        ├ skLPSxxSPI.cpp
        ├ skLPSxxSPI.h
        └ keywords.txt
Pressure.ino  ・・・・・・本体のサンプルスケッチ(LPS25HでのSPIバージョンです)
skLPSxxSPI.cpp ・・・・LPS331AP/LPS25H(I2C)関数ライブラリソース
skLPSxxSPI.h ・・・・・・ヘッダファイル
keywords.txt ・・・・・・・キーワードファイル

スケッチの動作はI2Cの場合と同じです。

Pressure.ino(SPI)

Pressure.inoを開くには、
IDEを起動して、メニューバーの「ファイル」→「スケッチの例」→「skLPSxxSPI」→[Pressure]
をクリック操作すればファイルが開かれます。
次に、IDEツールバーの「Upload」ボタンをクリックしてコンパイルとarduinoボードに書込みを行います。

skLPSxxSPI.h

LPS331AP/LPS25H関数のインクルードファイルです。
関数ライブラリを利用する場合は、メニューバーの「スケッチ」→「ライブラリを使用」→「skLPSxxSPI」を
クリック操作すれば、"#include <skLPSxxSPI.h>"がスケッチに追加されます。
まぁ、手動でキー入力しても良いんですけどね。

skLPSxxSPI.cpp

まず利用する場合は下記2行をスケッチの最初に記述します。
#include <Wire.h>
#include <skLPSxxSPI.h>

setup( )関数内で下の様にSPIを使用する為の初期化処理を記述します。
     SPI.begin() ;                           // SPIを行う為の初期化
     SPI.setBitOrder(MSBFIRST) ;             // ビットオーダー
     SPI.setClockDivider(SPI_CLOCK_DIV4) ;   // クロック(CLK)をシステムクロックの1/4で使用(16MHz/4)
     SPI.setDataMode(SPI_MODE3) ;            // クロック極性 1(HIGH) クロック位相 1(HIGH)
LPS331AP/LPS25H用関数の使い方を説明します。

skLPSxxx
 LPS331AP/LPS25H用関数ライブラリを使用する為に必要な宣言(初期化)を行います。
 skLPSxxx LPS(id,cspin) ;
  id         :デバイスの識別IDを指定します(LPS331AP=0xBB)(LPS25H=0xBD)
  cspin    : CSのピン番号を指定します

SS(CS)は"pins_arduino.h(スタンダードArduino時)"の中で"#define SS 10"と定義されています。
skLPSxxx LPS(LPS25H,SS) ;   // デバイスはLPS25Hを指定

※ SSを10番ピン以外で利用した場合でも、ArduinoのSPIライブラリの都合で10番ピンは他に
  使用不可です。

ans = LPS.PressureInit( )
 デバイスの初期化を行う処理です。
 I2Cの場合と同じですがエラー番号1〜5は有りません。
  ans   :戻り値   0=正常終了
             6=デバイスのIDチェックエラー

PressureReceive( ),PressureSend( ),PressurePD( ),PressureRead( )
 使い方はI2Cと同じですがansが有りません。

《LPS25HのFIFO機能》

通常はセンサーの値を読み出したらマイコンで平均値を算出するわけですが、LPS331AP/LPS25Hは
デバイス内部でデータを単純平均(1〜512回)してくれます、
ですのでデバイスから読み出している圧力・温度値はこの平均値を読み出している事になります。
でぇ、LPS25Hには更にFIFO(First-In,First-Out)機能が搭載されているのでデバイス自体が移動平均を
行ってくれます
、これを利用すればマイコンで移動平均処理はしなくても良くマイコンの負荷を減らす
事が出来ます。

FIFO機能はレジスタに設定を行うだけで後は圧力値(PRESS_OUT_XL/PRESS_OUT_L/PRESS_OUT_H)
を読み出すだけです。
FIFOの設定は、
・CTRL_REG2(21H)のFIFO_EN(6bit)をON(1)にしてFIFO機能を有効にします。
・FIFO_CTRL(2EH)にてFIFOの動作モード(F_MODE)と移動平均のサンプル数(WTM_POIN)を指定
 します。
 サンプル数は2,4,8,16,32の内から選び、圧力値は3ByteなのでMAX 32x3のFIFOバッファが有ります。

FIFOの入力圧力測定値の収集は、CTRL_REG1(20H)のODR(bit6-4)にて指定した出力レート毎に
行われ、RES_CONF(10H)のAVGP(bit1-0)で指定した内部平均数で平均した内容がFIFOバッファに
送られます。(今回の設定は、出力レートが"1Hz"で、内部平均数が"32")

FIFOのモード

FIFOには4つのモードが有り、FIFO_CTRL(2EH)のF_MODE(bit7-5)にて指定します。

バイパスモード(F_MODE="000")
  FIFOは動作しない、FIFOのバッファは空のままです。
  測定値はPRESS_OUTレジスタに直接で送られます。

FIFOモード(F_MODE="001")
  センサからの測定値は、FIFOバッファに送信されます。
  CTRL_REG2(21H)のWTM_EN(5bit)を有効にすると、バッファが満杯になればウォーターマーク割込み
  が発生します。 バッファが満杯になると、FIFOは、入力圧力測定値の収集を停止します。
  (圧力値を読み出せば、測定値の収集が再開されるのかは実験していません (|||_|||)ガビーン )

ストリームモード(F_MODE="010")
  FIFOモードと同じだがデータは古い順にすてられますので収集の停止はないです。

  FIFOモード/ストリームモードとも移動平均の算出は行われません、自分でデータを読み出さないと
  ダメです、例えば、バッファサンプルが4個ならPRESS_OUTレジスタから4回(4x3Byte)連続して
  読み出す事になります、って事は28Hの先頭番地から12回(4x3Byte)連続読み出しを行えば後は
  デバイスが自動的にアドレスを操作してくれます
  (読み出した後は自分で平均処理をマイコンで行う事になります)

FIFO移動平均モード(F_MODE="110")
  バッファがサンプルの個数溜まったら、データレート(ODR)毎に移動平均されPRESS_OUTレジスタに
  送り出されます。
  このモードは低消費電力での圧力のノイズを低減する為には有効らしい。
  CTRL_REG2(21H)のFIFO_MEAN_DEC(bit4)をONにした場合、出力データレートは"1Hz"固定となり
  バッファサンプル数や内部平均数の組み合わせは"ODR"ビット指定で決まるらしい。
  (下のサンプルスケッチはこのモードでFIFO_MEAN_DEC=OFFでの内容となります)

後ぉ、他に<Extra FIFO modes>が有ります、これは上の4つのモードの組合せみたいなモードです。
トリガ信号とは、INTERRUPT_CFG(24H)によって構成されたINT_SOURCE(25H)のIAビット[2]です。

BYPASS TO STREAM MODE (F_MODE="100")
FIFOは、トリガーイベントまでBYPASSモードである。その後、ストリームモードが起動します。
Stream to FIFO mode (F_MODE="011")
Bypass to FIFO mode (F_MODE="111")

サンプルスケッチについて

↓ここからArduino用サンプルスケッチファイルをダウンロードして下さい。
Pressure.lzh(2015/01/13)

このスケッチはI2C用で"Pressure.ino"のみです、上の"skLPSxxI2C:skLPSxxI2C.cpp"で動作します。
または、[skLPSxxI2C]->[examples]->[Pressure]->Pressure.inoのファイルと入換えて下さい。
尚、SPI版が欲しい人は自分で作成して下さい。
このスケッチでのLPS25HのI2Cアドレスは0x5C(SDO/SAO is LOW)となっています注意下さい。

実行結果4  この結果は"FIFO_MEAN MODE/
 4 samples"で設定し、
 データレート(ODR)は1Hzで収集です。
 設定後すぐに1秒毎にFIFOステータスを
 表示させています。

 1行目の表示は設定後すぐなのでその前
 までの内容が表示されています。
 1秒後の表示から4秒後までは4Sample/
 1Hzなのでdata levelは(0)、
 その後から圧力値も安定した内容で、
 上記実験の表示と比べれば更に精度が
 増している様な感じではないでしょうか?
 (左の温度値は無視しましょう、嘘値です)


FIFO機能時の温度値読出しについて

FIFO機能が無効時は、レジスタ28Hから連続5Byte読み出せば圧力・温度値が取り出せますが、
例えば)
   LPS.PressureReceive(0x28,&dt[0],5) ;
とすれば[28H]>[29H]>[2AH]>[2BH]>[2CH]とデバイスがアドレスを自動で操作します。

FIFO機能が有効になると
[28H]>[29H]>[2AH]>[28H]>[29H]>[2AH]...と言った感じにアドレスが操作されます。
だから温度値は連続では読み出せないです、別途読み出す必要が有ります。
例えば、FIFO移動平均モードとする)
   LPS.PressureReceive(0x28,&dt[0],3) ;
   LPS.PressureReceive(0x2B,&dt[3],2) ;
とこんな感じです。
尚、FIFOモード/ストリームモードで"4 samples"なら
   LPS.PressureReceive(0x28,&dt[0],12) ;
   LPS.PressureReceive(0x2B,&dt[12],2) ;
とこんな感じでしょうか。

実行結果2  因みに、これがLPS331APとLPS25Hの
 1秒毎の実行結果です。
 LPS25Hは移動平均させています。
 何方の値が正確なのかは解りませんが、
 温度値はさほど変わらない様ですが、
 大気圧が6hPaも異なるとは如何いう事?
 1hPa程なら解らなくもないのですがねぇ
 私の買ったのはハズレぇ?......


《その他》

実験風景
 左写真はI2CでLPS331APとMPL115A2を
 実験時の物です。
 左から電圧変換モジュール、LPS331AP、
 MPL115A2の順です。

 左下がその時の表示結果です、やっぱりぃ
 "MPL115A2"は±1hpa程、
 "LPS331AP"は±0.2hpa程の精度の様ですね。

 LPS331AP/LPS25Hは、圧力の変化時や
 測定終了時に割込みを出力する機能、
 マスターからの指示時のみ圧力を測定する
 ワンショット変換機能などが有りますが
 今回は試していませんので必要な人は自分で
 試して下さい。


 ちょっとぉ、今回の記事は長すぎた様な
 気もしますページを分けた方が良かったかもぉ。

 実行結果3




【きむ茶工房ガレージハウス】
Copyright (C) 2006-2015 Shigehiro Kimura All Rights Reserved.