The goal is to do the same as zig's std.hash.crc.Crc32.hash(&buf)
on STM32
hardware.
There is no real complication, but it took me a while to find the correct configuration. The zig std lib code is as follow:
const buf: [9]u8 = .{1, 2, 3, 4, 4, 5, 1, 23, 4};
const crc = std.hash.crc.Crc32.hash(&buf)
The produced CRC for the above code is 0xd85c2351
.
To get the same CRC with STM32 hardware, here is the code:
pub fn crc(buf: []const u8) u32 {
_ = buf;
regs.CRC.INIT.modify(.{ .CRC_INIT = 0xffffffff });
regs.CRC.POL.modify(.{
.Polynomialcoefficients = 0x4C11DB7,
});
regs.CRC.CR.modify(.{
.RESET = 1,
.REV_IN = 0b01,
.REV_OUT = 1,
});
const dr = @ptrCast(*volatile u8, regs.CRC.DR);
for (buf) |b| dr.* = b;
return @intCast(u32, regs.CRC.DR.*) ^ 0xffffffff;
}
There are multiple things to explain here.
First, the CRC_INIT
is the default value, but I do specify it here.
The POL
is the default value for polynomial coefficient is the same as zig,
except that if you read zig code, you will find this value: 0xedb88320
. It
took me a bit to realize it was the same but with bits reversed.
The CR
has three configurations:
RESET
just reset the CRC state machineREV_IN
told the CRC to bit reverse the input data byte per byteREV_OUT
told the CRC to bit reverse the result data
Now the last gotcha is that the DR
register must be accessed as 8 bit to
iterate the slice. This is what the ptrCast
is for.
Finally, the resulting CRC must be XOR'ed with 0xffffffff
to produce the same
result as the zig std lib. This also took me a bit to find out.
I hope this can help you if you need to generate a CRC on STM32 hardware and in software.
I tested this on STM32l0x1 hardware, but I think it should work on other MCU.