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?

C++ の勉強 ② cout つづき

Posted at

 前回、coutの利用を勉強しました。最後で、気圧センサLPS22HBの気圧を表示しましたが、それをまず、関数にして読みやすくします。coutの利用に変化はありません。

関数化を図る

 wakeupは一度きりなので、init()関数にし、気圧データを読み取るreadData()を前のプログラムから分離しました。

ex004.cpp
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>

#define lps22hbAdr 0x5d

int init();
float readData();
float press;
int fd;

int main(){
    std::cout << "\nstart LPS22HB data read \n";
    init();
    press = readData();
    std::cout << "press is "  << press << "hPa\n";

    return 0;
}

int init(){
    fd = open("/dev/i2c-1", O_RDWR);

    struct i2c_msg msg[1];  // /usr/include/linux/i2c.h
    struct i2c_rdwr_ioctl_data packets;  // /usr/include/linux/i2c-dev.h
    unsigned char data[2];

    // wake up
    msg[0].addr = lps22hbAdr;  // address
    msg[0].flags = 0;  // read、writeやアドレス長の指定
    msg[0].len = 2;    // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0x10;  // レジスタアドレス
    data[1] = 0x10;  // 書き込み値;5Hz
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int ret = ioctl(fd, I2C_RDWR, &packets);
    std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
              << std::hex << (int)data[0] << " - 0x"
              << std::hex << (int)data[1] << "\n";
    return 0;
}

float readData(){
    // read press data
    struct i2c_msg msgP[4];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data1[4], data2, data3, data4;

    msgP[0].addr = lps22hbAdr;
    msgP[0].flags = 0;        // 最初はアドレスを書き込み
    msgP[0].len = 1;
    msgP[0].buf = data1;

    data1[0] = 0x8a;          // 0x28 | 0x80 MSB data
    msgP[1].addr = lps22hbAdr;
    msgP[1].flags = I2C_M_RD; // リード時に設定
    msgP[1].len = 1;          // データを1byte読み出し
    msgP[1].buf = &data2;     // data2に読み出しデータが入る
    data1[0] = 0xa9;          // mid
    msgP[2].addr = lps22hbAdr;
    msgP[2].flags = 1;
    msgP[2].len = 1;          // データを1byte読み出し
    msgP[2].buf = &data3;     // data3に読み出しデータが入る
    data1[0] = 0xaa;          // LSB
    msgP[3].addr = lps22hbAdr;
    msgP[3].flags = 1;
    msgP[3].len = 1;          // データを1byte読み出し
    msgP[3].buf = &data4;     // data4に読み出しデータが入る

    packetsP.msgs = msgP;
    packetsP.nmsgs = 4;       // アドレス書き込みとデータ読み出しでmsgは4
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

    return press = std::stof( std::to_string((data2 << 16) | (data3 <<8) | (data4) )) / 4096;
}

温度も測定する

 気圧センサLPS22HBの気圧値は、温度に影響を受けるので、MEMSデバイス自体が温度を測定して補正に利用します。ついでに、温度も外部へ出力されます。BME280などでは、気温の温度が出力されますが、このLPS22HBは、デバイスの温度が出力されるようで、気温より数度高めになります。

 最初、

tupleを使って、気圧と温度の測定値を戻してもらう関数readData()を書いたのですが、bme280の時と違って、mainの後に関数を書いたので、プロトタイプ関数の宣言を最初にしないといけません。が、エラーが出て、対処方法がわかりませんし、検索しても誰も質問していません。
なので、気圧と温度は別々の関数にして、それぞれ、測定値を戻してもらうように書きました。

ex005.cpp
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>

#define lps22hbAdr 0x5d
float press;
float temp;
int init();
float read_pressData();
float read_tempData();
int fd;

int main(){
    std::cout << "\nstart LPS22HB data read \n";
    init();
    std::cout << "press is "  << read_pressData() << "hPa\n";
    std::cout << "temp is  "  << read_tempData() << "`C\n";
    return 0;
}

int init(){
    fd = open("/dev/i2c-1", O_RDWR);

    struct i2c_msg msg[1];  // /usr/include/linux/i2c.h
    struct i2c_rdwr_ioctl_data packets;  // /usr/include/linux/i2c-dev.h
    unsigned char data[2];

    // wake up
    msg[0].addr = lps22hbAdr;  // address
    msg[0].flags = 0;  // read、writeやアドレス長の指定
    msg[0].len = 2;    // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0x10;  // レジスタアドレス
    data[1] = 0x10;  // 書き込み値;5Hz
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int ret = ioctl(fd, I2C_RDWR, &packets);
    std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
              << std::hex << (int)data[0] << " - 0x"
              << std::hex << (int)data[1] << "\n";
    return 0;
}

float read_pressData(){
    // read press data
    struct i2c_msg msgP[4];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data1[4], data2, data3, data4;

    msgP[0].addr = lps22hbAdr;
    msgP[0].flags = 0;        // 最初はアドレスを書き込み
    msgP[0].len = 1;
    msgP[0].buf = data1;

    data1[0] = 0xa8;          // 0x28 | 0x80 MSB data
    msgP[1].addr = lps22hbAdr;
    msgP[1].flags = I2C_M_RD; // リード時に設定
    msgP[1].len = 1;          // データを1byte読み出し
    msgP[1].buf = &data2;     // data2に読み出しデータが入る
    data1[0] = 0xa9;          // mid
    msgP[2].addr = lps22hbAdr;
    msgP[2].flags = 1;
    msgP[2].len = 1;          // データを1byte読み出し
    msgP[2].buf = &data3;     // data3に読み出しデータが入る
    data1[0] = 0xaa;          // LSB
    msgP[3].addr = lps22hbAdr;
    msgP[3].flags = 1;
    msgP[3].len = 1;          // データを1byte読み出し
    msgP[3].buf = &data4;     // data4に読み出しデータが入る

    packetsP.msgs = msgP;
    packetsP.nmsgs = 4;       // アドレス書き込みとデータ読み出しでmsgは4
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

    press = std::stof( std::to_string((data2 << 16) | (data3 <<8) | (data4) )) / 4096;
    return press;
}

float read_tempData(){
    // read temp data
    struct i2c_msg msgT[3];
    struct i2c_rdwr_ioctl_data packetsT;
    unsigned char data1[2], data2, data3;

    msgT[0].addr = lps22hbAdr;
    msgT[0].flags = 0;        // 最初はアドレスを書き込み
    msgT[0].len = 1;
    msgT[0].buf = data1;

    data1[0] = 0xab;          // 0x2b | 0x80 MSB data
    msgT[1].addr =lps22hbAdr;
    msgT[1].flags = I2C_M_RD; // リード時に設定
    msgT[1].len = 1;          // データを1byte読み出し
    msgT[1].buf = &data2;     // data2に読み出しデータが入る
    data1[0] = 0xac;          // LSB
    msgT[2].addr = lps22hbAdr;
    msgT[2].flags = 1;
    msgT[2].len = 1;          // データを1byte読み出し
    msgT[2].buf = &data3;     // data3に読み出しデータが入る

    packetsT.msgs = msgT;
    packetsT.nmsgs = 3;       // アドレス書き込みとデータ読み出しでmsgは4
    int retT = ioctl(fd, I2C_RDWR, &packetsT);

    int tempNative = (data2 <<8) | data3;
    int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
    temp =  std::stof( std::to_string(temp2scomp)) / 100;

    return temp;
}

 実行します。

$ ./a.out

start LPS22HB data read

