日曜技術者のメモ

趣味でやった事のメモ書きです。

リチウムコイン電池で動くワイヤレス温湿度気圧ロガーを作ってみた-1-

秋月で温度・湿度・気圧をまとめて測定できるBME280というセンサーを買ったので
リチウムコイン電池で動くワイヤレスな装置を作ってみた。

f:id:ginnyu-tei:20170801215519j:plain:w400
(左:センサー・送信 右:受信)

主な部品リスト

名前 価格
MSP430G2553 210円
BME280モジュール 1080円
315MHz無線送信モジュール 2160円
315MHz無線受信モジュール 2160円
32.768kHz 水晶発振子 30円
ボタン電池基板取付用ホルダー CR2032用 50円
1.27mmピッチユニバーサル基板 100円
47kΩ
1.5kΩ
LED

リチウムコイン電池でマイコンを動かすのは初めてなので色々試行錯誤してやってみたい。
マイコンはTiの超低消費電力マイコンMSP430をチョイス。使った事のないマイコンなのでMSP430 Value Line LaunchPad Development Toolと追加でMSP430G2553を購入。 ユニバーサル基板はハーフピッチなのはIM315のコネクタが1.27mmピッチなのでそれに合わせた。

回路概要

今回はUEWを使って少しずつ動作確認しながら作成したので回路図は書かなかった。
裏側はこんなかんじ↓
f:id:ginnyu-tei:20170801231856j:plain:w400
なので、ここでは接続の概要だけ記載しておく。

送信側

BM280はSPI4線でMSP430のUSCI_B0に接続、CSはP1.3を使った。
IM315TXとはUART(P1.2、P1.3)で接続。(送信頻度が少ないのでBusyは配線してない)
32.768kHz 水晶発振子はLPM3からの復帰用にXIN、XOUTに接続。

受信側

受信側はPCでロギングする為にIM315RXとFT232xを接続した。

MSP430プログラム

ソフトはmain.cとUARTでの文字送信用関数群のsimple_uart.hとBM280を制御するbm280.hに分かれている。

BM280の制御プログラムは以下ページのコードを使わせて頂きました。
www.mgo-tec.com

main.c

#include <msp430.h> 
#include <stdint.h>
#include <simple_uart.h>
#include <bme280.h>

/*
 * main.c
 */
int main(void)
{
    //WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    WDTCTL = WDT_ADLY_1000;
    IE1 |= WDTIE;

    //クロック設定
    DCOCTL = 0;
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    //P1.5 SCLK
    P1SEL |= BIT5;
    P1SEL2 |= BIT5;
    //P1.6 SOMI
    P1SEL |= BIT6;
    P1SEL2 |= BIT6;
    //P1.7 SIMO
    P1SEL |= BIT7;
    P1SEL2 |= BIT7;
    //P1.3 SS
    P1DIR |= BIT3;
    P1OUT |= BIT3;

    //P1.1 RXD
    P1SEL |= BIT1;
    P1SEL2 |= BIT1;
    //P1.2 TXD
    P1SEL |= BIT2 ;
    P1SEL2 |= BIT2;

    //UART
    //enable software reset
    UCA0CTL1 = UCSWRST;

    //UART設定
    UCA0CTL1 |= UCSSEL_2; // SMCLK
    UCA0BR0 = 104; // 1MHz 9600
    UCA0BR1 = 0; // 1MHz 9600
    UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
    //clear software reset
    UCA0CTL1 &= ~UCSWRST;

    //SPI
    //enable software reset
    UCB0CTL1 = UCSWRST;
    //SPI設定
    UCB0CTL0 = UCCKPL|UCMODE0|UCMSB|UCMST|UCSYNC;
    //SCK設定
    UCB0CTL1 = UCSSEL1;
    UCB0BR0 = 1;
    UCB0BR1 = 0;
    //clear software reset
    UCB0CTL1 &= ~UCSWRST;

    //BME280
    uint8_t t_sb = 5; //stanby 1000ms
    uint8_t filter = 0; //filter O = off
    uint8_t spi3or4 = 0; //SPI 3wire or 4wire, 0=4wire, 1=3wire
    uint8_t osrs_t = 4; //OverSampling Temperature x4
    uint8_t osrs_p = 4; //OverSampling Pressure x4
    uint8_t osrs_h = 4; //OverSampling Humidity x4
    uint8_t Mode = 3; //Normal mode
    uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | Mode;
    uint8_t config_reg    = (t_sb << 5) | (filter << 2) | spi3or4;
    uint8_t ctrl_hum_reg  = osrs_h;
    writeReg(0xF2,ctrl_hum_reg);
    writeReg(0xF4,ctrl_meas_reg);
    writeReg(0xF5,config_reg);
    readTrim();

    while (1) {
        __bis_SR_register(LPM3_bits+GIE); //LPM3に移行

        volatile double temp_act = 0.0, press_act = 0.0, hum_act = 0.0, altitude_act = 0.0;
        int32_t temp_cal;
        uint32_t press_cal,hum_cal;

        readData();

        temp_cal = calibration_T(temp_raw);
        press_cal = calibration_P(pres_raw);
        hum_cal = calibration_H(hum_raw);
        //temp_act = (double)temp_cal / 100.0;
        //press_act = (double)press_cal / 100.0;
        //hum_act = (double)hum_cal / 1024.0;
        //IM315TXへ送信
        uart_puts("TXDT ");
        uart_putdec(temp_cal);
        uart_putdec(press_cal);
        uart_putdec(hum_cal);
        uart_puts("\r\n");
        //_delay_cycles(1000000);  // 1sec

    }
}

