0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PicoCalc版MachiKaniaでBME280を利用する

Posted at

はじめに

PicoCalc版MachiKaniaでI2C通信を使用するADT7410とSPI通信を使用するADT7310の両方で温度測定できるようになったので、I2C通信とSPI通信の両方を使用できる温度、湿度、気圧センサーのBME-280も使用できるだろうと、以前作成したMMBasicのI2CとADT7310のプログラムを元にいろいろと試し、動作するプログラムを作成できたのでその内容をまとめました。

検証環境

次の環境でプログラムを作成、実行しました。

デバイス間の接続

BME280とPicoCalcとの通信方式により、デバイス間は以下のように接続します。

I2C通信の場合

BME280とPicoCalcとの間をI2Cで通信する場合の接続は次の表の通りです。データシートを参考に、I2Cアドレスをデフォルトの0x76で使用するにしてあります。

BME280 PicoCalc Pico I2C
VDO 3V3 OUT
GND GND
CSB 未接続
SDI GP4 SDA
SDO GND
SCK GP5 SCL

実体配線図は次のようになります。

PicoCalc-BME280-I2C_ブレッドボード.png

SPI通信の場合

BME280とPicoCalcとの間をSPIで通信する場合のデバイス間の接続は次の表の通りです。データシートを参考に、4W接続にしています。チップ選択のCSBピンはSPIと関係ないGP5に接続しています。

BME280 PicoCalc Pico SPI
VDO 3V3 OUT
GND GND
CSB GP5
SDI GP3 TX
SDO GP4 RX
SCK GP2 CLK

実体配線図は次のようになります。

PicoCalc-BME280-SPI_ブレッドボード.png

通信用ピンの設定

MachiKaniaではI2CおよびSPIで使用するピンの指定はSDカードのルートディレクトリに配置した設定ファイルMACHIKAP.INIに記述します。

I2Cピンの設定

ADT7410のときと同様にGP4、GP5を使用するように下記の設定をしています。

MACHIKAP.INI
<略>
143  I2CSDA=4
144  I2CSCL=5
<略>

SPIピンの設定

ADT7310のときと同様にGP2、GP3、GP4を使用するように下記の設定をしています。

MACHIKAP.INI
<略>
119  SPIMISO=4
120  SPIMOSI=3
121  SPICLK=2
<略>

処理の流れ

BME280を使った温度、湿度、圧力測定は以下の流れで実行します。

  1. PicoCalcでそれぞれの通信のマスター機能を有効化する
  2. ソフトリセットコマンドの送信
  3. 測定モードの設定
  4. 補正係数の読み出し
  5. センサーから温度、湿度、気圧の生データの取得
  6. 取得した生データから温度、気圧、湿度の値を算出する
  7. 算出された温度、湿度、気圧の値の表示

通信のマスター機能の有効化

PicoCalcを2つの通信方式のマスターとして有効化する方法を示します。

I2Cの場合

KM-BASICではI2C sコマンドを使い、パラメータsに通信速度をkHz単位で指定します。パラメータ省略時は100kHzになります。

通信速度を200kHzするコードを示します。

I2C 200

SPIの場合

SPIを使用する場合は、SPI s, i, m, p コマンドを使用します。

パラメータ 説明
s 通信速度(kHz単位)
i 1ワードのビット数(8、16、32)省略時は8
m SPIモード(0、1、2、3)省略時は0
p CSピン、省略時は3

通信速度1MHz(1000kHz)、1ワードを8ビット、モード0、CSピンをGP5に設定する場合のコードを示します。

SPI 1000, 8, 0, 5

データ送信

I2CおよびSPIでのデータ送信について説明します。

I2Cの場合

I2Cでスレーブのデバイスにデータを送信する場合、以下のいずれかの命令を使用します。

コマンド 動作
I2CWRITE a, d1, d2, ... 固定バイトのデータ(d1, d2, ...)をアドレスaのデバイスに送信する
I2CWRITEDATA a, b, n, d1, d2, ... アドレスaのデバイスに固定バイトのデータ(d1, d2, ...)を送信後、バッファアドレスbからnバイトを送信する

BME280をソフトリセット(レジスタ0xE0にデータ0xB6をセット)する場合のコードを以下に示します。BME280のI2Cはアドレス0x76です。

固定バイトデータを送信する場合。

I2CWRITE $76, $E0, $B6

バッファBにデータを保存し、それを送信する場合のコードを示します。

DIM B(1)
B(0) = $E0 : B(1) = $B6
I2CWRITEDATA $76, C, 2

SPIの場合

BME280でSPIを使ってレジスタにデータを送受信(読み書き)する場合、8ビットレジスタアドレスはMSB(ビット7)を除いた7ビットが使用されます。MSBは送受信の指示に使用され、0のときが送信(書き込み)、1のときが受信(読み込み)を意味します。そのため、送信時はレジスタアドレスと0x7Fの論理積(AND)の値を求める必要があります。

SPIデバイスにデータを送信する場合、以下のいずれかの命令を使用します。

コマンド 動作
SPIWRITE d1, d2, ... 固定バイトのデータ(d1, d2, ...)をデバイスに送信する
SPIWRITEDATA b, n, d1, d2, ... デバイスに固定バイトのデータ(d1, d2, ...)を送信後、バッファアドレスbからnバイトを送信する

BME280をソフトリセット(レジスタ0xE0にデータ0xB6をセット)する場合のコードを以下に示します。

SPIWRITE $E0 AND $7F, $B6

バッファBにデータを保存し、それを送信する場合のコードを示します。

DIM B(1)
B(0) = $E0 AND $7F : B(1) = $B6
SPIWRITEDATA b, 2

なお、KM-BASICではCSのローとハイの切替え操作は命令実行前後で実行されます。

測定モードの設定

センサ内部のADCの測定サイクル中に温度、湿度、気圧測定を何回測定するか(オーバーサンプリング)、デバイスの動作速度、フィルタ、インターフェイスの設定します。

ここでは温度、湿度、気圧測定は測定サイクル中に1回測定、ノーマルモード動作、フィルタとSPIは使用しない設定します。

レジスタ 名称 設定項目 設定値
0xF2 ctrl_hum 湿度データのオーバーサンプリング 0x01
0xF4 ctrl_meas 温度、気圧のオーバサンプリング、センサーモード 0x27
0xF5 config ノーマルモードスタンバイ時間、フィルタ、SPI使用 0xA0

上記の設定をそれぞれの通信モードに応じた命令を使って、BME280に設定します。

湿度データのオーバサンプリング設定

BME280のデータシートページ26によると湿度のオーバサンプリングは下位3ビットで指定し、表に示す値を設定できます。オーバサンプリングを×1にするので0x01を使います。

osrs_h[2:0] 湿度のオーバサンプリング
000 スキップ (出力は0x8000にセット)
001 オーバサンプリング ×1
010 オーバサンプリング ×2
011 オーバサンプリング ×4
100 オーバサンプリング ×8
101, 他 オーバサンプリング ×16

温度、気圧のオーバサンプリング、センサーモード設定

BME280のデータシートページ27によると温度のオーバサンプリングはビット7〜5の3ビットで、気圧のオーバサンプリングはビット4〜2の3ビットで、センサーモードはビット1〜0の2ビットで指定し、表に示す値を設定できます。
オーバサンプリングを×1、ノーマルモードに設定するので設定値は2進表記で001 001 11、16進表記で0x27になります。

osrs_t osrs_p mode
001 001 11

osrs_p[2:0] 気圧オーバサンプリング
000 スキップ (出力は0x8000にセット)
001 オーバサンプリング ×1
010 オーバサンプリング ×2
011 オーバサンプリング ×4
100 オーバサンプリング ×8
101,他 オーバサンプリング ×16
osrs_t[2:0] 温度オーバサンプリング
000 スキップ (出力は0x8000にセット)
001 オーバサンプリング ×1
010 オーバサンプリング ×2
011 オーバサンプリング ×4
100 オーバサンプリング ×8
101,他 オーバサンプリング ×16

mode[1:0] モード
00 スリープモード
01と10 強制モード
11 ノーマルモード

スタンバイ時間、フィルタ、SPI使用の設定

BME280のデータシートページ28によるとノーマルモードのスタンバイ時間をビット7〜5の3ビット、フィルタの制御をビット4〜2の3ビット、3線SPIインターフェイス使用をビット0の1ビットで指定し、表に示す値を設定できます。
スタンバイ時間を500ms、フィルタ未使用、SPI 3Wを使用しない設定にするので設定値は2進表記で100 000 0 0、16進表記で0xA0になります。

