関数へ配列のデータを渡すというのをCRC-8のプログラムで勉強します。
CRC-8
過去記事のinclude文などを修正します。
#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_t
はstd::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ビット・データのビットの並びを逆順にします。
データの検証は、このサイトで行えます。

#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という表現が使われます。
CRCをチェックしたプログラム
最初に、softReset()でリセットをかけます。init()で高解像モードに設定します。
SHT4Xは、レジスタの指定なしで、6バイトのデータを読み出します。2バイトが温度データ、3バイト目がそのcrc、その次の2バイトが湿度データで、最後がそのcrcです。
#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%
波形です。
<参考資料>
<見つからなかったこと> マルチバイト・リード。レジスタ指定なしリード。
<動かなかったこと> smbusの記述がコンパイル・エラーになる。
これは、「C++ の勉強 ① cout」です。表示がおかしくなっています。