第29回 温湿度・気圧センサー(詳細)

今回は温湿度・気圧センサーモジュールの詳細仕様を説明します。

目次

今回の説明内容

前回の記事で、温湿度・気圧センサーのモジュール内にはメモリがあり、SPI通信を利用してそのメモリを読み書きすることにより、動作設定をしたり測定データを読み出したりすることを説明しました。

今回の記事では、そのメモリの構成や読み書き方法などについて、詳細を説明します。

メモリとは?

センサモジュール内のメモリは、通常のメモリと同様にアドレスが割り振られています。

「メモリにアドレスが振られている」と説明されても「アドレス?」という方もいらっしゃるかもしれません。

PICマイコン電子工作入門シリーズでは、メモリについて具体的な説明をしていませんので、ここで簡単にメモリについて説明します。(メモリをすでにご存知の方は次のセクションに進んでいただいて問題ありません)


普段使っているPCやスマホや、実践編で使用しているPICマイコンやセンサーモジュールの内部には、計算結果やデータを保存しておくための「メモリ」があります

このメモリは、1バイト(8ビット)を基本単位としています。

例えば、PIC16F18857では、内部に4096バイトのメモリを持っています。

上のように1バイトのデータを保存しておく箱のようなものが4096個用意されているわけです。

また、今回使用する温湿度・気圧センサーは56バイトのメモリを持っています。

一般的なPCやスマホでは、ギガバイト単位のメモリが搭載されていますので、ちょっとスケールが違いますよね。


メモリにデータを保存する場合、メモリの「どこに」保存するか指定する必要があります

また、メモリからデータを読み出す場合も同様に、「どこの」メモリからデータを読み出すのか指定する必要があります

このように「どこ」を指定するために、メモリには特定するための番号が振られています

この番号は、「アドレス」(住所)と呼ばれています。

実世界を見てみると、家やビルなどの場所を特定する場合、住所を使いますよね。メモリの場合もこれに見立てて「アドレス」と呼んでいます。

この「アドレス」は、通常16進数で表記します。

例えば、アドレスが0xFFFのメモリのことを「0xFFF番地のアドレス」というように〇〇番地、という呼び方をします。

さらに、メモリの構成を示したものを「メモリマップ」(メモリの地図)と呼びます。

このようにメモリを実世界に見立てて「マップ(地図)」「アドレス(住所)」「番地」という言葉を使用しています。


前回の記事で、センサーモジュール内のメモリに、いろいろなデータが格納されていることを説明しました。

例えば測定データを読み出す場合、何番地のアドレスから読み出すのか、読み出し時にアドレスを指定する必要があります。

センサーモジュールのデータシートには、何番地にどのようなデータが格納されているか説明されています。

次のセクションでは、センサー本体のBME280のメモリマップの詳細を説明します。

BME280のメモリマップ詳細

BME280のメモリマップは以下のように構成されています。

急に難しくなってきましたね…

この情報を読み取るとき、注意点があります。

メモリのアドレスは0番地から順番に割り振られていますが、上のメモリマップはアドレスの順番で説明するのではなく、「温度データ」は何番地にある、というように項目ごとに表示されています。

メモリの0番地から順番に説明されているわけではない、という点に注意して読み解いていきましょう!


例えば、湿度は16ビットデータとしてメモリに格納されていて、上位8ビット(=1バイト)は0xFD番地、下位8ビットは0xFE番地に格納されていることがわかります。

つまり、湿度データを取得する場合は、0xFD番地と0xFE番地のデータを読み出せば良いことがわかります。(なお、取得した湿度データは校正されていませんので、別途校正用データで計算をする必要があります)


次に、動作設定を行うには、表の中央付近にある0xF5番地に設定値を書き込みます。

前回の記事で説明しました、測定データの取得の流れをメモリマップを見ながら確認しましょう。

測定データは次の流れで取得します。

「❶ 動作設定」では、次のメモリにセンサの動作設定の値を書き込みます。

次に「❷ 構成用データ要求」と「❸ 構成用データ」の通信で、次の校正用データを取得します。

「❹ 測定指示」では、「❶ 動作設定」と同じメモリに測定指示をするための値を書き込みます。

測定指示をすると、センサは温湿度と気圧のデータを測定し、メモリの測定データのところに測定値を保存します。

これで測定ができましたので、「❺ 測定データ要求」と「❻ 測定データ」で次の測定データを取得します。


なんだかちょっとお腹いっぱい、という感じですよね。

そのような中、申し訳ございません。さらにSPI通信を利用してどのようにデータを読み出し、書き込むかについて説明します。

読み出し方法

特定のアドレスからデータを読み出す場合、次の通信手順で行います。

STEP
アドレスの送信

PICマイコンから、読み出したいデータの「アドレス」をMOSI信号線で送信します。

STEP
読み出しデータの受信

PICマイコンからクロック信号を送り続けると、センサーはそのアドレスのデータをMISO信号線で返信します。1バイトを読み取る場合は、クロック信号を8回発生すれば読むことができます。


ところで、メモリマップを確認すると、湿度データは2バイトですよね。つまり、2バイト読み出す必要があるわけです。

