ゆるく楽しむプログラミング

気圧センサー LPS25HB

I2CとSPIの両用で使用できる気圧センサー
3.3V駆動ですが、5Vでも駆動できるブレークアウトボードが存在します。

LPS25HB(3.3V DIP化ブレークアウトボード)
LPS25HBセンサーモジュール(2.5~5.5Vレギュレータ付き)
LPS25HB搭載 電圧レギュレータ付き気圧センサモジュール

I2C接続

I2Cで使用する場合には、CSをプルアップ接続してHIGHにします。
I2CアドレスはSDOをLOW(プルダウン)すると0x5C、HIGH(プルアップ)すると0x5Dになります。
レギュレータ無しのブレークアウトボードでも(チップ自体は同じなので動作は)同じです。




SPI接続

SPIのピン接続は SDA-MOSI 、SCL-SCLK、SDO-MISO、CS-CSとなります。




気圧センサーの使い方

LPS25HB データシート より抜粋 LPS25HBの主要なレジスタは次の通り
名称 形式 アドレス コメント
デバイスID R 0F BD
圧力分解能 R/W 10 温度・気圧の内部平均選択
制御レジスタ1 R/W 20 出力データ速度設定
制御レジスタ2 R/W 21 リセット/FIFO有効・無効設定
制御レジスタ3 R/W 22 割り込み制御
制御レジスタ4 R/W 23 割り込み制御
ステータス R 27 温度・気圧データの取得状態
気圧データ XL R 28 気圧データ
24Bit
気圧データ L R 29
気圧データ H R 2A
気温データ L R 2B 気温データ
16Bit
気温データ H R 2C
FIFO制御 R/W 2E
FIFOステータス R 2F

レジスタは基本的に1バイトづつしか読み出せない
参照アドレスの最上位ビットを1にする(アドレスと0x80の ORを取る)と、読みだし毎に読み出しアドレスがインクリメントされる。
※実質的に連続読み出しモードになる。

ただし、FIFOが有効な場合、0x28,0x29,0x2Aの読み出しアドレスはループループする
※0x2A読み取り後、インクリメントされたアドレスは0x2Bではなく0x28になる

このため、FIFOを有効にした場合は気圧と気温は別々に読み取るか、もしくは1バイトづつ読み取る必要がある。

気温・気圧データの読み取り

気圧データは取得した24Bit整数値÷4096でhPa単位の気圧に変換出来る。
気温データは取得した16Bit整数値÷480 + 42.5 で℃単位の気温に変換できる。
この変換式はLPS25HBテクニカルノートに記載されています。

-- I2C接続 --
LPS25HB-TEST-I2C.zip
※ <>を全角<>に置き換えています。コピペの際は注意してください。
#include <Wire.h>
#define LPSADDR 0x5C

uint32_t pressure = 0 ;
float temperature = 0 ;

uint8_t i2cBuff[6] ;

// Register読み取り
uint8_t readLPS(uint8_t addr) {
  uint8_t readData ;
  // Read Address Set
  Wire.beginTransmission(LPSADDR);
  Wire.write(addr);
  Wire.endTransmission();
  
  // 1Byte Read
  Wire.requestFrom(LPSADDR, 1);
  while(Wire.available()<1);
  readData = Wire.read();

  return readData ;
}

// Register読み取り
uint8_t readLPSBuff(uint8_t addr,int readLen) {
  uint8_t readData ;
  // Read Address Set
  Wire.beginTransmission(LPSADDR);
  Wire.write(addr|0x80);
  Wire.endTransmission();
  
  // 1Byte Read
  Wire.requestFrom(LPSADDR , readLen);
  while(Wire.available() < readLen);
  for (int i=0;i<readLen;i++) {
    i2cBuff[i] = Wire.read();
  }

  Serial.print("i2cBuff=") ;
  for (int i=0;i<readLen;i++) {
    Serial.print(i2cBuff[i],HEX) ;
    Serial.print(" ") ;
  }
 Serial.println() ;
 
  return readData ;
}

// 気圧センサー読み取り
bool readLPS_Sencer( ) {
  uint8_t stat = readLPS(0x27);
  uint8_t fifoStat = readLPS(0x2F);
  readLPSBuff(0x28,3) ;
  uint8_t pxl = i2cBuff[0] ;
  uint8_t pl = i2cBuff[1] ;
  uint8_t ph = i2cBuff[2] ;
  readLPSBuff(0x2B,2) ;
  uint8_t tl = i2cBuff[0] ;
  uint8_t th = i2cBuff[1] ;

//  uint8_t pxl = readLPS(0x28) ;
//  uint8_t pl = readLPS(0x29) ;
//  uint8_t ph = readLPS(0x2A) ;
//  uint8_t tl = readLPS(0x2B) ;
//  uint8_t th = readLPS(0x2C) ;
//  uint8_t fifoStat = readLPS(0x2F);

  // combine bytes
  pressure = (int32_t)(int8_t)ph << 16 | (uint16_t)pl << 8 | pxl;
  pressure /= 4096 ;
  int16_t temp = (int16_t)th << 8 | tl ;
  temperature = 42.5 + (float)temp / 480;

  Serial.print("stat = ") ;
  Serial.print(stat,HEX) ;
  Serial.print("  fifoStat = ") ;
  Serial.print(fifoStat,HEX) ;
  Serial.print("  ") ;
  
  return ((stat & 0x03) == 0x03) && ((fifoStat & 0xC0) == 0xC0)  ;
}

// 1バイトコマンドを送信
void sendLPS(uint8_t addr , uint8_t command) {
  Wire.beginTransmission(LPSADDR);
  Wire.write(addr);
  Wire.write(command);
  Wire.endTransmission();
}

