C++ の勉強 ① cout

　書籍を購入したので、勉強をします。このサイト、メモは書くなというお達しが出ていますが、うーん、メモです。

> C++実践プログラミング 単行本 – 2003/9/1
スティーブ オウアルライン (著), Steve Oualline (原名), 望月 康司 (翻訳), クイープ (翻訳)

### 環境

- Raspberry Pi 5 8GB
- 追加ボード；NVMe Base for Raspberry Pi 5 (NVMe Base by Pimoroni)
- Crucial クルーシャル P2シリーズ 500GB 3D NAND NVMe PCIe M.2 SSD CT500P2SSD8
- Ubuntu Desktop ***24.04LTS***(64-bit)


 ### 準備
 　Windows10のコマンドプロンプトからsshで入ります。
```
ssh yoshi.local
$ mkdir book
$ cd book
$ nano ex001.cpp
```
### coutを使ってみたが
　8ビットの二つのデータを16ビットに合成しています。センサの読み出してはよく使われます。
 　16進とバイナリ表示を併用しています。

```ex001.cpp
#include <iostream>
#include <bitset>

int main(){
    int high_byte = 0xab;
    int low_byte = 0xef;
    int data = (high_byte << 8) | low_byte;

    std::cout << "high_bye : " << high_byte << " 0x" << std::hex << high_byte << " 0b" << std::bitset<8>(high_byte) << "\n";
    std::cout << "low_bye  : " << low_byte << " 0x" << std::hex << low_byte << " 0b" << std::bitset<8>(low_byte) << "\n";
    std::cout << "data     : " << data << " 0x" << std::hex << data << " 0b" << std::bitset<16>(data) << "\n";

    return 0;
}
```
実行します。
```
$ g++ ex001.cpp
$ ./a.out
high_bye : 171 0xab 0b10101011
low_bye  : ef 0xef 0b11101111
data     : abef 0xabef 0b1010101111101111
```
　与えるデータを変えてみました。
```
    int high_byte = 0x02;
    int low_byte = 0x02;
```
実行します。
```
$ g++ ex001.cpp
$ ./a.out
high_bye : 2 0x2 0b00000010
low_bye  : 2 0x2 0b00000010
data     : 202 0x202 0b0000001000000010
```

　単に、``std::cout << "high_bye : " << high_byte``の挙動が変です。これが仕様なのかもしれませんけど。16進で与えたデータが、**表示で10進表示に見えたり**、明らかに文字の場合は **16進表示**になっています。
 　なので、
  - 16進表示　``<< " 0x" << std::hex``
  - バイナリ表示　``<< " 0b" << std::bitset<8>(high_byte)``
  を使わないと、かんちがいすることがありそうです😊

  　std::hexやstd::decはありますが、std::binはありません。
   intの長さを指定してみました。

  ```
    int8_t high_byte = 0x02;
    int8_t low_byte = 0x02;
    int16_t data = (high_byte << 8) | low_byte;
```
　実行します。
```
$ g++ ex001.cpp
$ ./a.out
high_bye :  0x 0b00000010
low_bye  :  0x 0b00000010
data     : 202 0x202 0b0000001000000010
```
　std::hexの表示がおかしくなりました。coutはint8を表示できなくて、すごく使いずらいです。

　最後に、次の3行を追加しました。
 ```
    std::cout << sizeof(high_byte) << "\n";
    std::cout << sizeof(low_byte) << "\n";
    std::cout << sizeof(data) << "\n";
```
　実行しました。
```
$ g++ ex001.cpp
$ ./a.out
high_bye :  0x 0b00000010
low_bye  :  0x 0b00000010
data     : 202 0x202 0b0000001000000010
1
1
2
```
　サイズは想定通りですね。


### <<はもともとシフトの演算子


```ex002.cpp
#include <iostream>

int main(){

     std::cout << "test1 " << 0xff+1 << " ok?\n";
     std::cout << "test2 " << 0xff<<1 << " ok?\n";
     std::cout << "test3 " << (0xff<<1) << " ok?\n";
     std::cout << "test4 " << 0xff & 1 << " ok?\n";
     std::cout << "test5 " << (0xff & 1) << " ok?\n";
    return 0;
}
```
　実行すると、エラーが出ます。
```
$ g++ ex002.cpp
ex002.cpp: In function ‘int main()’:
ex002.cpp:8:40: error: invalid operands of types ‘int’ and ‘const char [6]’ to binary ‘operator<<’
    8 |      std::cout << "test4 " << 0xff & 1 << " ok?\n";
      |                                      ~ ^~ ~~~~~~~~
      |                                      |    |
      |                                      int  c

```

