Skip to content

Instantly share code, notes, and snippets.

@rcls
Last active May 6, 2022 07:18
Show Gist options
  • Save rcls/627ea2cb60e3136da22539be2aad54d7 to your computer and use it in GitHub Desktop.
Save rcls/627ea2cb60e3136da22539be2aad54d7 to your computer and use it in GitHub Desktop.
LTC6811pec
// This is a 15 bit CRC used by the LTC6811.
//
// The CRC modulus polynomial is x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1.
//
// I.e., 0xc599 or 0x4599 depending on whether you include the leading
// coefficient.
//
// Natural bit order is used (high bit of first byte has highest order; for SPI,
// first bit on the wire has highest order), both for the data and PEC.
//
// There is an initialization vector 000000000010000 = 0x10, and a trailer of 15
// zeros (in the datasheet, implicit in the way new bits are stired in - the
// algorithm there does the reduction of the data polynomial multiplied by x¹⁵.
//
// The PEC is appended to the data and padded to 16 bits by appending
// a final zero.
//
// So the concatenation: 0x0010, data, PEC15 (with or without the final 0 bit)
// should be a polynomial multiple of the CRC modulus.
//
// In our calculations, we keep the CRC aligned in the high 15 bits of a 16 bit
// word, as this matches the output format.
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#define POLY 0xc599
// Left shift AKA multiplication by X, modulo the polynomial.
#define SHL(x) ((x & 0x8000 ? x ^ POLY : x) << 1)
// CRC of a single byte with one bit in the numbered position (0x01 -> B0,
// 0x80 -> B7).
enum {
B0 = SHL(0x8000), B1 = SHL(B0), B2 = SHL(B1), B3 = SHL(B2),
B4 = SHL(B3), B5 = SHL(B4), B6 = SHL(B5), B7 = SHL(B6),
};
#define A(X) X, X^B0, X^B1, X^B1^B0, X^B2, X^B2^B0, X^B2^B1, X^B2^B1^B0,
#define B(X) A(X) A(X^B3) A(X^B4) A(X^B4^B3)
static uint16_t crc_table[256] = {
B(0) B(B5) B(B6) B(B6^B5) B(B7) B(B7^B5) B(B7^B6) B(B7^B6^B5)
};
static inline uint16_t crc_byte(uint16_t c, int byte)
{
return c << 8 ^ crc_table[byte ^ c >> 8];
}
static uint16_t crc_bytes(uint16_t c, const uint8_t * buf, size_t len)
{
for (size_t i = 0; i < len; ++i)
c = crc_byte(c, buf[i]);
return c;
}
static inline int ltc6811_pec(const uint8_t * buf, size_t len)
{
// Note the left shift by one on the initialization value to match our
// high-bit aligned format.
return crc_bytes(0x0020, buf, len);
}
static inline bool ltc6811_check(const uint8_t * buf, size_t len)
{
return len >= 2 && ltc6811_pec(buf, len) == 0 && ~buf[len - 1] & 1;
}
int main()
{
const uint8_t bytes[] = { 0, 1 };
int c = ltc6811_pec(bytes, sizeof bytes);
printf("%04x (expect 0x3D6E)\n", c);
const uint8_t check[] = { 0, 1, 0x3d, 0x6e };
printf("Check = %s\n",
ltc6811_check(check, sizeof check) ? "PASS" : "FAIL");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment