LoginSignup
2
1

More than 5 years have passed since last update.

micro:bit / mbed / I2C デバイス (温湿度センサー HTU21D) を外づけする

Last updated at Posted at 2018-06-10

概要

I2C デバイス (温湿度センサー HTU21D) を micro:bit に外づけしてみる。I2C バスは micro:bit の標準端子 (SCL は P19、SDA は P20) をそのまま使う。標準の I2C バスを使うのであればプルアップ抵抗はスレーブ側には不要である。

ここではマルツ製のブレークアウトボード (温度/湿度センサーモジュール基板【MHTU21D】)を使った。

(1) 温度と湿度とを読み取る方法

#include "MicroBit.h"
MicroBit uBit;

#define HTDU21D_ADDRESS           0x40<<1
#define TRIGGER_TEMP_MEASURE_HOLD 0xE3
#define TRIGGER_HUMD_MEASURE_HOLD 0xE5

float readTemp(){
    // master hold モードで温度データ (8 ビットデータを 2 つ) を読み出す。
    char buf[] = {TRIGGER_TEMP_MEASURE_HOLD};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 2);

    // 2 つの 8 ビットデータを 16 ビットデータに並べ換えて、
    // 下 2 ビットは不要なので 00 にして、
    // 変換式を適用して返す。
    unsigned short raw = (buf[0]<<8 | buf[1]) & 0xfffc;
    return -46.85F + 175.72F * (float)raw / 65536.0F;
}

float readHumid(){
    // master hold モードで湿度データ (8 ビットデータを 2 つ) を読み出す。
    char buf[] = {TRIGGER_HUMD_MEASURE_HOLD};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 2);

    // 2 つの 8 ビットデータを 16 ビットデータに並べ換えて、
    // 下 2 ビットは不要なので 00 にして、
    // 変換式を適用して返す。
    unsigned short raw = (buf[0]<<8 | buf[1]) & 0xfffc;
    return -6.0F + 125.0F * (float)raw / 65536.0F;
}

int main(void){
    uBit.init();

    while(1){
        printf("%.1f C, %.1f %%\n", readTemp(), readHumid());
        wait(1.0);
    }

    release_fiber();
    return 0;
}

(2) CRC を試してみる場合


#include "MicroBit.h"
MicroBit uBit;

#define HTDU21D_ADDRESS 0x40<<1
#define SHIFTED_DIVISOR 0x988000UL // (2^8 + 2^5 + 2^4 + 1) << 15

unsigned short check_crc(char msb, char lsb, char crc){
    unsigned int divisor = SHIFTED_DIVISOR;

    // 測定値の上位 8 ビット、下位 8 ビット、CRC 値の順に並べて、
    unsigned int remainder = msb<<16 | lsb<<8 | crc;

    // その左端から順番に 1 が立っているかどうかを調べて何かをする。
    for(int i=23; i>7; i--){
        if(remainder & 1<<i){
            remainder ^= divisor;
        }
        divisor >>= 1;        
    }
    // 正しい場合は 0 を、誤りの場合は 999 を返す。
    return (remainder == 0) ? 0 : 999;
}

int main(void){
    uBit.init();

    // 正しい例 (データシートから引用) (0 が返る)
    printf("%d\n", check_crc(0x68,0x3A,0x7c));
    printf("%d\n", check_crc(0x4E,0x85,0x6b));

    // 誤りの例 (999 が返る)
    printf("%d\n", check_crc(0x68,0x3A,0x7d));
    printf("%d\n", check_crc(0x4E,0x85,0x6c));

    release_fiber();
    return 0;
}

(3) 温度、湿度の測定と CRC とを組み合わせる方法

#include "MicroBit.h"
MicroBit uBit;

#define HTDU21D_ADDRESS           0x40<<1
#define TRIGGER_TEMP_MEASURE_HOLD 0xE3
#define TRIGGER_HUMD_MEASURE_HOLD 0xE5
#define SHIFTED_DIVISOR           0x988000UL // (2^8 + 2^5 + 2^4 + 1) << 15

unsigned short check_crc(char msb, char lsb, char crc){
    unsigned int divisor   = SHIFTED_DIVISOR;
    unsigned int remainder = msb<<16 | lsb<<8 | crc;

    for(int i=23; i>7; i--){
        if(remainder & 1<<i){
            remainder ^= divisor;
        }
        divisor >>= 1;        
    }
    return (remainder == 0) ? 0 : 999;
}

float readTemp(){
    char buf[3] = {TRIGGER_TEMP_MEASURE_HOLD};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 3);

    unsigned short raw       = (buf[0]<<8 | buf[1]) & 0xfffc;
    unsigned short crcResult = check_crc(buf[0], buf[1], buf[2]);

    // CRC が正しい場合は測定値を返し、誤りの場合は 999 を返す。
    return (crcResult == 0) ?
           -46.85F + 175.72F * (float)raw / 65536.0F :
           crcResult;
}

float readHumid(){
    char buf[3] = {TRIGGER_HUMD_MEASURE_HOLD};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 3);

    unsigned short raw       = (buf[0]<<8 | buf[1]) & 0xfffc;
    unsigned short crcResult = check_crc(buf[0], buf[1], buf[2]);

    // CRC が正しい場合は測定値を返し、誤りの場合は 999 を返す。    
    return (crcResult == 0) ?
           -6.0F + 125.0F * (float)raw / 65536.0F :
           crcResult;
}

