CRC-15-CANを計算するプログラムを書いた

(この記事はQiitaで書いた下記記事と同一です。)

qiita.com

はじめに

仕事でCAN通信のプログラムを書いているんだけど、CRCエラーの表示が出る。 デバッグのために、CRCの変換プログラムが必要になっった。 CANのCRCCRC-15という特殊なフォーマットでネット上に資料が少ない。 ソースや調べたことをまとめておくことにした。

実装例

言語はCを使用。

#include <stdio.h>
#include <stdint.h>

// int to bin
char* int_to_binstr(int bin, int len)
{
int i;
static char buff[32];

for (i = 0; i < len; i++) {
if (bin & (1UL << (len - i - 1))) {
buff[i] = '1';
}
else {
buff[i] = '0';
}
}
buff[len] = '\0';
return buff;
}

uint16_t can_crc_next(uint16_t crc, uint8_t data)
{
uint8_t i;
crc ^= (uint16_t)data << 7;

for (i = 0; i < 8; i++) {
crc <<= 1;
if (crc & 0x8000) {
crc ^= 0xc599;
}
}
return crc & 0x7fff;
}

void cal_crc15(unsigned char* buff, int len) {
uint16_t crc;
crc = 0;

printf("crc15 0x");
for (int i = 0; i < len; i++) {
printf("%02X ", buff[i]);
}

printf("\n-> ");

for (int i = 0; i < len; i++) {
crc = can_crc_next(crc, buff[i]);
}
char binstr[16];
printf("0x%04X(%s)\n", crc, int_to_binstr(binstr, (int)crc, 15));
}

int main()
{
int i;
uint8_t data[] = { 0x00, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
cal_crc15(data, 11);
}

CRC元データについて

CRCの元データはSOFからデータまで。スタッフビットは除く。 バイナリはHEXにする。 SOF側が半端なビットになるようにする。

uint8_t data[] = { 0x00, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }

プログラムの動作

CRCの初期値は0。

crc = 0;

データをHEXごとに取り出して7ビット左シフトしてCRC排他的論理和をとる。

crc ^= (uint16_t)data << 7;

CRCを1ビット左シフト

crc <<= 1;

CRCの最上位ビットが1なら、CRC15の生成多項式x15 + x14 + x10 + x8 + x7 + x4 + x3 + 1(=0xc599)との排他的論理和を取る。

if (crc & 0x8000) {
crc ^= 0xc599;
}

15ビットなので16ビットの最上位ビットを省く。

return crc & 0x7fff;

注意

CANコントローラ(MCP2515)の吐き出した値と一致することは確認できているが、CAN15の仕様と完全に一致しているかは確認できていない。 おそらく大丈夫だとは思うが。

参考URL

http://forum.easyelectronics.ru/viewtopic.php?f=49&t=34508

USBのCRC:回路とCソース - Qiita

任意の CRC 値になるバイト列を逆算してみよう - Qiita

巡回冗長検査 - Wikipedia

Catalogue of parametrised CRC algorithms