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++ の勉強 ④ 関数へ配列データを渡す

Last updated at Posted at 2024-09-08

 関数へ配列のデータを渡すというのをCRC-8のプログラムで勉強します。

CRC-8

過去記事のinclude文などを修正します。

ex302.cpp
#include <iostream>
#include <cstdint>
#include <bitset>

uint8_t CalcCrc(uint8_t crcdata[]) {

    std::cout << "in function \n new data 0x" << std::hex << (int)crcdata[0] << std::hex << (int)crcdata[1]
              << " 0b" << ((std::bitset<8>(*crcdata)).to_string()).insert(4, " ") << "\n";

    uint8_t crc = 0xFF;
    for(int i = 0; i < 2; i++){
        crc ^= crcdata[i];
        for(uint8_t bit = 8; bit > 0; --bit) {
            if(crc & 0x80) {
                crc = (crc << 1) ^ 0x31u;
            } else {
                crc = (crc << 1);
            }
        }
    }
    std::cout << "out function \n";
    return (int)crc;
}

int main() {
    std::cout << "start \n";
    uint8_t data[2] = {0xbe, 0xef};
    uint8_t crc8 = CalcCrc(data);
    std::cout << "crc-8 0x" << std::hex << (int)crc8 << "\n";

    return 0;
}

 実行します。

$ g++ ex302.cpp
$ ./a.out
start
in function
 new data 0xbeef 0b1011 1110
out function
crc-8 0x92

 関数 CalcCrc(uint8_t crcdata[])へは、配列を渡しています。と思ったのですが、なんだか変な記述ですね。
 調べてみると、配列はポインタで渡すのだそうです。
  CalcCrc(uint8_t *crcdata)に変更します。プログラム名はex303.cppに変更しました。実行します。

$ g++ ex303.cpp
$ ./a.out
start
in function
 new data 0xbeef 0b1011 1110
out function
crc-8 0x92

 問題なく動きますね。
 検索すると、

C言語では、配列そのものを引数として渡せないので、ポインタを引数として渡します。

 つまり、普通の変数の値渡しのような記述ができないということなんですね。
で、最初のプログラムの書き方CalcCrc(uint8_t crcdata[])は、

配列の場合は参照渡し

配列の参照(ポインタ)を渡します

のような表現を見ます。えー、参照渡しなんだ。&出てこない!

 配列をポインタ渡しするとき、配列の長さも一緒に、という表現を見ますが、ここでは2バイトであることが明白なので省略しています。

 ついでに、CalcCrc(uint8_t crcdata[2])を試しましたが、問題なく動きました。

ポインタ渡しと違って配列の先頭アドレスを渡す

らしいです。
 頭の中がぐちゃぐかになったので、配列ではすなおに CalcCrc(uint8_t *crcdata)のポインタ渡しをすることにしますね。

 今更ですが、uint8_tstd::uint8_tと書かなくてもいいのかな。

CRC-8-Dallas/Maxim

 そもそもCRCはたくさんの種類があります。CRC-8にも数種類あります。ここで扱っているのは、ダラスセミコンダクターが作り、マキシムに買収されて、今はアナログデバイセズに買収されたCRC-8-Dallas/Maximの多項式x8 + x5 + x4 + 1 を使っています。
 最初のプログラムは、

  • Polynomial ; 0x31 (x8 + x5 + x4 + 1)
  • Initialization ; 0xFF
  • Reflect input ; False
  • Reflect output ; False
  • Final XOR ; 0x00
    という条件で動作しています。i2cのデバイスを想定しているので、Reflect input ; Falseですが、1wireだと逆順だったような。。。

テーブルを使う計算方法

 テーブル・ルックアップでCRC-8を計算する方法が解説されています。1wireの事例です。
 
https://www.analog.com/jp/resources/technical-articles/understanding-and-using-cyclic-redundancy-checks-with-maxim-1wire-and-ibutton-products.html

この方法でプログラムにチャレンジします。
reflect()は、8ビット・データのビットの並びを逆順にします。
 データの検証は、このサイトで行えます。

ex306.cpp
#include <iostream>
#include <cstdint>
#include <bitset>

uint8_t CalcCrc(const uint8_t * crcdata);
uint8_t reflect(uint8_t c);

int main() {
    std::cout << "start \n";
    uint8_t data[2] = {0xbe, 0xef};
    uint8_t crc8 = CalcCrc(data);
    std::cout << "crc-8 0x" << std::hex << (int)crc8 << "\n";

    return 0;
}