t_sb filter ビット1 spi3w
100 000 0

t_sb[2:0] tstandby [ms]
000 0.5
001 62.5
010 125
011 250
100 500
101 1000
110 10
111 20

filter[2:0] フィルタ係数
000 フィルタオフ
001 2
010 4
011 8
100,他 16

I2Cの設定コード

I2Cを利用する場合の設定コードを示します。

I2CWRITE $76, $F2, $01
I2CWRITE $76, $F4, $27
I2CWRITE $76, $F5, $A0

SPIの設定コード

SPIを利用する場合の設定コードを示します。レジスタへの書き込みなのでレジスタのMSBを0にするために0x7Fとの論理積(AND)が必要です。

SPIWRITE $F2 AND $7F, $01
SPIWRITE $F4 AND $7F, $27
SPIWRITE $F5 AND $7F, $A0

補正係数読み出しと計算

補正係数は温度、湿度、気圧を測定するために必要なデータです。下表に示すBME280のレジスタから読み取り、それぞれのデータ形式(16ビットおよび8ビットで符号なし、および符号付き)に応じた値に変換して変数に保存します。

項目 レジスタ範囲 補正係数
温度 0x88〜0x8D dig_T1〜dig_T3
気圧 0x8E〜0x9F dig_P1〜dig_P9
湿度 0xA1、0xE1〜0xE7 dig_H1〜dig_H6

I2Cでの補正係数の読み出し

補正係数はレジスタデータを送信後、温度、気圧、湿度のデータを読み取ります。KM-BASICではI2CREADDATA a, b, n, d1命令を使うことでまとめて読み取れます。パラメータd1に読み取り開始のレジスタアドレスを指定します。

温度、気圧の補正係数データは0x88から0x9Fまでの24バイトを連続して読み込み、計算します。また、湿度の補正係数は0xA1からの1バイトと0xE1から0xE7までの7バイトを読み込み計算します。

読み取りのコードを示します。tはバッファで24バイト分を確保しいます。
バッファから8ビットのデータを読み取るにはPEEK(バッファ)関数を使います。

DIM t(23)

I2CREADDATA $76, t, 24, $88
REM dig_T1からdig_T3、dig_P1からdig_P9の値を算出

IS2READDATA $76, t, 1, $A1
REN dig_H1の算出

I2CREADDATA $76, t, 7, $E1
REM dig_H2からdig_H6の値を算出

SPIでの補正係数の読み出し

SPIでレジスタから連続したデータを読み取るときはSPIREADDATA b, n, d1命令を使います。パラメータd1に読み込み開始のレジスタアドレスを指定して送信後、バッファにデータを指定バイト数分を読み込みます。

DIM t(23)

SPIREADDATA t, 24, $88
REM dig_T1からdig_T3、dig_P1からdig_P9の値を算出
SPIREADDATA t, 1, $A1
REM dig_H1の算出

SPIREADDATA t, 7, $E1
REM dig_H2からdig_H6の値を算出

補正係数の計算

温度、圧力、湿度の補正係数は表に示すレジスタに下位8ビット、上位8ビットの順で格納されています。

レジスタアドレス レジスタの内容 データ型
0x88/0x89 dig_T1[7:0]/[15:8] 符号なし整数
0x8A/0x8B dig_T2[7:0]/[15:8] 符号付き整数
0x8C/0x8D dig_T3[7:0]/[15:8] 符号付き整数
0x8E/0x8F dig_P1[7:0]/[15:8] 符号なし整数
0x90/0x91 dig_P2[7:0]/[15:8] 符号付き整数
0x92/0x93 dig_P3[7:0]/[15:8] 符号付き整数
0x94/0x95 dig_P4[7:0]/[15:8] 符号付き整数
0x96/0x97 dig_P5[7:0]/[15:8] 符号付き整数
0x98/0x99 dig_P6[7:0]/[15:8] 符号付き整数
0x9A/0x9B dig_P7[7:0]/[15:8] 符号付き整数
0x9C/0x9D dig_P8[7:0]/[15:8] 符号付き整数
0x9E/0x9F dig_P9[7:0]/[15:8] 符号付き整数
0xA1 dig_H1[7:0] 符号なし文字型
0xE1/0xE2 dig_H2[7:0]/[15:8] 符号付き整数
0xE3 dig_H3[7:0] 符号なし文字型
0xE4/0xE5[3:0] dig_H4[11:4]/[3:0] 符号付き整数
0xE5[7:4]/0xE6 dig_H5[3:0]/[11:4] 符号付き整数
0xE7 dig_H6 符号付き文字型

