今から始める電子工作 ⑲ I2Cバス その2-3 温湿度センサSHT45、Wireライブラリ、CRC
前回、Wireライブラリを使って、温湿度センサSHT45のデータを読み出しました。
●シリアル通信の特徴
古典的なシリアル通信であるRS-232C(最近ではUARTと呼ばれる)では、振幅は±15Vあり、一つのデータは5~8ビットで、多くは7もしくは8あり、パリティ・ビットが追加されていることもあります。実用上は10m程度の通信に適します。データ自体はLSB(最後のビット)から送られてきます。
今回利用しているI2CではデータはMSB(最上位ビット)から送られてきます。センサのデータは16ビットのものが多いので、複数バイトが連続して送られてきますが、その順番が、上位バイトからの場合が一般的です。しかし、下位バイトから送られてくる場合があるかもしれません したがって、データシートにMSBと書かれていた場合、最上位ビットもしくは最上位バイトの両方の意味があるので、慎重に読まないといけません。
また、BLEなどでは、バイナリ・データを最下位のバイトから最上位バイトに向かって順に並べる方式をリトル・エンディアン、逆順をビッグ・エンディアンと呼びます。センサなどのデータシートでは、あまり出てきませんが。
●CRCとは
シリアル通信では、信号が流れる経路が長いため、途中でノイズが乗りデータが化けることが頻繁に起こります。データを送るほうは、送るデータでCRCデータを計算して、データと一緒に送ります。受け取った側は受信したデータからCRCを計算して、CRCが一致しているかどうかを確認します。
CRCが一致しなければ、データがおかしくなっていると判断して、再送要求を出すなどの処理を行います。
ウキペディア;巡回冗長検査
●CRC-8-Dallas/Maxim
そもそもCRCはたくさんの種類があります。CRC-8にも数種類あります。ここで扱っているのは、ダラスセミコンダクターが作り、マキシムに買収されて、今はアナログデバイセズに買収されたCRC-8-Dallas/Maximの多項式x8 + x5 + x4 + 1 を使っています。
SHT45の条件は、次のように書かれています。
- Polynomial ; 0x31 (x8 + x5 + x4 + 1)
- Initialization ; 0xFF
- Reflect input ; False
- Reflect output ; False
- Final XOR ; 0x00
Initialization ; 0xFFは、CRCの計算結果を格納する変数の初期値で、Final XOR ; 0x00は何もしないですね。Initializationは、入ってきた処理の最初に、Finalは計算の最後に行います。
Reflect input とReflect output は、ビットの並びを逆順にするかどうかです。1-WireやUARTはLSBからデータが流れてきますが、i2cはMSBからです。
CRCの計算を検証できるWebサイト;https://www.sunshine2k.de/coding/javascript/crc/crc_js.html
●テーブルを使ったCRCの計算
テスト用のデータは0xbeefで、結果は0x92です。
void setup() {
Serial.begin(9600);
delay(1000);
}
void loop() {
uint8_t data[2] = {0xbe, 0xef};
Serial.println( crc8(data), HEX);
delay(10000);
}
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 crc8(const uint8_t* data){
uint8_t crc = 0xff;
crc = table[reflect(data[0]) ^ crc];
crc = table[reflect(data[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;
}
●SHT45でCRC計算を含める
前回、読みだしたSHT45のデータでCRCを無視しましたが、上記のCRC計算を組み入れました。
#include <Wire.h>
const uint8_t SHT45_address = 0x44;
uint8_t readbuffer[6];
float temperature, humidity;
void setup() {
Wire1.begin();
Serial.begin(9600);
delay(1000);
Serial.println("start ");
}
void loop() {
Wire1.beginTransmission(SHT45_address);
Wire1.write(0xfd);
Wire1.endTransmission();
delay(10);
Wire1.requestFrom(SHT45_address, 6);
readbuffer[0] = Wire1.read();
readbuffer[1] = Wire1.read();
readbuffer[2] = Wire1.read(); // CRC
readbuffer[3] = Wire1.read();
readbuffer[4] = Wire1.read();
readbuffer[5] = Wire1.read(); // CRC
Serial.println(" ");
if (readbuffer[2] == crc8(readbuffer)) {
int temp = readbuffer[0] * 256 + readbuffer[1];
temperature = -45 + 175 * temp / 65535.0;
}
if (readbuffer[5] == crc8(readbuffer+3)) {
int humi = readbuffer[3] * 256 + readbuffer[4];
humidity = -6 + 125 * humi / 65535.0;
}
Serial.println(" temperature= "+String(temperature,1)+"'C");
Serial.println(" RH%= "+String(humidity,0));
delay(5000);
}
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 crc8(const uint8_t* readbuffer){
uint8_t crc = 0xff;
crc = table[reflect(readbuffer[0]) ^ crc];
crc = table[reflect(readbuffer[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;
}