このように連続するメモリのデータを読み出したい場合、1バイトのデータを受信したあと、さらにPICマイコンがクロック信号を発生し続けると、センサーは次のアドレスのデータをMISO信号線で送ってきます。

先ほどのメモリマップを確認いただければわかると思いますが、測定データや校正用データのメモリはアドレスが連続しています。

このようなデータを読む場合は、「アドレス送信 → 1バイト読み込み、次のアドレス送信 → 1バイト読み込み」ということはせずに、「アドレス送信 → 1バイト読み込み → 1バイト読み込み…」と続けて読むことができます。

SPI通信手順にまとめると以下のようになります。以下は1バイト読み取りの例ですが、連続して読み込む場合はクロック信号を必要バイト数分繰り返せばOKです。(クリックすると拡大できます)


PICマイコンのプログラムは、このタイミングチャートに合わせて作成します。

書き込み方法

特定のアドレスにデータを書き込む場合、次の通信手順で行います。

STEP
アドレスの送信

PICマイコンから、書き込みを行う「アドレス」をMOSI信号線で送信します。

STEP
書き込みデータの送信

続いて、PICマイコンから書き込みデータをMOSI信号線で送信します。

データの書き込み手順は単純ですが、注意点があります。

データを書き込むときのアドレス指定は、アドレスの最上位1ビットを0にします。(これはSPI通信の決まりではなく、このセンサーの仕様です)

例えば、「動作設定」のアドレスは0xF5(=11110101)ですが、書き込むときに指定するアドレスは0x75(=01110101)になります。

つまり、動作設定を行う場合は次のようにアドレスを指定して、データを書き込みます。


データを書き込むときのSPI通信手順をまとめると次のようになります。(クリックすると拡大できます)

測定データ計算方法

最後に取得したデータの計算方法の概要を説明します。

取得した測定データは、校正用データで校正する必要があります。

ただ、申し訳ございません。計算方法の意味はよくわかっていないので詳しい説明はできません!

なんと無責任な、という感じですが、校正はセンサーの物理的な特性に合わせて計算する必要があり、その計算方法はメーカーから指定されていますので、その計算方法に従う必要があります。

データシートには計算式ではなく、C言語のプログラムで説明されていますので、測定データの校正はそのプログラムをコピペして作成することにします。

次のプログラムは、データシートに記載されている校正用のコードなんですが、、、何をしているのかわからないですよね。

次のプログラムで、dig_Tdig_Pdig_Hはそれぞれ温度、気圧、湿度の校正用データです。

adc_Tadc_Padc_Hはそれぞれ測定データ(校正前の生データ)になります。

何をしているのかはあまり深入りしないほうが良さそうですよね。

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BME280_S32_t t_fine;

BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T)
{
    BME280_S32_t var1, var2, T;
    var1 = ((((adc_T>>3) – ((BME280_S32_t)dig_T1<<1))) * ((BME280_S32_t)dig_T2)) >> 11;
    var2 = (((((adc_T>>4) – ((BME280_S32_t)dig_T1)) * ((adc_T>>4) – ((BME280_S32_t)dig_T1))) >> 12) * ((BME280_S32_t)dig_T3)) >> 14;
    t_fine = var1 + var2;
    T =(t_fine*5+128)>>8;

    return T;
}

// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P)
{
    BME280_S64_t var1, var2, p;
    var1 = ((BME280_S64_t)t_fine) – 128000;
    var2 = var1 * var1 * (BME280_S64_t)dig_P6;
    var2 = var2 + ((var1*(BME280_S64_t)dig_P5)<<17);
    var2 = var2 + (((BME280_S64_t)dig_P4)<<35);
    var1 = ((var1 * var1 * (BME280_S64_t)dig_P3)>>8) + ((var1 * (BME280_S64_t)dig_P2)<<12); var1 = (((((BME280_S64_t)1)<<47)+var1))*((BME280_S64_t)dig_P1)>>33;

    if (var1 == 0)
    {
         return 0; // avoid exception caused by division by zero
    }

    p = 1048576-adc_P;
    p = (((p<<31)-var2)*3125)/var1;
    var1 = (((BME280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
    var2 = (((BME280_S64_t)dig_P8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)dig_P7)<<4);

    return (BME280_U32_t)p;
}

// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46.333 %RH
BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H)
{
    BME280_S32_t v_x1_u32r;
    v_x1_u32r = (t_fine – ((BME280_S32_t)76800));
    v_x1_u32r = (((((adc_H << 14) – (((BME280_S32_t)dig_H4) << 20) – (((BME280_S32_t)dig_H5) * v_x1_u32r)) +
        ((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r *
        ((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
        ((BME280_S32_t)dig_H2) + 8192) >> 14));
    v_x1_u32r = (v_x1_u32r – (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >>; 7) * ((BME280_S32_t)dig_H1)) >> 4));
    v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
    v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);

    return (BME280_U32_t)(v_x1_u32r>>12);
}

(BOSCH社 BME280データシートより引用)


次回の記事から、SPI通信の動作確認プログラムを作成します。

更新履歴

日付内容
2018.4.22新規投稿
2025.7.28メモリ説明補足
引用プログラム整形
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次