16ビットの符号なし整数(unsigned short)の値は上位8ビットのデータを256倍し、下位8ビットのデータと加算します。dig_T1の値は次の式で算出します。他の符号なし整数の場合も同様の計算をします。

dig_T1 = PEEK(t) + PEEK(t+1) * 256

16ビット符号付きの整数(signed short)の値は上位8ビット[15:8]のデータを256倍し、下位8ビット[7:0]のデータと加算します。この値は2の補数表現なのでMSBが1となる32768(0x1000)以上の値の場合は負数なのでこの数から65536(0x10000)を減じて負数にします。
dig_T2の値は次の式で算出します。他の符号付き整数の場合も同様の計算をします。

dig_T2 = PEEK(t) + PEEK(t+1) * 256
IF dig_T2 >= 32768 THEN dig_T2 = dig_T2 - 65536

dig_H4とdig_H5の値はレジスタ0xE5の値を半分ずつ組み込む点が他の係数の計算と異なります。
dig_H4の値は12ビットで、ビット11から4にレジスタ0xE4の値を、ビット3から0にレジスタ0xE5の下位4ビットを当てはめます。その値は、レジスタ0xE4の値を左へ4ビットシフトした値とレジスタ0xE5の下位4ビットを取り出すための0x0Fとの論理積との論理和で得られます。
レジスタ0xE5の値も12ビットで、ビット11から4にレジスタ0xE6の値を、ビット3から0にレジスタ0xE5の上位4ビットを当てはめます。その値はレジスタ0xE5の値を左へ4ビットシフトした値とレジスタ0xE5の値を右へ4ビットシフトした値との論理和で得られます。
以下にそのコードを示します。

E4 = PEEK(t+3)
E5 = PEEK(t+4)
E6 = PEEK(t+5)
dig_H4 = E4 << 4 OR E5 AND $0F
dig_H5 = E6 << 4 OR E5 >> 4
dig_H6 = PEEK(t+6)
IF dig_H6 >= 128 THEN dig_H6 = dig_H6 - 256

センサーから温度、湿度、気圧の生データの取得

BME280が測定した温度、湿度、気圧ので生データはレジスタ0xF7から0xFEまでの8つのレジスタに気圧、温度、湿度の順に格納されています。気圧と温度は20ビット、湿度は16ビットのデータになります。これをまとめとバッファに読み込んで生データを組み立てます。

8つのレジスタからまとめてデータを取り出すI2C向けのコードを示します。I2CREADDATA命令を使います。パラメータの$76はBME280のI2Cのアドレス、Dはバッファ、8は読み取るバイト数、$F7は読み取り前に送信するデータ(ここでは最初のレジスタアドレス)です。

DIM D(7)

I2CREADDATA $76, D, 8, $F7

SPI向けのコードは次に示すようにSPIREADDATA命令を使います。パラメータのDはバッファ、8は読み取るバイト数、$F7は読み取り前に送信するデータ(ここでは最初のレジスタアドレス)です。

DIM D(7)

SPIREADDATA D, 8, $F7

気圧の生データ

気圧の測定生データはレジスタ0xF7から0xF9までの3つのレジスタに次の表に示す順で20ビット分が格納されています。

レジスタ 名前 説明
0xF7 press_msb[7:0] 生の気圧測定出力データのMSB部分(19:12)
0xF8 press_lsb[7:0] 生の気圧測定出力データのLSB部分(11:4)
0xF9(ビット7〜4) press_xlsb[3:0] 生の気圧測定出力データのXLSB部分(3:0)

気圧、温度、湿度のデータを一括で読み取り、バッファDに格納されている状態で生の気圧データは次のコードで算出します。3つのレジスタのデータを表に示す順の20ビットのデータになるようにシフト演算し、それらの値の論理和で求めています。

USEVAR raw_p

raw_p = PEEK(D) << 12 OR PEEK(D+1) << 4 OR PEEK(D+2) >> 4

温度の生データ

温度の測定生データはレジスタ0xFAから0xFCまでの3つのレジスタに次の表に示す順で20ビット分が格納されています。

