Search code examples
stm32spimicrochipcrc16

Verifying checksum for MCP3561/2/4R


I have an SPI communication with the chip in question.

https://ww1.microchip.com/downloads/en/DeviceDoc/MCP3561.2.4R-Data-Sheet-DS200006391A.pdf

According to the datasheet, section 6.5, after reading my 6 SDO bytes, last 16 bits being the checksum, the CRC16 of those 6 bytes will be 0x0000 when there is no error.

Here is a small dump of messages, last two bytes being the CRC16


13 ff ef d1 10 e0
13 ff ef cf 10 a4
13 ff ef c3 10 8c
13 ff ef c8 90 b5
13 ff ef ce 90 a1
13 ff ef d1 10 e0
13 ff ef cb 90 bf
13 ff ef c1 90 83
13 ff ef d6 90 f1
13 ff ef cb 90 bf
13 ff ef d2 10 ea
13 ff ef c9 10 b0
13 ff ef d0 90 e5
13 ff ef cf 10 a4
13 ff ef ce 90 a1
13 ff ef da 90 d9
13 ff ef d2 10 ea
13 ff ef ca 10 ba

Here is a description for each of the 6 bytes

0 : statusByte
1 : 24 bit high byte
2 : 24 bit middle byte
3 : 24 bit low byte
4 : CRC16 high byte
5 : CRC16 low byte

The code runs on a STM32 embedded device.

What I want is a reference implementation for the CRC16 used for SPI reads.


Solution

  • According to datasheet, the CRC is the standard CRC16 with polynomial known as 0x8005. Here is the CRC16 implementation that I used for all of my communication validations.

    
    #define POLY_REVERSED 0xA001  // bit reverse of polynomial 0x8005
    
    uint16_t CRC16(uint8_t* array, uint8_t len, uint16_t crc_init) {
    
      uint16_t _crc, _flag;
      _crc = crc_init;
    
      for (uint8_t i = 0; i < len; i++) {
        _crc ^= (uint16_t)array[i];
        for (uint8_t j = 8; j; j--) {
          _flag = _crc & 0x0001;
          _crc >>= 1;
          if (_flag)
            _crc ^= POLY_REVERSED;
        }
      }
    
      return _crc;
    
    }
    
    uint8_t crcError(uint16_t calculatedCRC, uint8_t crcH, uint8_t crcL) {
        return (calculatedCRC != (uint16_t )((crcH << 8) | crcL));
    }
    
    int main() {
        uint8_t data1[] = {0x13, 0xc8, 0xef, 0xff, 0x90, 0xb5, 0x20, 0x4a};
        uint8_t data2[] = {0x13, 0xc2, 0xef, 0xff, 0x90, 0x89, 0x2b, 0xd2};
        
        uint16_t crc = CRC16(data1, 6, 0x0000);
        printf("CRC: %02x %02x\n", crc>>8, crc & 0xff);  // 0x20 0x4a
        
        if (crcError(crc, data1[6], data1[7])) {
            printf("CRC error\n");
        }
    
        crc = CRC16(data2, 6, 0xFFFF);
        printf("CRC: %02x %02x\n", crc>>8, crc & 0xff);  // 0x2b 0xd2
        
        return 0;
    }
    

    The result of the CRC16 calculation can also be verified with online tool https://crccalc.com/?crc=0x13,0xc8,0xef,0xff,0x90,0xb5&method=crc16&datatype=hex&outtype=0

    Check on the result from CRC16/ARC line when using crc init of 0x0000.

    and https://crccalc.com/?crc=0x13,0xc2,0xef,0xff,0x90,0x89,&method=crc16&datatype=hex&outtype=0

    Check on the result from CRC16/Modbus line when using crc init of 0xFFFF.