/* {{copyright}} */ /* {{version}} */ /* {{license}} */ #pragma once #include #include namespace sdi_ToolBox::generic { //-------------------------------------------------------------- /** * @class CRC16_modbus * @brief CRC16 calculator for the Modbus protocol. * * Provides both static and incremental interfaces to compute the CRC16 checksum * as used in the Modbus protocol (polynomial 0xA001, initial value 0xFFFF). * * Usage examples: * @code * // One-shot CRC calculation * std::array data = { ... }; * uint16_t crc = Crc16_modbus::computeCRC(data); * * // Incremental CRC calculation * Crc16_modbus crcHandle; * crcHandle.init(); * for (uint8_t b : data) { * crcHandle.update(b); * } * uint16_t crc = crcHandle.finalize(); * @endcode */ class CRC16_modbus { static constexpr uint16_t INITIAL_VALUE = 0xFFFF; public: /** * @brief Computes the CRC16 of the given data in one call. * @param data The data buffer to compute the CRC for. * @return The computed CRC16 value. */ static uint16_t computeCRC(const std::span &data); public: CRC16_modbus() = default; // Default constructor virtual ~CRC16_modbus() = default; // Default destructor CRC16_modbus(const CRC16_modbus &other) = default; // Copy constructor CRC16_modbus(CRC16_modbus &&other) noexcept = default; // Move constructor CRC16_modbus &operator=(const CRC16_modbus &other) = default; // Copy assignment CRC16_modbus &operator=(CRC16_modbus &&other) noexcept = default; // Move assignment /** * @brief Constructs and initializes the CRC with the given data. * @param data The data buffer to initialize and update the CRC with. */ explicit CRC16_modbus(const std::span &data); /** * @brief Initializes or re-initializes the CRC to the default value (0xFFFF). */ void init(); /** * @brief Updates the CRC with a single byte. * @param val The byte value to update the CRC with. */ void update(const uint8_t &val); /** * @brief Updates the CRC with a data buffer. * @param data The data buffer to update the CRC with. */ void update(const std::span &data); /** * @brief Finalizes the CRC calculation and returns the CRC value. * @return The finalized CRC16 value. */ uint16_t finalize() const; /** * @brief Returns the current CRC value. * @return The current CRC16 value. */ [[nodiscard]] uint16_t getCRC() const; protected: uint16_t m_crc = INITIAL_VALUE; // Current CRC value }; //-------------------------------------------------------------- //-------------------------------------------------------------- /* Compute CRC in one go */ inline uint16_t CRC16_modbus::computeCRC(const std::span &data) { const CRC16_modbus crc(data); return crc.finalize(); } //-------------------------------------------------------------- /* Constructor */ inline CRC16_modbus::CRC16_modbus(const std::span &data) { init(); update(data); } //-------------------------------------------------------------- /* Reinit crc handle */ inline void CRC16_modbus::init() { m_crc = INITIAL_VALUE; } //-------------------------------------------------------------- /* Update CRC */ inline void CRC16_modbus::update(const uint8_t &val) { constexpr uint16_t PolynomialValue = 0xA001; m_crc ^= static_cast(val); // XOR byte into least sig. byte of crc for (int i = 8; i != 0; i--) // Loop over each bit { if ((m_crc & 0x0001) != 0) // If the LSB is set { m_crc >>= 1; // Shift right and XOR PolynomialValue m_crc ^= PolynomialValue; } else // Else LSB is not set { m_crc >>= 1; // Just shift right } } } //-------------------------------------------------------------- /* Update CRC */ inline void CRC16_modbus::update(const std::span &data) { for (const auto &c : data) update(c); } //-------------------------------------------------------------- /* Finalize and return CRC value */ inline uint16_t CRC16_modbus::finalize() const { return getCRC(); } //-------------------------------------------------------------- /* Return CRC value */ inline uint16_t CRC16_modbus::getCRC() const { return m_crc; } //-------------------------------------------------------------- } // namespace sdi_ToolBox::generic