#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer (void)
{
    __bic_SR_register_on_exit(LPM3_bits); // LPMを解除
}


// スタートアップルーチンから呼び出される初期化処理
void __low_level_init(void)
{
    WDTCTL = WDT_ADLY_1000; // WDTのクロック・ソースをACLK、
                            // 1sのインターバルタイマに設定
}

simple_uart.h

/*
 * simple_uart.h
 *
 */

#ifndef SIMPLE_UART_H_
#define SIMPLE_UART_H_

#include <stdint.h>

void uart_putc(char c)
{
    while (!(IFG2&UCA0TXIFG));
    UCA0TXBUF = c;
}

int uart_puts(const char *s) {
    int n;
    for (n = 0; *s; s++, n++) {
        uart_putc(*s);
    }
    return n;
}


void uart_putdec(uint32_t value) {
    int         i;          // General purpose counter.
    uint32_t    m;          // Mask for decimal number parser.
    char        nbuf[12];   // Buffer to store the decimal number.

    // Maximum power of 10 number in 32-bit.
    m = 1000000000;
    // Find the first masking number.
    while ((m > 1) && (value < m)) {
        m /= 10;
    }
    // Parse the value into a decimal number.
    i = 0;
    while (m >= 1) {
        nbuf[i++] = '0' + (value / m);
        value %= m;
        m /= 10;
    }
    nbuf[i++] = 0;      // End of string.
    uart_puts(nbuf);    // Put the decimal number.
}




#endif /* SIMPLE_UART_H_ */

bme280.h

/*
 * bme280.h
 *
 */

#ifndef BME280_H_
#define BME280_H_

#include <stdint.h>

uint16_t read16bit(uint8_t reg);
uint8_t read8bit(uint8_t reg);
void SpiWrite(uint8_t data);
uint8_t SpiRead();

uint32_t hum_raw, temp_raw, pres_raw;
 int32_t t_fine;

uint16_t dig_T1;
 int16_t dig_T2;
 int16_t dig_T3;

uint16_t dig_P1;
 int16_t dig_P2;
 int16_t dig_P3;
 int16_t dig_P4;
 int16_t dig_P5;
 int16_t dig_P6;
 int16_t dig_P7;
 int16_t dig_P8;
 int16_t dig_P9;

uint8_t  dig_H1;
 int16_t dig_H2;
uint8_t  dig_H3;
 int16_t dig_H4;
 int16_t dig_H5;
 int8_t  dig_H6;

 double SeaLevelPressure_hPa = 1013.25; //標準は1013.25


