异步 CRC (Async CRC)

[English]

本文档介绍了 ESP-IDF 中异步 CRC (Async CRC) 驱动程序的功能。目录如下:

概述

异步 CRC 驱动程序提供使用通用 DMA 外设的硬件加速 CRC 计算。它支持 AHB-GDMA 和 AXI-GDMA 后端,提供灵活的 CRC 计算,支持可配置的多项式、初始值、位反转选项和最终 XOR 处理。

主要功能包括:

  • 使用通用 DMA 进行硬件加速的 CRC 计算

  • 支持 8 位、16 位和 32 位 CRC 算法

  • 带回调通知的异步 API

  • 支持超时的阻塞 API

  • 支持可配置积压大小的请求队列

  • 支持 AHB 和 AXI DMA 后端

应用场景包括:

  • 通信协议的数据完整性验证

  • 文件和固件校验和计算

  • 网络数据包验证

  • 存储数据验证

快速开始

本节简要介绍如何使用异步 CRC 驱动程序。通过实际示例,演示如何初始化驱动程序、配置 CRC 参数以及执行异步和阻塞 CRC 计算。

异步 CRC 的典型使用流程如下:

异步 CRC 驱动程序的一般使用流程(点击放大)

创建和安装驱动程序

首先,您需要安装异步 CRC 驱动程序。该驱动程序根据芯片的功能支持 AHB-GDMA 和 AXI-GDMA 后端:

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));

备注

选择 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 计算:

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:

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, &params, 1000, &crc_result));

printf("CRC 结果: 0x%08X\n", crc_result);

阻塞 API 支持:

  • timeout_ms >= 0:等待指定的毫秒数

  • timeout_ms < 0:无限期等待

卸载驱动程序

当不再需要驱动程序时:

ESP_ERROR_CHECK(esp_async_crc_uninstall(crc_hdl));

如果存在挂起的操作或 CRC 引擎正忙,卸载函数将返回 ESP_ERR_INVALID_STATE。请在卸载前确保所有操作已完成。

CRC 参数配置

异步 CRC 驱动程序通过 async_crc_params_t 结构支持灵活的 CRC 算法配置。

CRC 宽度

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)

多项式

async_crc_params_t::polynomial 字段以十六进制格式指定 CRC 多项式。常见的多项式值包括:

  • CRC-32: 0x04C11DB7

  • CRC-16/CCITT: 0x1021

  • CRC-16/IBM: 0x8005

  • CRC-8/MAXIM: 0x31

初始值

async_crc_params_t::init_value 字段设置处理前的初始 CRC 值。常见的初始值:

  • 0xFFFFFFFF 用于 CRC-32

  • 0x0000 用于许多 CRC-16 变体

  • 0x00 用于许多 CRC-8 变体

最终 XOR 值

async_crc_params_t::final_xor_value 字段指定在与最终 CRC 结果进行异或之前的值。这通常是 CRC-32 的 0xFFFFFFFF,但对于某些变体可以是 0x0000

位反转选项

这些选项影响不同 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 都不能从中断上下文调用。具体来说:

回调限制

