SpresenseでLチカから始める (24) Wireライブラリ 温度気圧BMP280

 ボッシュのBMP280は温度と気圧が測れるセンサです。データはSPIもしくはI2Cで転送できます。BME280の温湿度と気圧の取得できるスペックとほぼ同じ性能をもちます。

BMP280のおもなスペック

  • 電源電圧;1.71~3.6V
  • 温度測定範囲;-40~+85℃、分解能0.01℃
  • 気圧測定範囲;300~1100hPa、確度±1hPa、分解能0.12hPa
  • インターフェース;SPI(データ転送速度最大10MHz)、I2C(データ転送速度最大3.4MHz)
  • I2Cアドレス;0x76、0x77どちらかを選択

レジスタ類

設定ctrl_meas(0xf4);温度、気圧のビット数、状態遷移のモードを決めるレジスタです。

bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
osrs_t osrs_p mode

 modeはスリープの(0,0)から連続測定の(1,1)、ワンショットの(0,1)があり、パワーオン・リセット時はスリープ状態です。ここでは連続測定モードを使いますが、消費電力を下げたいときは、ワンショットが望ましいです。
 osrs_tとosrs_pのどちらも、0では測定せずにデフォルト値0x8000000の読み出し、001で16ビット、111で20ビットです。
 温度と気圧を16ビットで、連続読み出しの設定では、0b001 001 11になります。このレジスタを設定しないと、温度と気圧の変換が始まりません。

気圧読み出し
 上位バイトが0xf7、下位バイトが0xf8、分解能16ビット以上のデータが0xf9(下の4ビットは0)です。

温度読み出し
 上位バイトが0xfa、下位バイトが0xfb、分解能16ビット以上のデータが0xfc(下の4ビットは0)です。
 3バイト読み出して、利用するのは2420ビットなので、読み出した3224ビット・データを4ビット右にシフトしてから利用します。

キャリブレーション

 読み出したデータに対して、デバイス固有の補正データと演算して温度と気圧を求めます。

アドレス レジスタ名 データ・タイプ
0x88 / 0x89 dig_T1 unsigned short
0x8a / 0x8b dig_T2  signed short
0x8c / 0x8d dig_T3  signed short
0x8e / 0x8f dig_P1 unsigned short
0x90 / 0x91 dig_P2  signed short
0x92 / 0x93 dig_P3  signed short
0x94 / 0x95 dig_P4  signed short
0x96 / 0x97 dig_P5  signed short
0x98 / 0x99 dig_P6  signed short
0x9a / 0x9b dig_P7  signed short
0x9c / 0x9d dig_P8  signed short
0x9e / 0x9f dig_P9  signed short

 補正の方法は、手計算で行う手順と浮動小数点による計算プログラム(C言語)がデータシートに掲載されています。型に注意すれば、Arduinoにはそのまま計算プログラムがコピペして動きます。

接続

 マイコン・ボードは3.3Vで動作させます。
 BMP280はI2Cインターフェースでつなぎました。SDO端子をGNDへつないだので、スレーブ・アドレスは、I2CScannerで0x76に見つけました。

テスト・スケッチ

 デバイス固有のIDを読み出します。データシートによれば、58です。読み出せました。

#include <Wire.h>
#define BMP280_address 0x76
#define ID_register 0xd0

void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("\nstart ");
Wire.beginTransmission(BMP280_address);
Wire.write(ID_register);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 1);
int ID = Wire.read();
Serial.println("\nID = " + String(ID,HEX));
}

void loop() {
// put your main code here, to run repeatedly:
}

気圧と温度を読み出す

 温度と気圧のデータは16ビットと初期設定を行ったのですが、データ自体は3バイトあるので、全部読み出しました。

#include <Wire.h>
#define BMP280_address 0x76
#define Temp_register 0xfa
#define Press_register 0xf7
#define Ctrl_register 0xf4
#define osrs_t 0b00100000 // Temp 16bit
#define osrs_p 0b00000100 // Press 16bit
#define mode_normal 0b00000011

void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("\nstart ");
Wire.beginTransmission(BMP280_address);
Wire.write(Ctrl_register);
Wire.write(osrs_t | osrs_p | mode_normal);
Wire.endTransmission();
Serial.println(String((osrs_t | osrs_p | mode_normal),BIN));
delay(100);
}

