C++ の勉強 ② cout　つづき

　前回、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は、デバイスの温度が出力されるようで、気温より数度高めになります。


 　最初、

  https://qiita.com/jamjam/items/edb799ec88bdfba29c4c

  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)``のかっこの中の数字が、小数点第何位まで表示するかを指定しています。

https://cpprefjp.github.io/reference/iomanip/setprecision.html
   
```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>にいつどういう理由で移動したかも見つけられていません。
  
------

 https://qiita.com/jamjam/items/e7a734f85f4554ba6273