　test4 の行をコメントアウトしました。
```
$ g++ ex001.cpp
yoshi@yoshi:~/book/src$ ./a.out
test1 257 ok?
test2 2552 ok?
test3 1020 ok?
test5 2 ok?
```
　実行します。
 ```
$ nano ex002.cpp
yoshi@yoshi:~/book$ g++ ex002.cpp
`yoshi@yoshi:~/book$ ./a.out
test1 256 ok?
test2 2551 ok?
test3 510 ok?
test5 1 ok?
```
　ビット演算は、動作を確認しながら使うのがよさそうです。

### 実際に使ってみる
　i2cバス接続の気圧センサLPS22HBをラズパイにつなぎました。
 
![lps22--.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/89051fa7-2f2f-6343-a5ce-603e6dd9a9d8.png)

 
```
$ ls /dev/i2c*
/dev/i2c-1  /dev/i2c-11  /dev/i2c-12
```
```
$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- 5d -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- UU
```
　BME280はつないでいませんが、デバイス・ドライバを入れたままになっているのでUUと表示が出ています。LPS22HBは0x5dに見つかりました。

　WHO AM Iレジスタ（0x0f）を読み出します。
 ```
 $ i2cget 1 0x5d 0x0f
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-1, chip address 0x5d, data address
0x0f, using read byte data.
Continue? [Y/n] y
0xb1
```
　0xb1はこのデバイでは正しい値です。
　LPS22HBの気圧データは、アドレス0xa8から、24ビット長で入っています。
 　パワーオン後Power downモードになっています。CTRL_REG1 (10h)に0x10を書き込んで、1Hzでデータを変換するようにします。
 　そのあと、3バイトを1バイトずつ読み出します。
```
$ i2cset 1 0x5d 0x10 0x10 b
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will write to device file /dev/i2c-1, chip address 0x5d,
data address 0x10, data 0x10, mode byte.
Continue? [Y/n] y
yoshi@yoshi:~/book$ i2cget 1 0x5d 0xa8 b
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-1, chip address 0x5d, data address
0xa8, using read byte data.
Continue? [Y/n] y
0x8a
yoshi@yoshi:~/book$ i2cget 1 0x5d 0xa9 b
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-1, chip address 0x5d, data address
0xa9, using read byte data.
Continue? [Y/n] y
0xf9
yoshi@yoshi:~/book$ i2cget 1 0x5d 0xaa b
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-1, chip address 0x5d, data address
0xaa, using read byte data.
Continue? [Y/n] y
0x3e
```
　MSBのバイトから入っているので、0x3ef98aが読み出した24ビット・データです。10進に直すと、4127114なので、これを、マニュアルに従って4096で割ります。
 　1007.5hPaです。正しく読めたようです。

 ### ioctlのプログラムを作る

https://www.nds-osk.co.jp/atc/wp-content/uploads/UserlandIO.pdf

　ここに書かれた手順を実装します。
 　for文を使っているのは、オシロスコープで波形を見るためです。

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

int main(){
    std::cout << "\nstart LPS22HB data read \n";
    int 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];
    int ret;

    msg[0].addr = 0x5d; /* addrは16bit幅 */
    msg[0].flags = 0; /* read、writeやアドレス長の指定に利用 */
    msg[0].len = 2; /* bufに指定するdataのサイズ */
    msg[0].buf = data;

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

    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";

    for (int i=0 ; i<100; i++){
        struct i2c_msg msgR[4];
        struct i2c_rdwr_ioctl_data packetsR;
        unsigned char data1[4], data2, data3, data4;

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

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

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

        std::cout << "read data: " << (int)data2 << " - " << (int)data3 << " - " << (int)data4 << "\n";
        float press = std::stof( std::to_string((data2 << 16) | (data3 <<8) | (data4) )) /4096;
        std::cout << "\npress is " << press << "\n";

        sleep(1);
    }
    //close(fd);
        return 0;
}
```


　実行します。
```
$ ./a.out

start LPS22HB data read

send: 0x5d - 0x10 - 0x10
read data: 3f - a - 17

press is 1008.63
read data: 3f - a - 17

press is 1008.63
read data: 3f - a - 17

press is 1008.63
```
　波形です。アドレスを毎回送出するので、3バイトを連続して読み出せていません。
　構造体msg[]の内容がバラエティに富んでいて、メンバの役割をまだ理解できていません。うまく指定ができると、マルチバイト・リードがスマートにできるかもしれません（本来、最初のアドレス送出以降に何度もアドレスが出る必要はない）。
 
![Cpura001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/4ca9f51d-8e55-7725-0d4e-0928a994341e.png)



### .formatは？
　printfではformatが利用できました。書籍には書かれていませんが、\<ios>を使うようです。リファレンスにはC98となっていたので、新しいのかもしれません。調べると、一番古かった。一番新しいのC20らしい。素人の推測は外れるものらしい。

 https://cpprefjp.github.io/reference/ios.html

 　右寄せとかが指定できるようです。別途勉強します。
 
（※）書いている途中で投稿してしまっていたようです。最終は9/2です。