mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-30 09:36:36 +03:00
347 lines
11 KiB
ReStructuredText
347 lines
11 KiB
ReStructuredText
====================
|
||
异步 CRC (Async 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 请求数。较高的值使用更多内存,但在突发工作负载下提供更好的吞吐量。
|
||
- **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, ¶ms, 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,1000ms 超时
|
||
ESP_ERROR_CHECK(esp_crc_calc_blocking(crc_hdl, data, data_len, ¶ms, 1000, &crc_result));
|
||
|
||
printf("CRC 结果: 0x%08X\n", crc_result);
|
||
|
||
阻塞 API 支持:
|
||
|
||
- **timeout_ms >= 0**:等待指定的毫秒数
|
||
- **timeout_ms < 0**:无限期等待
|
||
|
||
卸载驱动程序
|
||
------------
|
||
|
||
当不再需要驱动程序时:
|
||
|
||
.. 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), ¶ms, callback, NULL);
|
||
|
||
// 外部 Flash
|
||
static const char *flash_data = "Data in external Flash";
|
||
esp_async_crc_calc(crc_hdl, flash_data, strlen(flash_data), ¶ms, 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
|