int main(void){
    uBit.init();

    while(1){
        printf("%.1f C, %.1f %%\n", readTemp(), readHumid());
        wait(1.0);
    }

    release_fiber();
    return 0;
}

(4) 測定分解能を変更する方法

#include "MicroBit.h"
MicroBit uBit;

#define HTDU21D_ADDRESS 0x40<<1
#define WRITE_USER_REG  0xE6
#define READ_USER_REG   0xE7

// ユーザーレジスタを読み出すための函数
char read_user_register(void){
    char buf[] = {READ_USER_REG};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 1);

    return buf[0];
}

// 測定分解能を設定するための函数 (ビット 7, 0 だけを書き換える)
void setResolution(char resolution){

    // 現在のユーザーレジスタを読み出す。
    char userRegister = read_user_register();

    // 測定分解能に関係するビット (7, 0) だけを書き換える。
    // ビット 7, 0 以外を誤って書き換えてしまわないようにする。
    userRegister &= 0b01111110;
    resolution   &= 0b10000001;
    userRegister |= resolution;

    char buf[] = {WRITE_USER_REG, userRegister};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 2);
}

int main(void){
    uBit.init();

    // 湿度 12 ビット、温度 14 ビット (デフォルト) にする。0b 0000 0010 = 0x2 になるはずである。 
    setResolution(0b00000000);
    printf("User Register: 0x%X\n", read_user_register());

    // 湿度 8 ビット、温度 12 ビットにする。0b 0000 0011 = 0x3 になるはずである。 
    setResolution(0b00000001);
    printf("User Register: 0x%X\n", read_user_register());

    // 湿度 10 ビット、温度 13 ビットにする。0b 1000 0010 = 0x82 になるはずである。 
    setResolution(0b10000000);
    printf("User Register: 0x%X\n", read_user_register());

    // 湿度 11 ビット、温度 11 ビットにする。0b 1000 0011 = 0x83 になるはずである。 
    setResolution(0b10000001);
    printf("User Register: 0x%X\n", read_user_register());

    // デフォルトに戻す。0b 0000 0010 = 0x2 になるはずである。 
    setResolution(0b00000000);
    printf("User Register: 0x%X\n", read_user_register());

    release_fiber();
    return 0;
}

(5) ソフトリセットをかける方法

データシートによればソフトリセットを推奨するとの由。

#include "MicroBit.h"
MicroBit uBit;

#define HTDU21D_ADDRESS 0x40<<1
#define WRITE_USER_REG  0xE6
#define READ_USER_REG   0xE7
#define SOFT_RESET      0xFE

char read_user_register(void){
    char buf[] = {READ_USER_REG};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1); 
    uBit.i2c.read (HTDU21D_ADDRESS, buf, 1);

    return buf[0];
}

void setResolution(char resolution){
    char userRegister = read_user_register();

    userRegister &= 0b01111110;
    resolution   &= 0b10000001;
    userRegister |= resolution;

    char buf[] = {WRITE_USER_REG, userRegister};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 2);
}

// ソフトリセットをかけるための函数
void softReset(void){
    char buf[] = {SOFT_RESET};
    uBit.i2c.write(HTDU21D_ADDRESS, buf, 1);
}

int main(void){
    uBit.init();

    // 湿度 12 ビット、温度 14 ビット (デフォルト) にする。0b 0000 0010 = 0x2 になるはずである。 
    setResolution(0b00000000);
    printf("User Register: 0x%X\n", read_user_register());

    // 湿度 11 ビット、温度 11 ビットにする。0b 1000 0011 = 0x83 になるはずである。 
    setResolution(0b10000001);
    printf("User Register: 0x%X\n", read_user_register());

    // ソフトリセットをかける。デフォルト (0b 0000 0010 = 0x2) に戻るはずである。
    softReset();
    printf("User Register: 0x%X\n", read_user_register());

    release_fiber();
    return 0;
}

(6) 全体をごく簡単にライブラリにまとめた。

main.cpp
#include "MicroBit.h"
#include "HTU21D.h"

MicroBit uBit;
HTU21D   HTU;

int main(void){
    uBit.init();

    //HTU.softReset();

    //HTU.setResolution(RH12_TEMP14);
    //HTU.setResolution(RH08_TEMP12);
    //HTU.setResolution(RH10_TEMP13);
    //HTU.setResolution(RH11_TEMP11);
    while(1){
        //printf("User Register: 0x%X\n", HTU.read_user_register());

        float temp  = HTU.readTemp();
        float humid = HTU.readHumid();
        printf("%.1f C, %.1f %%\n", temp, humid);

        wait(1.0);
    }

    release_fiber();
    return 0;
}

実行結果 (値が 999.0 になっているのは SDA を引っこ抜いたとき):

参考

HTU21D データシート
https://lancaster-university.github.io/microbit-docs/ubit/i2c/
Harry Fairhead, micro:bit IoT in C, I/O Press, pp.91-101
勝 純一, mbed 電子工作レシピ, 翔泳社

関連

はてなブログ / 温湿度センサー HTU21D

2
1
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
2
1