void loop() {
Wire.beginTransmission(BMP280_address);
Wire.write(Temp_register);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 3);
int temp = Wire.read();
temp = temp << 8 | Wire.read();
// temp = temp << 8 | Wire.read();
Serial.println("\nTemp = " + String(temp,HEX));

Wire.beginTransmission(BMP280_address);
Wire.write(Press_register);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 3);
int press = Wire.read();
press = press << 8 | Wire.read();
// press = press << 8 | Wire.read();
Serial.println("press = " + String(press,HEX));
delay(2000);
}

 実行中の様子です。

補正値を読み出す

 デバイス固有の補正データを読み出します。読み出した温度と気圧の値は、これら補正値を用いて演算し、最終的な温度と気圧を得ます。

#include <Wire.h>
#define BMP280_address 0x76
#define Temp_register 0xfa
#define Press_register 0xf7
unsigned int dig_T1;
int dig_T2 , dig_T3 , dig_T4 ;
unsigned int dig_P1;
int dig_P2 , dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
byte readbuffer[6];

void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("\nstart ");

readCoefficients();
}

unsigned int read16(byte dig_registor){
Wire.beginTransmission(BMP280_address);
Wire.write(dig_registor);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 2);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
return ( readbuffer[1] << 8 | readbuffer[0] );
}

void readCoefficients(){
dig_T1 = read16(0x88);
dig_T2 = (int16_t)read16(0x8A);
dig_T3 = (int16_t)read16(0x8C);
dig_P1 = read16(0x8E);
dig_P2 = (int16_t)read16(0x90);
dig_P3 = (int16_t)read16(0x92);
dig_P4 = (int16_t)read16(0x94);
dig_P5 = (int16_t)read16(0x96);
dig_P6 = (int16_t)read16(0x98);
dig_P7 = (int16_t)read16(0x9A);
dig_P8 = (int16_t)read16(0x9C);
dig_P9 = (int16_t)read16(0x9E);
Serial.print("dig_T1="); Serial.println(dig_T1,2);
Serial.print("dig_T2="); Serial.println(dig_T2,2);
Serial.print("dig_T3="); Serial.println(dig_T3,2);
Serial.print("dig_P1="); Serial.println(dig_P1,2);
Serial.print("dig_P2="); Serial.println(dig_P2,2);
Serial.print("dig_P3="); Serial.println(dig_P3,2);
Serial.print("dig_P4="); Serial.println(dig_P4,2);
Serial.print("dig_P5="); Serial.println(dig_P5,2);
Serial.print("dig_P6="); Serial.println(dig_P6,2);
Serial.print("dig_P7="); Serial.println(dig_P7,2);
Serial.print("dig_P8="); Serial.println(dig_P8,2);
Serial.print("dig_P9="); Serial.println(dig_P9,2);
}

void loop() {
}

 読み出した補正値を(int)でキャストしたときの実行結果です。

 一部のデータが正常でなかったので、(int16_t)でキャストしました。

 出来上がったスケッチです。温度の補正は、データシートのプログラム例を、気圧の補正は、3.12 Calculating pressure and temperatureの計算方法です。

#include <Wire.h>
#define BMP280_address 0x76
#define Ctrl_register 0xf4
#define osrs_t 0b00100000 // Temp 16bit
#define osrs_p 0b00000100 // Press 16bit
#define mode_normal 0b00000011
#define Config_register 0xf5
#define t_sb 0b00100000 // 62.5
#define filter 0b00000000
#define spi3w_en 0b00000000
#define ID_register 0xd0
#define Temp_register 0xfa
#define Press_register 0xf7
unsigned int dig_T1;
int dig_T2 , dig_T3 , dig_T4 ;
unsigned int dig_P1;
int dig_P2 , dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
#define BMP280_S32_t int32_t
#define BMP280_U32_t uint32_t
#define BMP280_S64_t int64_t
BMP280_S32_t t_fine;
byte readbuffer[2];

void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("\nstart ");

Wire.beginTransmission(BMP280_address);
Wire.write(Config_register);
Wire.write(t_sb | filter | spi3w_en);
Wire.endTransmission();
delay(100);