//*****************初期値読み込み**************************************
void readTrim(void) {
    dig_T1 = read16bit(0x88);
    dig_T2 = (int16_t)read16bit(0x8A);
    dig_T3 = (int16_t)read16bit(0x8C);

    dig_P1 = read16bit(0x8E);
    dig_P2 = (int16_t)read16bit(0x90);
    dig_P3 = (int16_t)read16bit(0x92);
    dig_P4 = (int16_t)read16bit(0x94);
    dig_P5 = (int16_t)read16bit(0x96);
    dig_P6 = (int16_t)read16bit(0x98);
    dig_P7 = (int16_t)read16bit(0x9A);
    dig_P8 = (int16_t)read16bit(0x9C);
    dig_P9 = (int16_t)read16bit(0x9E);

    dig_H1 = read8bit(0xA1);
    dig_H2 = (int16_t)read16bit(0xE1);
    dig_H3 = read8bit(0xE3);
    dig_H4 = (int16_t)((read8bit(0xE4) << 4) | (read8bit(0xE5) & 0x0F));
    dig_H5 = (int16_t)((read8bit(0xE6) << 4) | (read8bit(0xE5) >> 4));
    dig_H6 = (int8_t)read8bit(0xE7);
}
//***************BME280へ初期レジスタ書き込み関数****************************
void writeReg(uint8_t reg_address, uint8_t data) {
  P1OUT &= ~BIT3;
  SpiWrite(reg_address & 0x7F); // write, bit 7 low
  SpiWrite(data);
  P1OUT |= BIT3;
}
//***************BME280からの温度、湿度、気圧データ読み込み関数****************************
void readData() {

  uint32_t data[8];
  uint8_t i;

  P1OUT &= ~BIT3;
  SpiWrite(0xF7 | 0x80); //0xF7 pressure msb read, bit 7 high
  for(i=0; i<8; i++){
    data[i] = SpiRead();
  }
  P1OUT |= BIT3;
  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); //0xF7, msb+lsb+xlsb=19bit
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); //0xFA, msb+lsb+xlsb=19bit
  hum_raw  = (data[6] << 8) | data[7];  //0xFD, msb+lsb=19bit(16:0)
}
//***************温度キャリブレーション関数****************************
int32_t calibration_T(int32_t adc_T) {
    int32_t var1, var2, T;
    var1 = ((((adc_T >> 3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11;
    var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;

    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T;
}
//***************気圧キャリブレーション関数****************************
uint32_t calibration_P(int32_t adc_P) {
    int32_t var1, var2;
    uint32_t P;
    var1 = (((int32_t)t_fine)>>1) - (int32_t)64000;
    var2 = (((var1>>2) * (var1>>2)) >> 11) * ((int32_t)dig_P6);
    var2 = var2 + ((var1*((int32_t)dig_P5))<<1);
    var2 = (var2>>2)+(((int32_t)dig_P4)<<16);
    var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((int32_t)dig_P2) * var1)>>1))>>18;
    var1 = ((((32768+var1))*((int32_t)dig_P1))>>15);
    if (var1 == 0) {
        return 0;
    }
    P = (((uint32_t)(((int32_t)1048576)-adc_P)-(var2>>12)))*3125;
    if(P<0x80000000) {
       P = (P << 1) / ((uint32_t) var1);
    }else{
        P = (P / (uint32_t)var1) * 2;
    }
    var1 = (((int32_t)dig_P9) * ((int32_t)(((P>>3) * (P>>3))>>13)))>>12;
    var2 = (((int32_t)(P>>2)) * ((int32_t)dig_P8))>>13;
    P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4));
    return P;
}
//***************湿度キャリブレーション関数****************************
uint32_t calibration_H(int32_t adc_H) {
    int32_t v_x1;

    v_x1 = (t_fine - ((int32_t)76800));
    v_x1 = (((((adc_H << 14) -(((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1)) +
              ((int32_t)16384)) >> 15) * (((((((v_x1 * ((int32_t)dig_H6)) >> 10) *
              (((v_x1 * ((int32_t)dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + ((int32_t)2097152)) *
              ((int32_t) dig_H2) + 8192) >> 14));
   v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
   v_x1 = (v_x1 < 0 ? 0 : v_x1);
   v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
   return (uint32_t)(v_x1 >> 12);
}
//***************標高計算関数****************************************************
//double ReadAltitude(double SeaLevel_Pres, double pressure) {
//  double altitude = 44330.0 * (1.0 - pow(pressure / SeaLevel_Pres, (1.0/5.255)));
//  return altitude;
//}
//***************BME280から16bitデータ読み込み関数****************************
uint16_t read16bit(uint8_t reg) {
  uint16_t d1, d2;
  uint16_t data;
  P1OUT &= ~BIT3;
  SpiWrite(reg | 0x80); // read, bit 7 high
  d1 = SpiRead();
  d2 = SpiRead();
  data = (d2 << 8) | d1;
  P1OUT |= BIT3;
  return data;
}
//***************BME280から8bitデータ読み込み関数****************************
uint8_t read8bit(uint8_t reg) {
  uint8_t data;
  P1OUT &= ~BIT3;
  SpiWrite(reg | 0x80); // read, bit 7 high
  data = SpiRead();
  P1OUT |= BIT3;
  return data;
}
//***************BME280へSPI信号データ送信関数****************************
void SpiWrite(uint8_t data) {
    volatile char hoge;
    while (!(IFG2 & UCB0TXIFG));
    UCB0TXBUF = data;
    while (!(IFG2 & UCB0RXIFG));
    hoge = UCB0RXBUF;
}
//***************BME280からのSPI信号データ読み込み関数****************************
uint8_t SpiRead() {
  uint8_t r_data = 0;
  UCB0TXBUF = 0x00;
  while (!(IFG2 & UCB0RXIFG));
  r_data = UCB0RXBUF;
  return r_data;
}

#endif /* BME280_H_ */

電圧・電流測定

作成後、電圧をどこまで下げても動作するかとその際の電流値を測定した。
下の様に安定化電源で電源供給してDMMで電流測定している。 f:id:ginnyu-tei:20170801234005j:plain:w400
電流値は送信中とスリープ中で全く異なる値になるのでしばらく動かしてMaxを確認する。

電圧 Imax Imin
3.3V 957uA 541uA
3.0V 920uA 531uA
2.5V 865uA 516uA
2.0V 820uA 502uA
1.8V 799uA 496uA

1.8Vまでは起動・送信が問題なかったが1.8Vを下回ると不安定になった。
これで動作確認もできたので次回は長時間のロギングをやってみる。

参考にしたサイト

BME280 – スイッチサイエンス
MSP430 Tutorial and Resources · Argenox Technologies
MSP430班(2006/05/02) ウォッチドッグタイマ2