はじめに
PicoCalc版MachiKaniaでI2C通信を使用するADT7410とSPI通信を使用するADT7310の両方で温度測定できるようになったので、I2C通信とSPI通信の両方を使用できる温度、湿度、気圧センサーのBME-280も使用できるだろうと、以前作成したMMBasicのI2CとADT7310のプログラムを元にいろいろと試し、動作するプログラムを作成できたのでその内容をまとめました。
検証環境
次の環境でプログラムを作成、実行しました。
- PcioCalc : コアをRaspberry Pi Pico 2 Wに交換
- ファームウェア : MachiKania phyllosoma : 1.5.2.0 KM-1509
- センサー:BME280使用 温湿度・気圧センサーモジュールキット
デバイス間の接続
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 |
実体配線図は次のようになります。
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 |
実体配線図は次のようになります。
通信用ピンの設定
MachiKaniaではI2CおよびSPIで使用するピンの指定はSDカードのルートディレクトリに配置した設定ファイルMACHIKAP.INI
に記述します。
I2Cピンの設定
ADT7410のときと同様にGP4、GP5を使用するように下記の設定をしています。
<略>
143 I2CSDA=4
144 I2CSCL=5
<略>
SPIピンの設定
ADT7310のときと同様にGP2、GP3、GP4を使用するように下記の設定をしています。
<略>
119 SPIMISO=4
120 SPIMOSI=3
121 SPICLK=2
<略>
処理の流れ
BME280を使った温度、湿度、圧力測定は以下の流れで実行します。
- PicoCalcでそれぞれの通信のマスター機能を有効化する
- ソフトリセットコマンドの送信
- 測定モードの設定
- 補正係数の読み出し
- センサーから温度、湿度、気圧の生データの取得
- 取得した生データから温度、気圧、湿度の値を算出する
- 算出された温度、湿度、気圧の値の表示
通信のマスター機能の有効化
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で表示は変わりません。
さいごに
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文字のファイル名にしてください。
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#