Files
nuki_hub/lib/Crc16/Crc16.h
2022-03-22 21:10:43 +01:00

244 lines
7.3 KiB
C++

//-------------------------------------------------------------------------------------
// CRC16 support class
// Based on various examples found on the web
// Copyright (C) 2014 Vincenzo Mennella (see license.txt)
// History
// 0.1.0 31/05/2014: First public code release
// 0.1.1 17/12/2014: Minor revision and commented code
// 0.1.2 06/06/2019: Fix reflect routine for 16 bit data
// Added ModBus and Mcrf4XX inline functions
//
// License
// "MIT Open Source Software License":
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in the
// Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-------------------------------------------------------------------------------------
#ifndef CRC16_H
#define CRC16_H
#define LIBRARY_VERSION_CRC16_H "0.1.2"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#elif defined(ARDUINO)
#include "WProgram.h"
#else
#include <cstdint>
#endif
class Crc16 {
private:
//Crc parameters
uint16_t _msbMask;
uint16_t _mask;
uint16_t _xorIn;
uint16_t _xorOut;
uint16_t _polynomial;
uint8_t _reflectIn;
uint8_t _reflectOut;
//Crc value
uint16_t _crc;
uint8_t reflect(uint8_t data);
uint16_t reflect(uint16_t data);
public:
inline Crc16()
{
//Default to XModem parameters
_reflectIn = false;
_reflectOut = false;
_polynomial = 0x1021;
_xorIn = 0x0000;
_xorOut = 0x0000;
_msbMask = 0x8000;
_mask = 0xFFFF;
_crc = _xorIn;
}
inline Crc16(uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask)
{
_reflectIn = reflectIn;
_reflectOut = reflectOut;
_polynomial = polynomial;
_xorIn = xorIn;
_xorOut = xorOut;
_msbMask = msbMask;
_mask = mask;
_crc = _xorIn;
}
inline void clearCrc();
inline void updateCrc(uint8_t data);
inline uint16_t getCrc();
inline unsigned int fastCrc(uint8_t data[], uint8_t start, uint16_t length, uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask);
inline unsigned int XModemCrc(uint8_t data[], uint8_t start, uint16_t length)
{
// XModem parameters: poly=0x1021 initialize=0x0000 refin=false refout=false xorout=0x0000
return fastCrc(data, start, length, false, false, 0x1021, 0x0000, 0x0000, 0x8000, 0xffff);
}
inline unsigned int Mcrf4XX(uint8_t data[], uint8_t start, uint16_t length)
{
return fastCrc(data, start, length, true, true, 0x1021, 0xffff, 0x0000, 0x8000, 0xffff);
}
inline unsigned int Modbus(uint8_t data[], uint8_t start, uint16_t length)
{
return fastCrc(data, start, length, true, true, 0x8005, 0xffff, 0x0000, 0x8000, 0xffff);
}
};
//---------------------------------------------------
// Initialize crc calculation
//---------------------------------------------------
void Crc16::clearCrc()
{
_crc = _xorIn;
}
//---------------------------------------------------
// Update crc with new data
//---------------------------------------------------
void Crc16::updateCrc(uint8_t data)
{
if (_reflectIn != 0)
data = (uint8_t) reflect(data);
int j = 0x80;
while (j > 0)
{
uint16_t bit = (uint16_t)(_crc & _msbMask);
_crc <<= 1;
if ((data & j) != 0)
{
bit = (uint16_t)(bit ^ _msbMask);
}
if (bit != 0)
{
_crc ^= _polynomial;
}
j >>= 1;
}
}
//---------------------------------------------------
// Get final crc value
//---------------------------------------------------
uint16_t Crc16::getCrc()
{
if (_reflectOut != 0)
_crc = (unsigned int)((reflect(_crc) ^ _xorOut) & _mask);
return _crc;
}
//---------------------------------------------------
// Calculate generic crc code on data array
// Examples of crc 16:
// Kermit: width=16 poly=0x1021 initialize=0x0000 refin=true refout=true xorout=0x0000 check=0x2189
// Modbus: width=16 poly=0x8005 initialize=0xffff refin=true refout=true xorout=0x0000 check=0x4b37
// XModem: width=16 poly=0x1021 initialize=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3
// CCITT-False: width=16 poly=0x1021 initialize=0xffff refin=false refout=false xorout=0x0000 check=0x29b1
//---------------------------------------------------
unsigned int Crc16::fastCrc(uint8_t data[], uint8_t start, uint16_t length, uint8_t reflectIn, uint8_t reflectOut, uint16_t polynomial, uint16_t xorIn, uint16_t xorOut, uint16_t msbMask, uint16_t mask)
{
uint16_t crc = xorIn;
int j;
uint8_t c;
unsigned int bit;
if (length == 0) return crc;
for (int i = start; i < (start + length); i++)
{
c = data[i];
if (reflectIn != 0)
c = (uint8_t) reflect(c);
j = 0x80;
while (j > 0)
{
bit = (unsigned int)(crc & msbMask);
crc <<= 1;
if ((c & j) != 0)
{
bit = (unsigned int)(bit ^ msbMask);
}
if (bit != 0)
{
crc ^= polynomial;
}
j >>= 1;
}
}
if (reflectOut != 0)
crc = (unsigned int)((reflect((uint16_t) crc) ^ xorOut) & mask);
return crc;
}
//-------------------------------------------------------
// Reflects bit in a uint8_t
//-------------------------------------------------------
uint8_t Crc16::reflect(uint8_t data)
{
const uint8_t bits = 8;
unsigned long reflection = 0x00000000;
// Reflect the data about the center bit.
for (uint8_t bit = 0; bit < bits; bit++)
{
// If the LSB bit is set, set the reflection of it.
if ((data & 0x01) != 0)
{
reflection |= (unsigned long)(1 << ((bits - 1) - bit));
}
data = (uint8_t)(data >> 1);
}
return reflection;
}
//-------------------------------------------------------
// Reflects bit in a uint16_t
//-------------------------------------------------------
uint16_t Crc16::reflect(uint16_t data)
{
const uint8_t bits = 16;
unsigned long reflection = 0x00000000;
// Reflect the data about the center bit.
for (uint8_t bit = 0; bit < bits; bit++)
{
// If the LSB bit is set, set the reflection of it.
if ((data & 0x01) != 0)
{
reflection |= (unsigned long)(1 << ((bits - 1) - bit));
}
data = (uint16_t)(data >> 1);
}
return reflection;
}
#endif