send: 0x5d - 0x10 - 0x10
press is 1008.7hPa
temp is  28.57`C

coutの勉強をしてるんです その1

 ということで、2の補数計算をしているときの、ビットの表示をしてみることにしました。
 気圧と温度のどちらも読み出したデータは2の補数形式です。でも、気圧は自然界ではマイナスの値をとらないので、温度のほうだけ対処しています。
 温度のデータは16ビットです。2の補数は、最上位ビットを見ます。'1'だったら、マイナスの符号をつけます。
 なお、データシートに従って、読み出した値を100で割ると摂氏表示の温度が得られます。

c.cpp
    int tempNative = (data2 <<8) | data3;
    int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
    temp =  std::stof( std::to_string(temp2scomp)) / 100;

 テスト用ソースを作ります。

ex006.cpp
#include <iostream>

int main(){

    int tempNative = -2345;
    int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
    float temp =  std::stof( std::to_string(temp2scomp)) / 100;
    std::cout << "temp is " <<temp << "\n";

    return 0;
}

 実行します。

$ g++ ex006.cpp
$ ./a.out
temp is -23.45

 -2345のマイナスをとって2345に変更して実行します。

$ g++ ex006.cpp
$ ./a.out
temp is 23.45

 途中経過を表示します。

ex006.cpp
#include <iostream>
#include <iomanip>
//#include <iosfwd>
#include <bitset>

int main(){

    int tempNative = -2345;
    int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
    float temp =  std::stof( std::to_string(temp2scomp)) / 100;

    std::cout << "tempNative is " << std::dec << tempNative
              << " 0b" << std::bitset<16>(tempNative)
              << " 0b" << ((std::bitset<16>(tempNative)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
              << "\n";

    std::cout << "temp2scomp is " << std::dec << temp2scomp
              << " 0b" << std::bitset<16>(temp2scomp)
              << " 0b" << ((std::bitset<16>(temp2scomp)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
              << "\n";


    std::cout << "temp is " << temp << "\n";
    std::cout << "temp is " << std::fixed << std::setprecision(1) << temp << "`C\n";
    return 0;
}

 実行します。

$ g++ ex006.cpp
$ ./a.out
tempNative is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp2scomp is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp is -23.45
temp is -23.5`C

 テスト・データを正の数値にして実行します。

$ ./a.out
tempNative is 2345 0b0000100100101001 0b0000 1001 0010 1001
temp2scomp is 2345 0b0000100100101001 0b0000 1001 0010 1001
temp is 23.45
temp is 23.5`C

coutの勉強をしてるんです その2

 気圧の出力がpress is 1008.7hPaのように、小数点第1位まで表示されていますが、特に桁数の指定はしていません。台風の時は1000hPaを切りますが、テレビでは、温度や湿度は小数点以下の表示をしているのに、気圧は整数値です。
 台風の気圧は直接測れないので、衛星写真を撮って、目視で大きさ(直径)を測って気圧を導き出しています。
 温度計は、どこかの団体に手数料を払って、精度?の保証をしてもらっているんですけどね。
 任意の小数点の桁数表示にチャレンジします。
 というか、前のプログラムで実装しました。
 setprecision(1)のかっこの中の数字が、小数点第何位まで表示するかを指定しています。

ex006.cpp
#include <iostream>
#include <iomanip>
//#include <iosfwd>
#include <bitset>

int main(){

    int tempNative = -2345;
    int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
    float temp =  std::stof( std::to_string(temp2scomp)) / 100;

    std::cout << "tempNative is " << std::dec << tempNative
              << " 0b" << std::bitset<16>(tempNative)
              << " 0b" << ((std::bitset<16>(tempNative)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
              << "\n";

    std::cout << "temp2scomp is " << std::dec << temp2scomp
              << " 0b" << std::bitset<16>(temp2scomp)
              << " 0b" << ((std::bitset<16>(temp2scomp)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
              << "\n";


    std::cout << "temp is " << temp << "\n";
    std::cout << "temp is " << std::fixed << std::setprecision(1) << temp << "`C\n";
    std::cout << "temp is " << std::setprecision(1) << temp << "`C\n";
    std::cout << "temp is " << std::fixed << std::setprecision(0) << temp << "`C\n";
    std::cout << "temp is " << std::setprecision(0) << temp << "`C\n";
    return 0;
}

 std::fixed を使わない例もあるようです。実行しました。

$ g++ ex006.cpp
$ ./a.out
tempNative is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp2scomp is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp is -23.45
temp is -23.5`C
temp is -23.5`C
temp is -23`C
temp is -23`C

 どうも、丸められているように見えますが、解説にはそのようなことは書かれていませんでした。
 <ios>にあったbitsetが、<bitset>にいつどういう理由で移動したかも見つけられていません。


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?