从 STM32F0 和 zlib 匹配 CRC32

Matching CRC32 from STM32F0 and zlib

我正在研究 link 计算机 运行 Linux 和 STM32F0 之间的通信。我想对我的数据包使用某种错误检测,并且由于 STM32F0 具有 CRC32 硬件并且我在 Linux 上具有带 CRC32 的 zlib,我认为在我的项目中使用 CRC32 是个好主意。问题是我不会在不同平台上为相同的数据获得相同的 CRC 值。

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>

int
main(void)
{
  uint8_t byte0 = 0x00;
  uint32_t crc0 = crc32(0L, Z_NULL, 0);
  crc0 = crc32(crc0, &byte0, 1);
  printf("CRC32 value of %" PRIu8 " is: %08" PRIx32 "\n", byte0, crc0);
}

输出 CRC32 value of 0 is: d202ef8d 与几个在线计算器的结果相匹配。

似乎无论我在 STM32 上使用什么设置,我都无法获得相同的 CRC。 我找到了关于 CRC hw 如何计算其值的流程图 in an application note from ST 但我无法弄清楚它是如何在 zlib 中完成的。

有谁知道他们是否兼容?

[编辑 1] 它们都使用相同的初始值和多项式。

[edit 2] STM32 代码相对不感兴趣,因为它使用的是硬件。

...
/* Default values are used for init value and polynomial, see edit 1 */
CRC->CR |= CRC_CR_RESET;
CRC->DR = (uint8_t)0x00;
uint32_t crc = CRC->DR;
...

我没有测试过这个,但我怀疑你实际上没有在 STM32 上进行 8 位写入。

相反,您可能正在写入寄存器的整个宽度(32 位),这当然意味着您计算的 CRC32 字节数比您预期的多。

反汇编生成的代码以分析正在使用的确切存储指令。

从文档来看,您的 STM32 代码似乎不仅无趣,而且相当不完整。根据文档,为了使用 CRC 硬件,您需要:

  1. 通过 RCC 外设启用 CRC 外设时钟。
  2. 通过配置初始 CRC 值寄存器 (CRC_INIT) 将 CRC 数据寄存器设置为初始 CRC 值。(a)
  3. 通过CRC控制寄存器(CRC_CR)中的REV_IN[1:0]和REV_OUT位分别设置I/O反向位序。(a)
  4. 通过CRC中的POLYSIZE[1:0]位设置多项式大小和系数 控制寄存器 (CRC_CR) 和 CRC 多项式寄存器 (CRC_POL) 分别。(b)
  5. 通过 CRC 控制寄存器 (CRC_CR) 中的复位位复位 CRC 外设。
  6. 将数据设置到 CRC 数据寄存器。
  7. 读取CRC数据寄存器的内容。
  8. 禁用 CRC 外设时钟。

请特别注意步骤 2、3 和 4,它们定义了正在计算的 CRC。他们说他们的例子有 rev_in 和 rev_out false,但对于 zlib crc,它们需要为真。根据硬件的实现方式,多项式也可能需要反转 (0xedb88320UL)。初始 CRC 需要 0xffffffff,最终 CRC 反转以匹配 zlib crc。

STM32Fx 上的 CRC32 实现似乎不是您在许多在线 CRC 计算器和 zip 中使用的标准 CRC32 实现。

与使用小端和最终翻转掩码的 zip CRC32 相比,STM32 实现了使用大端和无最终翻转掩码的 CRC32-MPEG2。

我找到了 this 支持 CRC32-MPEG2 的在线计算器。

如果您对其他 CRC 算法及其实现更感兴趣,请查看此 link

PS: STM 的 HAL 驱动程序支持字节、半字和字格式的输入,它们似乎适用于 v1.3.1 中的 STM32F0x

根据我的经验,您无法将STM32 CRC 单元输出的CRC32 代码与在线CRC32 计算器进行比较。

请找到我的 STM32 CRC32 计算器

这个功能已经在我的项目中使用,证明是正确的。

我在 STM32 CRC 模块上实施 CRC 时遇到了类似的问题,其中最终校验和不匹配。通过查看 STMCube 提供的 stm32f30x_crc.c 中的示例代码,我能够解决这个问题。在源代码中,它有一个使用以下代码的 8 位 CRC 函数

(uint8_t)(CRC_BASE) = (uint8_t) CRC_Data;

写入DR寄存器。如前所述,行 CRC->DR 被定义为 volatile uint32_t,它将访问整个寄存器。如果 ST 更明确地说明访问 DR 寄存器而不是简单地说它支持 8 位类型,那将会很有帮助。