void setup() {
  Wire.begin();

  Serial.begin(9600);
  while (!Serial); // Leonardo: wait for serial monitor
  Serial.println("\nLPS25HB 気圧センサーテスト - FIFO有効/分割読み出し");

  // -- Read WHO AM I
  uint8_t deviceID = readLPS(0x0F) ;
  Serial.print("Device ID = ") ;
  Serial.println(deviceID,HEX) ;

  // --- Reset LPS25HB
  sendLPS(0x21,0x04);   // ソフトウェアリセット
  while(readLPS(0x21)); // リセット確認
  delay(100);
  sendLPS(0x21,0x80);   // リブートメモリ
  while(readLPS(0x21)); // リブート確認
  delay(100);
  sendLPS(0x20,0xc0);   // アクティブモード、アウトプット25Hz
                        // ※VCCに5V入れた場合、1Hzだと何故か誤差が大きい
                        // ※この設定だと+2hPa +1℃位のズレで収まる
  delay(100);
  sendLPS(0x21,0x40);   // FiFO有効
  sendLPS(0x2E,0xC7);   // FIFO平均モード 8サンプル
                        // ※サンプル数が大きすぎるとFIFOがFULLになるまでに時間がかかる
  delay(1000);
}

void loop() {
  // ----- Read 気圧センサー
  bool ret = readLPS_Sencer( ) ;
  Serial.print("ret = ") ;
  Serial.print(ret) ;
  Serial.print("  /  pressure = ") ;
  Serial.print(pressure) ;
  Serial.print("  /  Temperature = ") ;
  Serial.println(temperature) ;

  delay(10*1000) ; // 10秒ディレイ
}


-- SPI接続 --
LPS25HB-TEST-SPI.zip
※ <>を全角<>に置き換えています。コピペの際は注意してください。
#include <SPI.h>
#define SS 10 // ArduinoUNO 10 : M5Stamp 19 : RaspberryPi PICO 5

uint32_t pressure = 0 ;
float temperature = 0 ;

// Register読み取り
uint8_t readLPS(uint8_t addr) {
  uint8_t readData ;

  digitalWrite(SS, HIGH);
  digitalWrite(SS, LOW);  //通信開始
  SPI.transfer(addr|0x80);
  readData = SPI.transfer(0x00);
  digitalWrite(SS, HIGH); //通信終了  
  return readData ;
}

// 1バイトコマンドを送信
void sendLPS(uint8_t addr , uint8_t command) {
  digitalWrite(SS, HIGH);
  digitalWrite(SS, LOW);  //通信開始
  SPI.transfer(addr);
  SPI.transfer(command);
  digitalWrite(SS, HIGH); //通信終了  
}

// 気圧センサー読み取り
bool readLPS_Sencer( ) {
  uint8_t stat = readLPS(0x27);
  uint8_t pxl = readLPS(0x28) ;
  uint8_t pl = readLPS(0x29) ;
  uint8_t ph = readLPS(0x2A) ;
  uint8_t tl = readLPS(0x2B) ;
  uint8_t th = readLPS(0x2C) ;
  uint8_t fifoStat = readLPS(0x2F);

  // combine bytes
  pressure = (int32_t)(int8_t)ph << 16 | (uint16_t)pl << 8 | pxl;
  pressure /= 4096 ;
  int16_t temp = (int16_t)th << 8 | tl ;
  temperature = 42.5 + (float)temp / 480;

  Serial.print("stat = ") ;
  Serial.print(stat,HEX) ;
  Serial.print("  fifoStat = ") ;
  Serial.print(fifoStat,HEX) ;
  Serial.print("  ") ;
  
  return ((stat & 0x03) == 0x03) && ((fifoStat & 0xC0) == 0xC0)  ;
}

void setup() {
  Serial.begin(9600);
  while (!Serial); // Leonardo: wait for serial monitor
  Serial.println("\nLPS25HB 気圧センサーテスト SPI/FIFO有効");

  pinMode (SS, OUTPUT);
  digitalWrite(SS, HIGH);
  SPI.begin ();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE3);
  SPI.setClockDivider(SPI_CLOCK_DIV16);

  // -- Read WHO AM I
  uint8_t deviceID = readLPS(0x0F) ;
  Serial.print("Device ID = ") ;
  Serial.println(deviceID,HEX) ;

  // --- Reset LPS25HB
  sendLPS(0x21,0x04);   // ソフトウェアリセット
  while(readLPS(0x21)); // リセット確認
  delay(100);
  sendLPS(0x21,0x80);   // リブートメモリ
  while(readLPS(0x21)); // リブート確認
  delay(100);
  sendLPS(0x20,0x90);   // アクティブモード、アウトプット25Hz
                        // ※VCCに5V入れた場合、1Hzだと何故か誤差が大きい
                        // ※この設定だと+2hPa +1℃位のズレで収まる
  delay(100);
  sendLPS(0x21,0x40);   // FiFO有効
  sendLPS(0x2E,0xC7);   // FIFO平均モード 8サンプル
                        // ※サンプル数が大きすぎるとFIFOがFULLになるまでに時間がかかる
  delay(1300);
}

void loop() {
  // ----- Read 気圧センサー
  bool ret = readLPS_Sencer( ) ;
  Serial.print("ret = ") ;
  Serial.print(ret) ;
  Serial.print("  /  pressure = ") ;
  Serial.print(pressure) ;
  Serial.print("  /  Temperature = ") ;
  Serial.println(temperature) ;

  delay(10*1000) ; // 10秒ディレイ
}


解説動画