回调函数(async_crc_isr_cb_t)在中断上下文中执行。因此:

  • 不要执行阻塞操作(如 vTaskDelay、带超时的 xQueueSend

  • 保持执行时间最小化,以免影响系统中断延迟

  • 不要使用 malloc 或类似函数分配内存

  • 只使用 ISR 安全的 FreeRTOS API(如 xQueueSendFromISR, xSemaphoreGiveFromISR

  • 如果回调唤醒了高优先级任务,返回 true

使用队列的回调示例:

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)中。驱动程序自动处理两者:

// 内部 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 控制器功能。

API 参考

异步 CRC 驱动程序函数

Header File

  • components/esp_driver_dma/include/esp_async_crc.h

  • This header file can be included with:

    #include "esp_async_crc.h"
    
  • This header file is a part of the API provided by the esp_driver_dma component. To declare that your component depends on esp_driver_dma, add the following to your CMakeLists.txt:

    REQUIRES esp_driver_dma
    

    or

    PRIV_REQUIRES esp_driver_dma
    

Functions

esp_err_t esp_async_crc_uninstall(async_crc_handle_t crc_hdl)

Uninstall async CRC driver.

参数:

crc_hdl -- [in] Handle of async CRC driver that returned from install functions

返回:

  • ESP_OK: Uninstall async CRC driver successfully

  • ESP_ERR_INVALID_ARG: Uninstall async CRC driver failed because of invalid argument

  • ESP_FAIL: Uninstall async CRC driver failed because of other error

esp_err_t esp_async_crc_calc(async_crc_handle_t crc_hdl, const void *data, size_t size, const async_crc_params_t *params, async_crc_isr_cb_t cb_isr, void *cb_args)

Send an asynchronous CRC calculation request.

备注

The callback function is invoked in interrupt context, never do blocking jobs in the callback.

参数:
  • crc_hdl -- [in] Handle of async CRC driver that returned from install functions

  • data -- [in] Pointer to data buffer for CRC calculation

  • size -- [in] Size of data in bytes

  • params -- [in] CRC calculation parameters

  • cb_isr -- [in] Callback function, which got invoked in interrupt context. Set to NULL can bypass the callback.

  • cb_args -- [in] User defined argument to be passed to the callback function

返回:

  • ESP_OK: Send CRC calculation request successfully

  • ESP_ERR_INVALID_ARG: Send CRC calculation request failed because of invalid argument

  • ESP_ERR_INVALID_STATE: CRC driver is not in proper state to accept new requests

  • ESP_FAIL: Send CRC calculation request failed because of other error

esp_err_t esp_crc_calc_blocking(async_crc_handle_t crc_hdl, const void *data, size_t size, const async_crc_params_t *params, int32_t timeout_ms, uint32_t *result)

Blocking CRC calculation function with timeout.

备注

This function is blocking and should not be called from interrupt context.

参数:
  • crc_hdl -- [in] Handle of async CRC driver that returned from install functions

  • data -- [in] Pointer to data buffer for CRC calculation

  • size -- [in] Size of data in bytes

  • params -- [in] CRC calculation parameters

  • timeout_ms -- [in] Timeout in milliseconds:

    • < 0: Wait forever (no timeout)

    • 0: Return immediately (poll once)

    • > 0: Wait up to specified milliseconds

  • result -- [out] Pointer to store CRC calculation result

返回:

  • ESP_OK: Calculate CRC successfully

  • ESP_ERR_INVALID_ARG: Calculate CRC failed because of invalid argument

  • ESP_ERR_INVALID_STATE: Function called from ISR context or driver in invalid state

  • ESP_ERR_TIMEOUT: Operation timed out

  • ESP_FAIL: Calculate CRC failed because of other error

Structures

struct async_crc_event_data_t

Async CRC event data.

Public Members

uint32_t crc_result

CRC calculation result

struct async_crc_config_t

Type of async CRC configuration.

Public Members

uint32_t backlog

Maximum number of pending CRC requests that can be queued per driver instance. Higher values use more memory but provide better throughput for bursty workloads.

size_t dma_burst_size

DMA transfer burst size, in bytes

struct async_crc_params_t

CRC calculation parameters.

Public Members

uint8_t width

CRC bit width: 8, 16, or 32 bits

uint32_t polynomial

CRC polynomial

uint32_t init_value

Initial CRC value

uint32_t final_xor_value

Final XOR value

bool reverse_input

Reverse data bits before processing

bool reverse_output

Reverse final CRC result

Type Definitions

typedef struct async_crc_context_t *async_crc_handle_t

Async CRC driver handle.

typedef bool (*async_crc_isr_cb_t)(async_crc_handle_t crc_hdl, async_crc_event_data_t *edata, void *cb_args)

Type of async CRC interrupt callback function.

备注

User can call OS primitives (semaphore, mutex, etc) in the callback function. Keep in mind, if any OS primitive wakes high priority task up, the callback should return true.

备注

This callback function is invoked in interrupt context (ISR). The following restrictions apply:

  • Do not perform blocking operations (e.g., vTaskDelay, xQueueSend with non-zero timeout)

  • Keep execution time minimal to avoid impacting system interrupt latency

  • Avoid calling non-ISR-safe FreeRTOS functions

  • Do not allocate memory or perform heavy computations

  • Use only ISR-safe APIs (xQueueSendFromISR, xSemaphoreGiveFromISR, etc.)

Param crc_hdl:

Handle of async CRC

Param edata:

Event data object, which contains related data for this event

Param cb_args:

User defined arguments, passed from esp_async_crc_calc function

Return:

Whether a high priority task is woken up by the callback function


此文档对您有帮助吗?