レジスタ 名前 説明
0xFA temp_msb[7:0] 生の温度測定出力データのMSB部分(19:12)
0xFB temp_lsb[7:0] 生の温度測定出力データのLSB部分(11:4)
0xFC(ビット7〜4) temp_xlsb[3:0] 生の温度測定出力データのXLSB部分(3:0)

気圧、温度、湿度のデータを一括で読み取り、バッファDに格納されている状態で温度生データは次のコードで算出できます。3つのレジスタのデータを表に示す順の20ビットのデータになるようにシフト演算し、それらの値の論理和で求めています。

USEVAR raw_t

raw_t = PEEK(D+3) << 12 OR PEEK(D+4) << 4 OR PEEK(D+5) >> 4

湿度の生データ

湿度の測定生データはレジスタ0xFDと0xFEの2つのレジスタに次の表のように16ビット分が格納されています。

レジスタ 名前 説明
0xFD hum_msb[7:0] 生の湿度測定出力データのMSB部分(15:8)
0xFE hum_lsb[7:0] 生の湿度測定出力データのLSB部分(7:0)

気圧、温度、湿度のデータを一括で読み取り、バッファDに格納されている状態で湿度生データは次のコードで算出できます。2つのレジスタのデータを16ビットのデータのとしての位置なるようにシフト演算し、それらの値の論理和で求めています。

USEVAR raw_h

raw_h = PEEK(D+6) << 8 OR PEEK(D+7) 

温度、湿度、気圧の値の計算

温度、湿度、気圧の生データを取得できたら温度、湿度、気圧の値を計算できます。データシートではAPIの仕様を推奨していますがKM-BASIC向けはありませんのでデータシートのページ49の8.1 Compensation formulas in double precision floating pointに記されているC言語のプログラムを変換することになります。
なお、湿度と気圧の値は温度の値を使って計算するので最初に温度の値を求める必要があります。

温度の値計算

データシートのページ49の8.1 Compensation formulas in double precision floating pointに示されたC言語のコードをKM-BASICに変換したサブルーチンを示します。
raw_tは上記で算出した温度の生データです。

USEVAR t_fine#

LABEL GETTMP
  VAR var1#
  VAR var2#
    
  var1# =  (FLOAT#(raw_t) / 16384  - FLOAT#(dig_T1) / 1024) * FLOAT#(dig_T2)
  var2# =  (FLOAT#(raw_t) / 131072 - FLOAT#(dig_T1) / 8192) * (FLOAT#(raw_t) / 131072 - FLOAT#(dig_T1) / 8192) * FLOAT#(dig_T3)
  t_fine# = var1# + var2#

RETURN t_fine# / 5120

気圧の値計算

データシートのページ49の8.1 Compensation formulas in double precision floating pointに示されたC言語のコードをKM-BASICに変換したサブルーチンを示します。戻り値はPa単位になります。
raw_pは上記で算出した上記の気圧の生データです。t_fine#は温度算出時に得られた値です。

LABEL GETPRE
  VAR var1#
  VAR var2#
  VAR p#
  
  REM --- hosei ---
  var1# = t_fine# / 2 - 64000
  var2# = var1# * var1# * FLOAT#(dig_P6) / 32768
  var2# = var2# + var1# * FLOAT#(dig_P5) * 2
  var2# = var2# / 4 + FLOAT#(dig_P4) * 65536
  var1# = (FLOAT#(dig_P3) * var1# * var1# / 524288 + FLOAT#(dig_P2) * var1#)/ 524288
  var1# = (1 + var1# / 32768) * FLOAT#(dig_P1)
  IF var1# = 0 THEN
    p# = 0
  ELSE
    p# = 1048576 - FLOAT#(raw_p)
    p# = (p# - var2# / 4096) * 6250 / var1#
    var1# = FLOAT#(dig_P9) * p# * p# / 2147483648
    var2# = p# * FLOAT#(dig_P8) / 32768
    p# = p# + (var1# + var2# + FLOAT#(dig_P7)) / 16
  ENDIF

RETURN p#

湿度の値計算

データシートのページ49の8.1 Compensation formulas in double precision floating pointに示されたC言語のコードをKM-BASICに変換したサブルーチンを示します。
raw_hは上記で算出した湿度の生データです。t_fine#は温度算出時に得られた値です。

