Cyclic Redundancy Check (CRC)
1. Introduction
CRC is a technique to detect errors in data. It is widely used in communication systems to detect errors in data transmission. It is also used in data storage systems to detect errors in stored data. It is a type of hash function that takes an input data stream and produces a fixed-size hash value. The hash value is used to detect errors in the data stream. The hash value is appended to the data stream before transmission. The receiver calculates the hash value of the received data stream and compares it with the hash value received. If the hash values match, the data stream is considered error-free. If the hash values do not match, an error is detected.
2. Types of CRC
There are many types of CRC algorithms. The most common types are CRC-8
, CRC-16
, and CRC-32
. The number in the name of the CRC algorithm represents the size of the hash value produced by the algorithm. For example, CRC-8 produces an 8-bit hash value, CRC-16 produces a 16-bit hash value, and CRC-32 produces a 32-bit hash value.
3. Understanding CRC
The algorithm works by dividing the input data stream by a fixed polynomial
. The remainder of the division is the hash value
.
3.1. CRC Polynomials
Many industries have standardized on specific polynomials for compatibility and proven reliability.
Name |
Polynomial |
Hex |
Applications |
---|---|---|---|
CRC-8-SMBUS |
\(x^8 + x^2 + x + 1\) |
|
Automotive, SMBus |
CRC-16-IBM |
\(x^{16} + x^{15} + x^2 + 1\) |
|
IBM, ZMODEM |
CRC-16-CCITT |
\(x^{16} + x^{12} + x^5 + 1\) |
|
Telecommunications, Bluetooth |
CRC-32-ISO |
\(x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1\) |
|
Ethernet, ZIP files |
CRC-64-ISO |
\(x^{64} + x^4 + x^3 + x + 1\) |
|
Data storage, digital signatures |
See more at crccalc or at wikipedia.
Note
The actual length of the polynomial is one bit longer than the number of bits in the CRC. The most significant bit (MSB) is always 1. So, it is not included in the polynomial.
3.2. CRC Calculation
The simplest CRC algorithm is the CRC-8
algorithm. CRC can be expensive to calculate on real-time systems. So, it is better to use a lookup table.
Generating lookup table for CRC-8:
uint8_t poly = 0x07;
// Do you know? The actual polynomial for CRC-8 is 9-bit long. But, the MSB is always 1. So, it is not included in the polynomial.
uint8_t remainder;
uint8_t crc8_table[256];
for (int dividend = 0; dividend < 256; ++dividend)
{
remainder = dividend;
for (int b = 8; b > 0; --b)
{
if (remainder & (1 << 7)) // if MSB is 1
{
remainder = (remainder << 1) ^ poly; // XOR with polynomial. The MSB is removed because, (MSB of remainder i.e. 1) ^ (MSB of 9-bit polynomial i.e. 1) = 0 always.
}
else
{
remainder = (remainder << 1); // left shift till MSB becomes 1
}
}
crc8_table[(uint8_t)dividend] = remainder; // remainder is the CRC value of dividend
}
All the CRC value of 0-256
is stored in the table with the index as the dividend. For one byte data, the CRC is always in the table and is super easy. But for array, the remainder(CRC) of first byte (from table) is xored with the second byte, and the resulting value acts as dividend whose remainder(CRC from table) xored with third byte and so on. The remainder(CRC) of final byte calculated through this process is the remainder(CRC) of the whole array.
Calculating CRC-8 of array using lookup table:
uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05}; // input data
uint8_t dividend;
uint8_t remainder = 0;
for (int index = 0; index < len; ++index)
{
dividend = data[index] ^ remainder;
remainder = crc8_table[dividend];
}
printf("CRC-8: 0x%02X\n", remainder);
Please see this article and go through it to understand the CRC algorithm in detail. Take your time. Get a good understanding of the algorithm. This article is the best one, I have found on the internet.
After you know CRC-8, now it easy to understand other type. CRC8-8 is worth for small size of data but for bigger chunk or less collision, CRC-16 or CRC-32 are used.
Generating lookup table for CRC-16:
uint16_t poly = 0x1021;
uint16_t remainder;
uint16_t crc16_table[256];
for (int dividend = 0; dividend < 256; ++dividend)
{
remainder = dividend << 8; // This is the difference from CRC-8. The 8 bits are aligned to MSB of 16-bit. For CRC-32, it is shifted left by 24;
for (int b = 8; b > 0; --b)
{
if (remainder & (1 << 7)) // if MSB is 1
{
remainder = (remainder << 1) ^ poly; // XOR with polynomial. The MSB is removed because, (MSB of remainder i.e. 1) ^ (MSB of 9-bit polynomial i.e. 1) = 0 always.
}
else
{
remainder = (remainder << 1); // left shift till MSB becomes 1
}
}
crc16_table[(uint8_t)dividend] = remainder; // remainder is the CRC value of dividend
}
Calculating CRC-16 of array using lookup table:
uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x05}; // input data
uint8_t dividend;
uint16_t remainder = 0;
for (int index = 0; index < len; ++index)
{
dividend = data[index] ^ (remainder >> 8); // For CRC-16, remainder have to be shifted right by 8. For CRC-32, it is 24.
remainder = crc16_table[dividend];
}
printf("CRC-16: 0x%04X\n", remainder);
You got the idea. Now, you can generate the lookup table for CRC-32 and calculate the CRC-32 of the data. The are also some parameters like initial value, final XOR value, input reflected, output reflected and check value, which can be used to improve the CRC calculation. But, the above code is the basic one.
4. Implementing CRC in C++
Let’s implement the CRC algorithm in C++ using template class. I hope you have basic knowledge of C++ programming. In embedded programming, use of C++ standard library (eg: iostream, std::vector) is not recommended. Create and copy these three files.
crc.hpp:
1/**
2 * @file crc.hpp
3 * @brief Header file for the CRC template class, providing support for CRC computation with customizable parameters.
4 *
5 * This file defines a template class for calculating CRC (Cyclic Redundancy Check) with support for reflection and various CRC types.
6 *
7 * @details
8 * The `CRC` template class supports 8-bit, 16-bit, and 32-bit CRC calculations with configurable polynomials,
9 * initial values, final XOR values, and input/output bit reflections. It includes functionality for table generation
10 * and bit reflection. The default configuration values and polynomial constants for common CRC types (e.g., CRC-8 SMBus,
11 * CRC-16 CCITT, CRC-32 ISO) are also provided.
12 *
13 * @note
14 * This implementation enforces compile-time type checks to ensure only valid CRC types are used.
15 *
16 * @author
17 * Sagar Chaudhary
18 *
19 * @date
20 * 2024
21 */
22
23#ifndef CRC_HPP
24#define CRC_HPP
25
26#include <stdint.h>
27#include <stddef.h>
28#include <assert.h>
29#include <type_traits>
30
31/**
32 * @typedef CRC8_Type
33 * @brief Defines an 8-bit CRC type.
34 */
35using CRC8_Type = uint8_t;
36
37/**
38 * @typedef CRC16_Type
39 * @brief Defines a 16-bit CRC type.
40 */
41using CRC16_Type = uint16_t;
42
43/**
44 * @typedef CRC32_Type
45 * @brief Defines a 32-bit CRC type.
46 */
47using CRC32_Type = uint32_t;
48
49// Definitions of some CRC standards
50#define CRC8_SMBUS_POLYNOMIAL 0x07 ///< Polynomial for CRC-8 SMBus
51#define CRC8_SMBUS_INITIAL_CRC 0x00 ///< Initial value for CRC-8 SMBus
52#define CRC8_SMBUS_FINAL_XOR 0x00 ///< Final XOR value for CRC-8 SMBus
53#define CRC8_SMBUS_REFLECT_INPUT false ///< Input reflection for CRC-8 SMBus
54#define CRC8_SMBUS_REFLECT_OUTPUT false ///< Output reflection for CRC-8 SMBus
55
56#define CRC16_CCITT_POLYNOMIAL 0x1021 ///< Polynomial for CRC-16 CCITT
57#define CRC16_CCITT_INITIAL_CRC 0xFFFF ///< Initial value for CRC-16 CCITT
58#define CRC16_CCITT_FINAL_XOR 0x0000 ///< Final XOR value for CRC-16 CCITT
59#define CRC16_CCITT_REFLECT_INPUT false ///< Input reflection for CRC-16 CCITT
60#define CRC16_CCITT_REFLECT_OUTPUT false ///< Output reflection for CRC-16 CCITT
61
62#define CRC32_ISO_POLYNOMIAL 0x04C11DB7 ///< Polynomial for CRC-32 ISO
63#define CRC32_ISO_INITIAL_CRC 0xFFFFFFFF ///< Initial value for CRC-32 ISO
64#define CRC32_ISO_FINAL_XOR 0xFFFFFFFF ///< Final XOR value for CRC-32 ISO
65#define CRC32_ISO_REFLECT_INPUT true ///< Input reflection for CRC-32 ISO
66#define CRC32_ISO_REFLECR_OUTPUT true ///< Output reflection for CRC-32 ISO
67
68#define CRC_VALIDATION_STRING "123456789" ///< Standard validation string for CRC
69
70/**
71 * @class CRC
72 * @brief Template class for CRC computation.
73 *
74 * @tparam CRC_Type The CRC type, must be one of `CRC8_Type`, `CRC16_Type`, or `CRC32_Type`.
75 *
76 * @details
77 * This class allows calculation of CRC using a lookup table for improved performance.
78 * It supports customizable parameters such as polynomial, initial value, and bit reflection.
79 */
80template <typename CRC_Type>
81class CRC
82{
83 static_assert(std::is_same<CRC_Type, CRC8_Type>::value ||
84 std::is_same<CRC_Type, CRC16_Type>::value ||
85 std::is_same<CRC_Type, CRC32_Type>::value,
86 "CRC template class only supports CRC8_Type, CRC16_Type, and CRC32_Type!");
87
88public:
89 CRC();
90 CRC(CRC_Type polynomial, CRC_Type initial_crc = 0, const CRC_Type final_xor = 0,
91 bool reflect_input = false, bool reflect_output = false);
92
93 CRC(const CRC &) = delete;
94 CRC &operator=(const CRC &) = delete;
95
96 static CRC_Type reflect_bits(CRC_Type value, int num_bits);
97 CRC_Type compute_hash(const uint8_t *data, const size_t len) const;
98
99 void print_table() const;
100
101 // Getters for class members
102 const CRC_Type get_polynomial() const; ///< Get the polynomial used for CRC.
103 const CRC_Type get_initial_crc() const; ///< Get the initial CRC value.
104 const CRC_Type get_final_xor() const; ///< Get the final XOR value.
105 const CRC_Type *get_table() const; ///< Get the CRC lookup table.
106 const CRC_Type get_check() const; ///< Get the validation CRC value.
107
108 static constexpr int width = sizeof(CRC_Type) * 8; ///< CRC width in bits.
109
110private:
111 void initialize(); ///< Initialize CRC parameters.
112 void compute_table(); ///< Compute the CRC lookup table.
113
114 const CRC_Type polynomial_; ///< Polynomial for CRC calculation.
115 const CRC_Type initial_crc_; ///< Initial CRC value.
116 const CRC_Type final_xor_; ///< Final XOR value.
117 const bool reflect_input_; ///< Input bit reflection flag.
118 const bool reflect_output_; ///< Output bit reflection flag.
119 CRC_Type table_[256]; ///< Lookup table for CRC calculation.
120 CRC_Type check_; ///< Validation CRC value.
121};
122
123#include "crc.tpp"
124
125#endif // CRC_HPP
crc.tpp:
1/**
2 * @file crc.tpp
3 * @brief Inline implementation file for the CRC template class.
4 *
5 * This file contains the inline implementation of the CRC template class,
6 * including methods for bit reflection, table computation, and CRC calculation.
7 *
8 * @ref https://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
9 *
10 * @details
11 * The CRC template class supports computation of CRC values for 8-bit, 16-bit,
12 * and 32-bit types. This file includes the reflection function, constructors
13 * for default and parameterized configurations, table initialization, and the
14 * main hash computation function. Additionally, the CRC lookup table can be printed
15 * for debugging or validation purposes.
16 *
17 * @note
18 * Ensure that the CRC polynomial is non-zero when using the parameterized constructor.
19 *
20 * @warning
21 * The CRC template does not allow copy construction or assignment.
22 *
23 * @author
24 * Sagar Chaudhary
25 *
26 * @date
27 * 2024
28 */
29
30#include <string.h>
31#include <stdio.h>
32
33#include "crc.hpp"
34
35/**
36 * @brief Default constructor for CRC-8 configuration.
37 */
38template <>
39CRC<CRC8_Type>::CRC()
40 : polynomial_(CRC8_SMBUS_POLYNOMIAL),
41 initial_crc_(CRC8_SMBUS_INITIAL_CRC),
42 final_xor_(CRC8_SMBUS_FINAL_XOR),
43 reflect_input_(CRC8_SMBUS_REFLECT_INPUT),
44 reflect_output_(CRC8_SMBUS_REFLECT_OUTPUT)
45{
46 this->initialize();
47}
48
49/**
50 * @brief Default constructor for CRC-16 configuration.
51 */
52template <>
53CRC<CRC16_Type>::CRC()
54 : polynomial_(CRC16_CCITT_POLYNOMIAL),
55 initial_crc_(CRC16_CCITT_INITIAL_CRC),
56 final_xor_(CRC16_CCITT_FINAL_XOR),
57 reflect_input_(CRC16_CCITT_REFLECT_INPUT),
58 reflect_output_(CRC16_CCITT_REFLECT_OUTPUT)
59{
60 this->initialize();
61}
62
63/**
64 * @brief Default constructor for CRC-32 configuration.
65 */
66template <>
67CRC<CRC32_Type>::CRC()
68 : polynomial_(CRC32_ISO_POLYNOMIAL),
69 initial_crc_(CRC32_ISO_INITIAL_CRC),
70 final_xor_(CRC32_ISO_FINAL_XOR),
71 reflect_input_(CRC32_ISO_REFLECT_INPUT),
72 reflect_output_(CRC32_ISO_REFLECR_OUTPUT)
73{
74 this->initialize();
75}
76
77/**
78 * @brief Parameterized constructor for CRC configuration.
79 *
80 * @param polynomial The polynomial for CRC calculation.
81 * @param initial_crc The initial CRC value.
82 * @param final_xor The final XOR value.
83 * @param reflect_input Flag to enable input bit reflection.
84 * @param reflect_output Flag to enable output bit reflection.
85 */
86template <typename CRC_Type>
87CRC<CRC_Type>::CRC(CRC_Type polynomial, CRC_Type initial_crc, CRC_Type final_xor,
88 bool reflect_input, bool reflect_output)
89 : polynomial_(polynomial),
90 initial_crc_(initial_crc),
91 final_xor_(final_xor),
92 reflect_input_(reflect_input),
93 reflect_output_(reflect_output)
94{
95 if (polynomial == 0)
96 printf("ERROR::CRC polynomial cannot be zero\n");
97
98 this->initialize();
99}
100
101/**
102 * @brief Initializes the CRC instance, including table computation and validation.
103 */
104template <typename CRC_Type>
105inline void CRC<CRC_Type>::initialize()
106{
107 this->compute_table();
108
109 const char crc_validation_string[] = CRC_VALIDATION_STRING;
110 this->check_ = this->compute_hash((uint8_t *)crc_validation_string, strlen(crc_validation_string));
111}
112
113/**
114 * @brief Reflects the bits of a value.
115 *
116 * @tparam CRC_Type The type of the value (e.g., uint8_t, uint16_t, uint32_t).
117 * @param value The value to reflect.
118 * @param num_bits The number of bits to reflect.
119 * @return The reflected value.
120 */
121template <typename CRC_Type>
122inline CRC_Type CRC<CRC_Type>::reflect_bits(CRC_Type value, int num_bits)
123{
124 CRC_Type reflected = 0;
125 for (int i = 0; i < num_bits; ++i)
126 {
127 if (value & (1 << i))
128 {
129 reflected |= (1 << (num_bits - 1 - i));
130 }
131 }
132 return reflected;
133}
134
135/**
136 * @brief Computes the CRC lookup table.
137 */
138template <typename CRC_Type>
139inline void CRC<CRC_Type>::compute_table()
140{
141 // Calculate CRC of all 256 possible byte values.
142 for (int dividend = 0; dividend < 256; ++dividend)
143 {
144 // Align the byte to leftmost position.
145 CRC_Type remainder = dividend << (this->width - 8);
146
147 for (int bit = 8; bit > 0; --bit)
148 {
149 // Left shift till bit 1 found at MSB.
150 if (remainder & (1 << (width - 1)))
151 {
152 // XOR with polynomial if MSB is 1.
153 // The MSB of polynomial is always 1 which is not included. So, MSB polynomial ^ MSB of remainder = 0.
154 // That's why it is left lefted first then XORed to find remainder.
155 remainder = (remainder << 1) ^ this->polynomial_;
156 }
157 else
158 {
159 remainder = (remainder << 1);
160 }
161 }
162
163 // Store the remainder in the lookup table.
164 this->table_[dividend] = remainder;
165 }
166}
167
168/**
169 * @brief Computes the CRC hash for the given data.
170 *
171 * @param data Pointer to the input data.
172 * @param len Length of the input data.
173 * @return The computed CRC value.
174 */
175template <typename CRC_Type>
176inline CRC_Type CRC<CRC_Type>::compute_hash(const uint8_t *data, const size_t len) const
177{
178 // Initialize the remainder with the initial CRC value.
179 CRC_Type remainder = this->initial_crc_;
180 uint8_t dividend;
181
182 // data cannot be nullptr.
183 if (data != nullptr)
184 {
185 // Initial CRC is XORed with first byte; the CRC of resulting is XORed with second byte and so on.
186 // The final CRC is XORed with final_xor_.
187
188 for (size_t index = 0; index < len; ++index)
189 {
190 // Reflect byte from data if enabled.
191 dividend = reflect_input_ ? reflect_bits(data[index], 8) : data[index];
192
193 // Left most byte of remainder is XORed with data as data is one byte in each step.
194 dividend ^= (remainder >> (this->width - 8));
195
196 // CRC of resultant is taken from lookup table.
197 // Used left most byte of remainder is removed shifting left by 8.
198 // Now, XORed to find new remainder.
199 remainder = table_[dividend] ^ (remainder << 8);
200 }
201 }
202
203 // Reflect remainder if enabled and XOR with final xor value.
204 return (reflect_output_ ? reflect_bits(remainder, this->width) : remainder) ^ this->final_xor_;
205}
206
207/**
208 * @brief Prints the CRC lookup table to the console.
209 */
210template <typename CRC_Type>
211inline void CRC<CRC_Type>::print_table() const
212{
213 for (int i = 0; i < 256; ++i)
214 {
215 printf("%0*X", this->width / 4, this->table_[i]);
216
217 if ((i + 1) % 8 == 0)
218 {
219 printf("\n");
220 }
221 else
222 {
223 printf("\t");
224 }
225 }
226}
crc_testing.cpp:
1#include <stdio.h>
2#include <string.h>
3#include "crc.hpp"
4
5int main()
6{
7 CRC<uint32_t> crc(0xF4ACFB13, 0xFFFFFFFF, 0xFFFFFFFF, true, true);
8 crc.print_table();
9 printf("check: %08X\n", crc.get_check());
10
11 char data[] = "Hello World";
12 printf("hash: %08X\n", crc.compute_hash((uint8_t*)data, strlen(data)));
13 return 0;
14}
Compile and run using your computer. To verify, use online crc calculator: sunshine2k or crccalc.
g++ crc_testing.cpp -o crc_testing
./crc_testing
Output:
00000000 F4ACFB13 1DF50D35 E959F626 3BEA1A6A CF46E179 261F175F D2B3EC4C
77D434D4 8378CFC7 6A2139E1 9E8DC2F2 4C3E2EBE B892D5AD 51CB238B A567D898
EFA869A8 1B0492BB F25D649D 06F19F8E D44273C2 20EE88D1 C9B77EF7 3D1B85E4
987C5D7C 6CD0A66F 85895049 7125AB5A A3964716 573ABC05 BE634A23 4ACFB130
2BFC2843 DF50D350 36092576 C2A5DE65 10163229 E4BAC93A 0DE33F1C F94FC40F
5C281C97 A884E784 41DD11A2 B571EAB1 67C206FD 936EFDEE 7A370BC8 8E9BF0DB
C45441EB 30F8BAF8 D9A14CDE 2D0DB7CD FFBE5B81 0B12A092 E24B56B4 16E7ADA7
B380753F 472C8E2C AE75780A 5AD98319 886A6F55 7CC69446 959F6260 61339973
57F85086 A354AB95 4A0D5DB3 BEA1A6A0 6C124AEC 98BEB1FF 71E747D9 854BBCCA
202C6452 D4809F41 3DD96967 C9759274 1BC67E38 EF6A852B 0633730D F29F881E
B850392E 4CFCC23D A5A5341B 5109CF08 83BA2344 7716D857 9E4F2E71 6AE3D562
CF840DFA 3B28F6E9 D27100CF 26DDFBDC F46E1790 00C2EC83 E99B1AA5 1D37E1B6
7C0478C5 88A883D6 61F175F0 955D8EE3 47EE62AF B34299BC 5A1B6F9A AEB79489
0BD04C11 FF7CB702 16254124 E289BA37 303A567B C496AD68 2DCF5B4E D963A05D
93AC116D 6700EA7E 8E591C58 7AF5E74B A8460B07 5CEAF014 B5B30632 411FFD21
E47825B9 10D4DEAA F98D288C 0D21D39F DF923FD3 2B3EC4C0 C26732E6 36CBC9F5
AFF0A10C 5B5C5A1F B205AC39 46A9572A 941ABB66 60B64075 89EFB653 7D434D40
D82495D8 2C886ECB C5D198ED 317D63FE E3CE8FB2 176274A1 FE3B8287 0A977994
4058C8A4 B4F433B7 5DADC591 A9013E82 7BB2D2CE 8F1E29DD 6647DFFB 92EB24E8
378CFC70 C3200763 2A79F145 DED50A56 0C66E61A F8CA1D09 1193EB2F E53F103C
840C894F 70A0725C 99F9847A 6D557F69 BFE69325 4B4A6836 A2139E10 56BF6503
F3D8BD9B 07744688 EE2DB0AE 1A814BBD C832A7F1 3C9E5CE2 D5C7AAC4 216B51D7
6BA4E0E7 9F081BF4 7651EDD2 82FD16C1 504EFA8D A4E2019E 4DBBF7B8 B9170CAB
1C70D433 E8DC2F20 0185D906 F5292215 279ACE59 D336354A 3A6FC36C CEC3387F
F808F18A 0CA40A99 E5FDFCBF 115107AC C3E2EBE0 374E10F3 DE17E6D5 2ABB1DC6
8FDCC55E 7B703E4D 9229C86B 66853378 B436DF34 409A2427 A9C3D201 5D6F2912
17A09822 E30C6331 0A559517 FEF96E04 2C4A8248 D8E6795B 31BF8F7D C513746E
6074ACF6 94D857E5 7D81A1C3 892D5AD0 5B9EB69C AF324D8F 466BBBA9 B2C740BA
D3F4D9C9 275822DA CE01D4FC 3AAD2FEF E81EC3A3 1CB238B0 F5EBCE96 01473585
A420ED1D 508C160E B9D5E028 4D791B3B 9FCAF777 6B660C64 823FFA42 76930151
3C5CB061 C8F04B72 21A9BD54 D5054647 07B6AA0B F31A5118 1A43A73E EEEF5C2D
4B8884B5 BF247FA6 567D8980 A2D17293 70629EDF 84CE65CC 6D9793EA 993B68F9
check: 1697D06A
hash: 07AAD778
5. Using STM32 CRC Hardware
STM32 microcontrollers have CRC-32 hardware. We can use it to calculate CRC-32.
Note
STM32F103C8T6
and STM32F407VGT6
CRC hardware have no configuration, so only default CRC-32 can be calculated. Their default parameters are:
Polynomial: 0x04C11DB7
Initial Value: 0xFFFFFFFF
Final XOR Value: None (must be applied manually if needed).
Reflect Input/Output: Not supported in hardware (manual reflection required).
5.1. CubeMX Setup
Generate baisc code with:
microcontroller
:STM32F103C8T6
project name
:crc_test
Toolchain/IDE
:Makefile
Also enable USB
for printf
if ITM SWV
not woriking (for bluepill using duplicate stlink which do not have SWO). See this for USB setup.

5.2. Code Implementation
Navigate to Core
> Src
> main.c
and add the following code. Add the following includes.
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "usbd_cdc_if.h"
/* USER CODE END Includes */
Add the following test code to calculate CRC-32.
/* USER CODE BEGIN 2 */
char data[] = "123456789";
char buf[100]; // For CDC transmit
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
uint32_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)data, strlen(data));
snprintf(buf, sizeof(buf), "data: %s, crc: 0x%8lX\n", data, crc);
// if it supports CRC Hardware configurtion
// snprintf(buf, sizeof(buf), "POL: 0x%8lX, INIT: 0x%8lX, CRC: 0x%8lX\n", hcrc.Instance->POL, hcrc.Instance->INIT, crc);
CDC_Transmit_FS((uint8_t*)buf, strlen(buf));
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
Compile and upload the code. Open the serial terminal and you will see the CRC-32 of the data. Verify output using online CRC calculator.