uint8_t table[256] ={
   0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,35, 125, 159,193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

uint8_t CalcCrc(const uint8_t * crcdata){

    std::cout << "in function \n new data 0x" << std::hex << (int)crcdata[0] << std::hex << (int)crcdata[1]
              << " 0b" << ((std::bitset<8>(*crcdata)).to_string()).insert(4, " ") << "\n";

    uint8_t crc = 0xff;
    crc = table[reflect(crcdata[0]) ^ crc];
    crc = table[reflect(crcdata[1]) ^ crc];

    return reflect(crc);
}

uint8_t reflect( uint8_t c ){
  c = ((c & 0x55) <<  1) | (c >>  1) & 0x55;
  c = ((c & 0x33) <<  2) | (c >>  2) & 0x33;
  c = ((c & 0x0f) <<  4) | (c >>  4) & 0x0f;
  return c;
}

実行します。

$ g++ ex306.cpp
$ ./a.out
start
in function
 new data 0xbeef 0b1011 1110
crc-8 0x92

湿度センサSHT45をつなぐ

 データシートです。SHT40/41/45の読み書きは同じタイミングで行えるので、sht4xという表現が使われます。

sht45-.png

CRCをチェックしたプログラム

 最初に、softReset()でリセットをかけます。init()で高解像モードに設定します。
 SHT4Xは、レジスタの指定なしで、6バイトのデータを読み出します。2バイトが温度データ、3バイト目がそのcrc、その次の2バイトが湿度データで、最後がそのcrcです。

ex309.cpp
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#include <iomanip>
#include <cstdint>
#include <bitset>
#include <tuple>

#define sht45Adr 0x44
float temp;
float humi;
int fd;
int softReset();
int init();

std::tuple<float, float> readSht45_data();
uint8_t CalcCrc(const uint8_t * crcdata);
uint8_t reflect(uint8_t c);

int main() {

    std::cout << "start SHT45 measurement\n";
    fd = open("/dev/i2c-1", O_RDWR);
    softReset();
    usleep(1000);
    init();
    usleep(10000);
    auto [temp, humi] = readSht45_data();

    std::cout << "\ntemp is "  << std::fixed << std::setprecision(1) << temp << "`C\n";
    std::cout << "humi is "  << std::fixed << std::setprecision(0) << humi << "%\n";

    return 0;
}

int softReset(){
    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[1];

    // set measure mode
    msg[0].addr = sht45Adr;  // address
    msg[0].flags = 0;   // read、writeやアドレス長の指定
    msg[0].len = 1;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0x94;     // reset
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int retreset = ioctl(fd, I2C_RDWR, &packets);

    std::cout << "\nsend: 0x" << std::hex << (int)msg[0].addr << " - 0x"
              << std::hex << (int)data[0] << "\n";
    return 0;
}

int init(){
    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[1];

    // set measure mode
    msg[0].addr = sht45Adr;  // address
    msg[0].flags = 0;   // read、writeやアドレス長の指定
    msg[0].len = 1;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0xfd;     // high precision
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int retinit = ioctl(fd, I2C_RDWR, &packets);

    std::cout << "\nsend: 0x" << std::hex << (int)msg[0].addr << " - 0x"
              << std::hex << (int)data[0] << "\n";
    return 0;
}

std::tuple<float, float> readSht45_data(){
    // read 6 data
    struct i2c_msg msgP[1];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data1[1], data2[6];

    msgP[0].addr =  sht45Adr;
    msgP[0].flags = I2C_M_RD;   // アドレスを書き込み
    msgP[0].len = 6;
    msgP[0].buf = &data2[0];

    packetsP.msgs = msgP;
    packetsP.nmsgs = 1;        // データ読み出しでmsgは1
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

    std::cout << "read all data " << (int)data2[0] << " " << (int)data2[1] << " " << (int)data2[2] << " " << (int)data2[3] << " " << (int)data2[4] << " " << (int)data2[5] << "\n";

    int tempCrc = CalcCrc(data2);
    if (data2[2] == tempCrc) {

        std::cout << " temp crc is 0x" << std::hex << (int)data2[2] << " calcedCRC is 0x" << std::hex << tempCrc << "\n";

        int Stemp = data2[0] << 8 | data2[1];
        temp = -45 + (float)Stemp * 175.0 / 65535.0;
    }
    int humiCrc = CalcCrc(data2+3);
    if (data2[5] == humiCrc) {

        std::cout << " humi crc is 0x" << std::hex << (int)data2[5] << " calcedCRC is 0x" << std::hex << humiCrc << "\n";

        int SRH = data2[3] << 8 | data2[4];
        humi = 100.0 * (float)SRH / 65535.0;
    }

    return  std::make_tuple(temp, humi);
}

uint8_t table[256] ={
   0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,35, 125, 159,193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

uint8_t CalcCrc(const uint8_t * crcdata){

//    std::cout << "in function \n new data 0x" << std::hex << (int)crcdata[0] << std::hex << (int)crcdata[1]
//              << " 0b" << ((std::bitset<8>(*crcdata)).to_string()).insert(4, " ") << "\n";

    uint8_t crc = 0xff;
    crc = table[reflect(crcdata[0]) ^ crc];
    crc = table[reflect(crcdata[1]) ^ crc];

    return reflect(crc);
}

uint8_t reflect( uint8_t c ){
  c = ((c & 0x55) <<  1) | (c >>  1) & 0x55;
  c = ((c & 0x33) <<  2) | (c >>  2) & 0x33;
  c = ((c & 0x0f) <<  4) | (c >>  4) & 0x0f;
  return c;
}

 実行しました。

$ ./a.out
start SHT45 measurement

send: 0x44 - 0x94

send: 0x44 - 0xfd
read all data 6c 88 a3 8d e3 70
 temp crc is 0xa3 calcedCRC is 0xa3
 humi crc is 0x70 calcedCRC is 0x70

temp is 29.2`C
humi is 55%
$ ./a.out
start SHT45 measurement

send: 0x44 - 0x94

send: 0x44 - 0xfd
read all data 6f 14 ca ad 94 c3
 temp crc is 0xca calcedCRC is 0xca
 humi crc is 0xc3 calcedCRC is 0xc3

temp is 30.9`C
humi is 68%

 波形です。

Cpura302.png


<参考資料>

<見つからなかったこと> マルチバイト・リード。レジスタ指定なしリード。
<動かなかったこと> smbusの記述がコンパイル・エラーになる。


  これは、「C++ の勉強 ① cout」です。表示がおかしくなっています。

0
0
8

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?