LABEL GETHUM
  VAR hum#
  
  hum# = t_fine# - 76800
  hum# = (FLOAT#(raw_h) - (FLOAT#(dig_H4) * 64 + FLOAT#(dig_H5) / 16384 * hum#)) * (FLOAT#(dig_H2) / 65536 * (1 + FLOAT#(dig_H6) / 67108864 * hum# * (1 + FLOAT#(dig_H3) / 67108864 * FLOAT#(hum))))
  hum# = hum# * (1 - FLOAT#(dig_H1) * hum# / 524288)
  
  IF hum# > 100 Then hum# = 100
  If hum# < 0   Then hum# = 0

RETURN hum#

結果の表示

BME280での温度、湿度、気圧の取得、計算、表示を行なう部分のコードを以下に示します。レジスタの並び順で気圧、温度、湿度の生データを取得し、温度、気圧、湿度の順でそれぞれの値をサブルーチンから取得し、日本語表示しています。

USEGRAPHIC
USECLASS CKNJ16
K=New(CKNJ16,"UTF-8")

Do
  REM --- read data from 0xF7..0xFE: 8byte
  SPIREADDATA D, 8, $F7

  REM --- Get raw data
  raw_p = PEEK(D)   << 12 OR PEEK(D+1) << 4 OR PEEK(D+2) >> 4
  raw_t = PEEK(D+3) << 12 OR PEEK(D+4) << 4 OR PEEK(D+5) >> 4
  raw_h = PEEK(D+6) <<  4 OR PEEK(D+7)

  REM --- Get temperature, pressure, humidity
  temp# = GOSUB#(GETTMP)       :REM Get temperature
  pres# = GOSUB#(GETPRE) / 100 :REM Get pressure
  humi# = GOSUB#(GETHUM)       :REM Get humidity

  REM --- Display results ---
  POINT 0,  0 : K.GPRT(STRFTIME$("%Y-%m-%d %H:%M:%S"),7, 1)
  POINT 0, 18 : K.GPRT("温度:"+SPRINTF$("%3.1f",temp#)+"℃", 7, 2)
  POINT 0, 36 : K.GPRT("湿度:"+SPRINTF$("%3.1f",humi#)+"%",  7, 0)
  POINT 0, 54 : K.GPRT("気圧:"+SPRINTF$("%4.1f",pres#)+"hPa",7, 0)

  WAIT 600
LOOP

実行結果

実行結果の表示は次のとおりです。I2CとSPIで表示は変わりません。

DSC04372.JPG

さいごに

I2Cでの通信はすんなりできたのでSPIもすんなりできるだろうなと思っていたらうまくデータを取得できなかった。いろいろと調べるうちにpico-sdkのサンプルプログラム(pico-examples/spi/bme280_spi/bme280_spi.c)の125行目に buf[0] = reg & 0x7f; // remove read bit as this is a writとあり、レジスタアドレスの最上位ビットを0にすると書き込みであることが分かり、これで解決しました。データシートページ33にもその旨が下記のようにちゃんと記されていました。

In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read).

参考サイト

プログラム

以下にI2CおよびSPIで実行できるプログラムを示します。
BME280をI2C接続している場合は1行目の変数Fの値を0に、SPI接続している場合は1に設定します。
出力にはデバイスのIDを表示するようにしています。

ファイル名が長いので実機で実行する場合は8文字のファイル名にしてください。

BME280-I2C-SPI-KM-Picocalc.bas
F=0 : REM Flag: I2C=0, SPI=1
REM --------------------------------------------------
REM BME280 measurement program by KM-BASIC on PicoCalc
REM I2C bus#0, SDA=GP4, SCL=GP5 (100kHz)
REM I2C address: $76
REM SPI bus#0, SDA=GP4, SCL=GP5 (100kHz)
REM --------------------------------------------------
USEGRAPHIC
USECLASS CKNJ16

REM --- I2C address
USEVAR ADDR : ADDR = $76

USEVAR t_fine#
USEVAR pres  :REM pressure
USEVAR temp  :REM temper
USEVAR humi  :REM Humidity

DIM D(7) : REM Data buffer
DIM C(1)
C(0)=$E0 AND $7F : C(1)=$B6

REM --- calib const
USEVAR dig_T1, dig_T2, dig_T3
USEVAR dig_P1, dig_P2, dig_P3
USEVAR dig_P4, dig_P5, dig_P6
USEVAR dig_P7, dig_P8, dig_P9
USEVAR dig_H1, dig_H2, dig_H3
USEVAR dig_H4, dig_H5, dig_H6
USEVAR E4, E5, E6
USEVAR var1#, var2#
USEVAR raw_t, raw_p, raw_h,hum#

CLS
REM --- Open channel
IF F = 0 THEN
  I2C 100
ELSE
  SPI 10000, 8, , 5 : REM 10MHz/8bit/Mode0 CS:GP5
ENDIF

REM --- Soft Rest
IF F = 0 THEN
  I2CWRITEDATA ADDR, C, 2
ELSE
  SPIWRITEDATA C,2
ENDIF
DELAYMS 2

REM --- Setup and Get Compensation parameters
GOSUB Setup
GOSUB calibration

REM --- Get device ID
IF F = 0 THEN
  I2CREADDATA ADDR, D, 1, $D0
ELSE
  SPIREADDATA D, 1, $D0
ENDIF
I = PEEK(D)

REM --- Display ID
K=New(CKNJ16,"UTF-8")
POINT 0, 72 : K.GPRT("ID:"+HEX$(I),7,3)

REM --- main loop
Do
  REM --- read data from 0xF7-0xFE: 8byte
  IF F = 0 THEN 
    I2CREADDATA ADDR, D, 8, $F7
  ELSE
    SPIREADDATA D, 8, $F7
  ENDIF

  REM --- Get raw data
  raw_p = PEEK(D) << 12 OR PEEK(D+1) < 4 OR PEEK(D+2) >> 4
  raw_t = PEEK(D+3) << 12 OR PEEK(D+4) << 4 OR PEEK(D+5) >> 4
  raw_h = PEEK(D+6) << 8 OR PEEK(D+7)

  REM --- Get temperature, pressure, hu,idity
  temp# = GOSUB#(GETTMP)       :REM Get temperature
  pres# = GOSUB#(GETPRE) / 100 :REM Get pressure
  humi# = GOSUB#(GETHUM)       :REM Get humidity

  REM --- Display results
  POINT 0,  0 : K.GPRT(STRFTIME$("%Y-%m-%d %H:%M:%S"),7, 1)
  POINT 0, 18 : K.GPRT("温度:"+SPRINTF$("%3.1f",temp#)+"℃", 7, 2)
  POINT 0, 36 : K.GPRT("湿度:"+SPRINTF$("%3.1f",humi#)+"%",  7, 2)
  POINT 0, 54 : K.GPRT("気圧:"+SPRINTF$("%4.1f",pres#)+"hPa",7, 2)

  WAIT 120
Loop

End

REM --- Setup measurement mode  ---
LABEL Setup
  IF F = 0 THEN
    I2CWRITE ADDR, $F2, $01   : REM ctrl_hum: x1
    I2CWRITE ADDR, $F4, $27   : REM ctrl_meas: temp x1, press x1, mode normal(3)
    I2CWRITE ADDR, $F5, $A0   : REM config_reg
  ELSE
    SPIWRITE $F2 and $7f, $01 : REM ctrl_hum: x1
    SPIWRITE $F4 and $7f, $27 : REM ctrl_meas: temp x1, press x1, mode normal(3)
    SPIWRITE $F5 and $7f, $A0 : REM config_reg
  ENDIF
RETURN

REM --- BME280 calibration ---
LABEL calibration
  DIM t(23)
 
  REM --- temp
  IF F = 0 THEN
    I2CREADDATA ADDR, t, 24, $88
  ELSE 
    SPIREADDATA t, 24, $88
  ENDIF
      
  dig_T1 = PEEK(t) + PEEK(t+1) * 256
  dig_T2 = PEEK(t+2) + PEEK(t+3) * 256
  IF dig_T2 >= 32768 THEN dig_T2 = dig_T2 - 65536
  dig_T3 = PEEK(t+4) + PEEK(t+5) * 256
  IF dig_T3 >= 32768 THEN dig_T3 = dig_T3 - 65536

  REM --- press
  dig_P1 = PEEK(t+6) + PEEK(t+7) * 256
  dig_P2 = PEEK(t+8) + PEEK(t+9) * 256
  IF dig_P2 >= 32768 THEN dig_P2 = dig_P2 - 65536
  dig_P3 = PEEK(t+10) + PEEK(t+11) * 256
  IF dig_P3 >= 32768 THEN dig_P3 = dig_P3 - 65536
  dig_P4 = PEEK(t+12) + PEEK(t+13) * 256
  IF dig_P4 >= 32768 THEN dig_P4 = dig_P4 - 65536
  dig_P5 = PEEK(t+14) + PEEK(t+15) * 256
  IF dig_P5 >= 32768 THEN dig_P5 = dig_P5 - 65536
  dig_P6 = PEEK(t+14) + PEEK(t+17) * 256
  IF dig_P6 >= 32768 THEN dig_P6 = dig_P6 - 65536
  dig_P7 = PEEK(t+16) + PEEK(t+19) * 256
  IF dig_P7 >= 32768 THEN dig_P7 = dig_P7 - 65536
  dig_P8 = PEEK(t+18) + PEEK(t+21) * 256
  IF dig_P8 >= 32768 THEN dig_P8 = dig_P8 - 65536
  dig_P9 = PEEK(t+20) + PEEK(t+23) * 256
  IF dig_P9 >= 32768 THEN dig_P9 = dig_P9 - 65536

  REM --- Humi
  IF F = 0 THEN
    I2CREADDATA ADDR, t, 1, $A1
  ELSE
    SPIREADDATA t, 1, $A1
  ENDIF
  dig_H1 = PEEK(t)

  IF F = 0 THEN
    I2CREADDATA ADDR, t, 7, $E1
  ELSE
    SPIREADDATA t, 7, $E1
  ENDIF  
  dig_H2 = PEEK(t) + PEEK(t+1) * 256
  IF dig_H2 > 32767 THEN dig_H2 = dig_H2 - 65536

  dig_H3 = PEEK(t+2)
  E4 = PEEK(t+3)
  E5 = PEEK(t+4)
  E6 = PEEK(t+5)
  dig_H4 = E4 << 4 OR E5 And $0F
  dig_H5 = E6 << 4 OR E5 >> 4
  dig_H6 = PEEK(t+6)
  IF dig_H6 >= 128 THEN dig_H6 = dig_H6 - 256
RETURN

REM --------------------------------------
REM Calculate temp
LABEL GETTMP
  VAR var1#
  VAR var2#

  var1# =  (FLOAT#(raw_t) / 16384  - FLOAT#(dig_T1) / 1024) * FLOAT#(dig_T2)
  var2# =  (FLOAT#(raw_t) / 131072 - FLOAT#(dig_T1) / 8192) * (FLOAT#(raw_t) / 131072 - FLOAT#(dig_T1) / 8192) * FLOAT#(dig_T3)
  t_fine# = var1# + var2#
RETURN t_fine# / 5120

REM --------------------------------------
REM Calculate pressure
LABEL GETPRE
  VAR var1#
  VAR var2#
  VAR p#

  var1# = t_fine# / 2 - 64000
  var2# = var1# * var1# * FLOAT#(dig_P6) / 32768
  var2# = var2# + var1# * FLOAT#(dig_P5) * 2
  var2# = var2# / 4 + FLOAT#(dig_P4) * 65536
  var1# = (FLOAT#(dig_P3) * var1# * var1# / 524288 + FLOAT#(dig_P2) * var1#)/ 524288
  var1# = (1 + var1# / 32768) * FLOAT#(dig_P1)
  IF var1# = 0 THEN
    p# = 0
  Else
    p# = 1048576 - FLOAT#(raw_p)
    p# = (p# - var2# / 4096) * 6250 / var1#
    var1# = FLOAT#(dig_P9) * p# * p# / 2147483648
    var2# = p# * FLOAT#(dig_P8) / 32768
    p# = p# + (var1# + var2# + FLOAT#(dig_P7)) / 16
  EndIF
RETURN p#

REM --------------------------------------
REM Calculate humidity 
LABEL GETHUM
  VAR hum#
    
  hum# = t_fine# - 76800
  hum# = (FLOAT#(raw_h) - (FLOAT#(dig_H4) * 64 + FLOAT#(dig_H5) / 16384 * hum#)) * (FLOAT#(dig_H2) / 65536 * (1 + FLOAT#(dig_H6) / 67108864 * hum# * (1 + FLOAT#(dig_H3) / 67108864 * FLOAT#(hum))))
  hum# = hum# * (1 - FLOAT#(dig_H1) * hum# / 524288)
  
  IF hum# > 100 THEN hum# = 100
  IF hum# < 0   THEN hum# = 0

RETURN hum#
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?