Wire.beginTransmission(BMP280_address);
Wire.write(Ctrl_register);
Wire.write(osrs_t | osrs_p | mode_normal);
Wire.endTransmission();
delay(100);

readCoefficients();
}

unsigned int read16(byte dig_registor){
Wire.beginTransmission(BMP280_address);
Wire.write(dig_registor);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 2);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
return ( readbuffer[1] << 8 | readbuffer[0] );
}

void readCoefficients(){
dig_T1 = read16(0x88);
dig_T2 = (int16_t)read16(0x8A);
dig_T3 = (int16_t)read16(0x8C);
dig_P1 = read16(0x8E);
dig_P2 = (int16_t)read16(0x90);
dig_P3 = (int16_t)read16(0x92);
dig_P4 = (int16_t)read16(0x94);
dig_P5 = (int16_t)read16(0x96);
dig_P6 = (int16_t)read16(0x98);
dig_P7 = (int16_t)read16(0x9A);
dig_P8 = (int16_t)read16(0x9C);
dig_P9 = (int16_t)read16(0x9E);
Serial.print("dig_T1="); Serial.println(dig_T1,2);
Serial.print("dig_T2="); Serial.println(dig_T2,2);
Serial.print("dig_T3="); Serial.println(dig_T3,2);
Serial.print("dig_P1="); Serial.println(dig_P1,2);
Serial.print("dig_P2="); Serial.println(dig_P2,2);
Serial.print("dig_P3="); Serial.println(dig_P3,2);
Serial.print("dig_P4="); Serial.println(dig_P4,2);
Serial.print("dig_P5="); Serial.println(dig_P5,2);
Serial.print("dig_P6="); Serial.println(dig_P6,2);
Serial.print("dig_P7="); Serial.println(dig_P7,2);
Serial.print("dig_P8="); Serial.println(dig_P8,2);
Serial.print("dig_P9="); Serial.println(dig_P9,2);
}

void loop() {
Wire.beginTransmission(BMP280_address);
Wire.write((byte)Temp_register);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 3);
uint32_t temp = Wire.read();
temp = temp << 8 | Wire.read();
temp = temp << 8 | Wire.read();
temp = temp >> 4;
Serial.println("\nReadTempData = " + String(temp));
float T = bmp280_compensate_T_double((BMP280_S32_t)temp);
Serial.println(T/100);

Wire.beginTransmission(BMP280_address);
Wire.write(Press_register);
Wire.endTransmission();
Wire.requestFrom(BMP280_address, 3);
uint32_t press = Wire.read();
press = press << 8 | Wire.read();
press = press << 8 | Wire.read();
press = press >> 4;
Serial.println("ReadPressData = " + String(press));
float p = bmp280_compensate_P_double((BMP280_S32_t)press);
Serial.println(p/100.0);
delay(2000);
}

BMP280_S32_t bmp280_compensate_T_double(BMP280_S32_t adc_T){
BMP280_S32_t var1, var2, T;
var1 = ((((adc_T >> 3) - ((BMP280_S32_t)dig_T1 << 1))) * ((BMP280_S32_t)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((BMP280_S32_t)dig_T1)) * ((adc_T >> 4) - ((BMP280_S32_t)dig_T1))) >> 12) * ((BMP280_S32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}

BMP280_U32_t bmp280_compensate_P_double(BMP280_S32_t adc_P){
BMP280_S64_t var1 , var2 , P;
var1 = (t_fine / 2.0) - 64000.0;
var2 = var1 * (var1 * dig_P6 / 32768.0);
var2 = var2 + (var1 * dig_P5 * 2.0);
var2 = (var2 / 4.0) + ((dig_P4) * 65536.0);
var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0;
var1 = (1.0 + var1 / 32768.0) * dig_P1;
P = 1048576.0 - adc_P;
P = (P - (var2 / 4096.0)) * 6250.0 / var1 ;
var1 = dig_P9 * P * P / 2147483648.0;
var2 = P*dig_P8 / 32768.0;
P = P + (var1 + var2 + dig_P7) / 16.0;
return (BMP280_U32_t)P;
}

前へ

SpresenseでLチカから始める (23) Wireライブラリ 温湿度HDC2080

次へ

~閑話休題 割り算は遅いか