0
0

C++ の勉強 ① cout

Last updated at Posted at 2024-08-25

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

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

$ 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のプログラムを作る

 ここに書かれた手順を実装します。
 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

.formatは?

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

 右寄せとかが指定できるようです。別途勉強します。

(※)書いている途中で投稿してしまっていたようです。最終は9/2です。

0
0
4

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