项目文件夹

文件
2026-07-13 13:04:25 +08:00

345 行
11 KiB
ReStructuredText

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
=============
异步 CRC 计算
=============
:link_to_translation:`en:[English]`
本文档介绍了 ESP-IDF 中异步 CRC (Async CRC) 驱动程序的功能。目录如下:
.. contents::
:local:
:depth: 2
概述
====
异步 CRC 驱动程序提供使用通用 DMA 外设的硬件加速 CRC 计算。它支持 AHB-GDMA 和 AXI-GDMA 后端,提供灵活的 CRC 计算,支持可配置的多项式、初始值、位反转选项和最终 XOR 处理。
主要功能包括:
- 使用通用 DMA 进行硬件加速的 CRC 计算
- 支持 8 位、16 位和 32 位 CRC 算法
- 带回调通知的异步 API
- 支持超时的阻塞 API
- 支持可配置积压大小的请求队列
- 支持 AHB 和 AXI DMA 后端
应用场景包括:
- 通信协议的数据完整性验证
- 文件和固件校验和计算
- 网络数据包验证
- 存储数据验证
快速开始
========
本节简要介绍如何使用异步 CRC 驱动程序。通过实际示例,演示如何初始化驱动程序、配置 CRC 参数以及执行异步和阻塞 CRC 计算。
异步 CRC 的典型使用流程如下:
.. blockdiag::
:scale: 100%
:caption: 异步 CRC 驱动程序的一般使用流程(点击放大)
:align: center
blockdiag {
default_fontsize = 14;
node_width = 250;
node_height = 80;
class emphasis [color = pink, style = dashed];
install [label="esp_async_crc_install_gdma_*"];
calc [label="esp_async_crc_calc"];
wait [label="等待回调"];
process [label="处理结果"];
uninstall [label="esp_async_crc_uninstall"];
install -> calc -> wait -> process;
process -> calc [folded];
calc -> uninstall [folded];
}
创建和安装驱动程序
------------------
首先,您需要安装异步 CRC 驱动程序。该驱动程序根据芯片的功能支持 AHB-GDMA 和 AXI-GDMA 后端:
.. code:: c
async_crc_handle_t crc_hdl = NULL;
async_crc_config_t config = {
.backlog = 8, // 队列中最大挂起请求数
.dma_burst_size = 16, // DMA 突发传输大小(字节)
};
// 使用 AHB-GDMA 后端安装(如果可用)
ESP_ERROR_CHECK(esp_async_crc_install_gdma_ahb(&config, &crc_hdl));
// 或使用 AXI-GDMA 后端安装(如果可用)
// ESP_ERROR_CHECK(esp_async_crc_install_gdma_axi(&config, &crc_hdl));
.. note::
**选择 AHB-GDMA 和 AXI-GDMA 后端**
后端选择取决于芯片的功能和性能要求:
- **AHB-GDMA**:大多数 ESP 芯片均可使用。连接到 AHB 总线,适用于通用 DMA 操作。最适合:
- 标准性能要求
- 大多数 ESP 芯片变体的兼容性
- **AXI-GDMA**:在支持 AXI 总线的高端 ESP 芯片上可用。为内存密集型操作提供更高的带宽和更好的性能。最适合:
- 高吞吐量 CRC 计算
- 处理大量数据
- 需要最大性能的应用
- 更高效地访问外部存储器(PSRAM
创建驱动程序实例时,您需要配置:
- **backlog**:可排队等待的最大 CRC 请求数。较高的值使用更多内存,但在突发工作负载下提供更好的吞吐量。
- **intr_priority**DMA 中断优先级。设置为 ``0`` 时使用默认的低/中优先级;设置为非零值时请求指定的中断优先级。
- **dma_burst_size**DMA 传输突发大小(字节)。
驱动程序句柄 ``crc_hdl`` 是一个不透明指针,用于所有后续操作。
执行异步 CRC 计算
-----------------
异步 API 允许您在不阻塞的情况下排队 CRC 计算:
.. code:: c
static bool crc_complete_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
{
uint32_t result = edata->crc_result;
// 进一步处理 CRC 结果
// 例如,发送到任务队列,记录日志等。
return false;
}
// 配置 CRC-32 的 CRC 参数
async_crc_params_t params = {
.width = 32,
.polynomial = 0x04C11DB7,
.init_value = 0xFFFFFFFF,
.final_xor_value = 0xFFFFFFFF,
.reverse_input = true,
.reverse_output = true,
};
// 启动异步 CRC 计算
const char *data = "Hello, World!";
size_t data_len = strlen(data);
ESP_ERROR_CHECK(esp_async_crc_calc(crc_hdl, data, data_len, &params, crc_complete_callback, NULL));
CRC 计算完成时会在中断上下文中调用回调函数。回调接收:
- **crc_hdl**:驱动程序句柄
- **edata**:包含 CRC 结果的事件数据
- **cb_args**:在 ``esp_async_crc_calc`` 期间传递的用户定义参数
执行阻塞 CRC 计算
-----------------
对于更简单的使用场景或不需要异步操作的情况,请使用阻塞 API:
.. code:: c
uint32_t crc_result = 0;
async_crc_params_t params = {
.width = 32,
.polynomial = 0x04C11DB7,
.init_value = 0xFFFFFFFF,
.final_xor_value = 0xFFFFFFFF,
.reverse_input = true,
.reverse_output = true,
};
const char *data = "Hello, World!";
size_t data_len = strlen(data);
// 阻塞 CRC,无限期等待
ESP_ERROR_CHECK(esp_crc_calc_blocking(crc_hdl, data, data_len, &params, -1, &crc_result));
printf("CRC 结果: 0x%08X\n", crc_result);
阻塞 API 仅支持 ``timeout_ms = -1``,表示无限期等待直到 CRC 计算完成。
卸载驱动程序
------------
当不再需要驱动程序时:
.. code:: c
ESP_ERROR_CHECK(esp_async_crc_uninstall(crc_hdl));
如果存在挂起的操作或 CRC 引擎正忙,卸载函数将返回 :c:macro:`ESP_ERR_INVALID_STATE`。请在卸载前确保所有操作已完成。
CRC 参数配置
============
异步 CRC 驱动程序通过 :cpp:type:`async_crc_params_t` 结构支持灵活的 CRC 算法配置。
CRC 宽度
---------
:cpp:member:`async_crc_params_t::width` 字段指定 CRC 位宽度:
- **8**8 位 CRC(如 CRC-8、CRC-8/MAXIM
- **16**16 位 CRC(如 CRC-16/CCITT、CRC-16/IBM
- **32**32 位 CRC(如 CRC-32、CRC-32/BZIP2
多项式
--------
:cpp:member:`async_crc_params_t::polynomial` 字段以十六进制格式指定 CRC 多项式。常见的多项式值包括:
- CRC-32: ``0x04C11DB7``
- CRC-16/CCITT: ``0x1021``
- CRC-16/IBM: ``0x8005``
- CRC-8/MAXIM: ``0x31``
初始值
---------
:cpp:member:`async_crc_params_t::init_value` 字段设置处理前的初始 CRC 值。常见的初始值:
- ``0xFFFFFFFF`` 用于 CRC-32
- ``0x0000`` 用于许多 CRC-16 变体
- ``0x00`` 用于许多 CRC-8 变体
最终 XOR 值
-----------
:cpp:member:`async_crc_params_t::final_xor_value` 字段指定在与最终 CRC 结果进行异或之前的值。这通常是 CRC-32 的 ``0xFFFFFFFF``,但对于某些变体可以是 ``0x0000``。
位反转选项
-----------
- :cpp:member:`async_crc_params_t::reverse_input` 如果为 true,在处理前反转每个输入字节的位顺序
- :cpp:member:`async_crc_params_t::reverse_output` 如果为 true,在应用最终 XOR 之前反转最终 CRC 结果的位顺序
这些选项影响不同 CRC 算法的反射设置。
常见 CRC 配置
-------------
下表列出了常见的 CRC 配置:
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC 算法 | 位宽 | 多项式 | 初始值 | 最终 XOR 值 | 反转输入 | 反转输出 |
+================+==========+===============+===============+==================+===============+===============+
| CRC-32 | 32 | 0x04C11DB7 | 0xFFFFFFFF | 0xFFFFFFFF | true | true |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-16/CCITT | 16 | 0x1021 | 0x0000 | 0x0000 | false | false |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-16/IBM | 16 | 0x8005 | 0x0000 | 0x0000 | true | true |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
| CRC-8/MAXIM | 8 | 0x31 | 0x00 | 0x00 | true | true |
+----------------+----------+---------------+---------------+------------------+---------------+---------------+
线程安全
========
异步 CRC 驱动程序设计为线程安全的,可以从多个任务中使用。该驱动程序采用 **无竞争有限状态机(FSM)** 架构,确保线程安全并正确处理并发 CRC 请求。
线程安全保证
------------
- 所有公共 API 可以同时从不同任务调用
- 驱动程序对内部状态使用原子操作和临界区保护
- 请求队列确保并发调用被正确串行化
ISR 上下文限制
--------------
异步 API 和阻塞 API 都不能从中断上下文调用。具体来说:
- :cpp:func:`esp_async_crc_calc`:涉及内存分配/释放、DMA 准备工作和非 ISR 安全的日志函数
- :cpp:func:`esp_crc_calc_blocking`:使用可能阻塞的同步原语
回调限制
--------
回调函数(:cpp:type:`async_crc_isr_cb_t`)在中断上下文中执行。因此:
- 不要执行阻塞操作(如 ``vTaskDelay``、带超时的 ``xQueueSend``
- 保持执行时间最小化,以免影响系统中断延迟
- 不要使用 ``malloc`` 或类似函数分配内存
- 只使用 ISR 安全的 FreeRTOS API(如 ``xQueueSendFromISR``, ``xSemaphoreGiveFromISR``
- 如果回调唤醒了高优先级任务,返回 ``true``
使用队列的回调示例:
.. code:: c
static bool crc_callback(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)
{
QueueHandle_t queue = (QueueHandle_t)cb_args;
BaseType_t high_task_awoken = pdFALSE;
// 通过 ISR 安全队列将结果发送到任务
xQueueSendFromISR(queue, &edata->crc_result, &high_task_awoken);
return high_task_awoken == pdTRUE;
}
缓冲区要求
==========
异步 CRC 驱动程序对数据缓冲区有特定要求。
内存类型
--------
数据缓冲区可以来自内部存储区域(DRAM/IRAM)或外部存储区域(PSRAM, Flash)中。驱动程序自动处理两者:
.. code:: c
// 内部 RAM
static char internal_data[] = "Data in internal RAM";
esp_async_crc_calc(crc_hdl, internal_data, strlen(internal_data), &params, callback, NULL);
// 外部 Flash
static const char *flash_data = "Data in external Flash";
esp_async_crc_calc(crc_hdl, flash_data, strlen(flash_data), &params, callback, NULL);
性能注意事项
============
积压配置
--------
``backlog`` 配置影响性能:
- 小积压(4-8):内存使用量较低,高负载下可能会产生背压
- 大积压(16+):突发工作负载的吞吐量更好,内存使用量更高
根据应用的内存约束和工作负载模式进行选择。
DMA 突发大小
------------
``dma_burst_size`` 影响 DMA 传输效率:
- 较大的突发大小可以提高吞吐量
- 典型值:16、32、64 字节
最佳值取决于芯片的 DMA 控制器功能。
应用示例
========
- :example:`peripherals/dma/async_crc` 演示了如何通过交互式控制台 CLI 使用异步 CRC 驱动程序。
API 参考
========
异步 CRC 驱动程序函数
---------------------
.. include-build-file:: inc/esp_async_crc.inc