Merge branch 'feat/async_crc_example' into 'master'

feat(crc): added async crc console example (your terminal CRC calculator 🧮)

See merge request espressif/esp-idf!46084
This commit is contained in:
morris
2026-02-27 18:59:36 +08:00
17 changed files with 371 additions and 19 deletions
@@ -332,6 +332,11 @@ The ``dma_burst_size`` affects DMA transfer efficiency:
The optimal value depends on your chip's DMA controller capabilities.
Application Examples
====================
- :example:`peripherals/dma/async_crc` demonstrates how to use the Async CRC driver through an interactive console CLI.
API Reference
=============
@@ -332,6 +332,11 @@ DMA 突发大小
最佳值取决于芯片的 DMA 控制器功能。
应用示例
========
- :example:`peripherals/dma/async_crc` 演示了如何通过交互式控制台 CLI 使用异步 CRC 驱动程序。
API 参考
========
@@ -95,6 +95,13 @@ examples/peripherals/dac/dac_cosine_wave:
- esp_driver_dac
- soc
examples/peripherals/dma/async_crc:
disable:
- if: SOC_GDMA_SUPPORT_CRC != 1
depends_components:
- esp_driver_dma
- esp_console
examples/peripherals/gpio:
depends_components:
- esp_driver_gpio
@@ -151,6 +151,4 @@ I (8291) sensor_init: Format in use:DVP_8bit_20Minput_RGB565_640x480_6fps
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
@@ -151,6 +151,4 @@ I (2609) sensor_init: Format in use:DVP_8bit_20Minput_RAW8_1024x600_15fps
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
@@ -134,6 +134,4 @@ I (408) dvp_spi_lcd: Screen lit up now!
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
@@ -152,6 +152,4 @@ This image is also used as a reference, you can check output image after ISP aut
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/camera_driver.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
idf_build_set_property(MINIMAL_BUILD ON)
project(async_crc)
@@ -0,0 +1,69 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |
# Async CRC Console Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example demonstrates how to use the Async CRC driver (`esp_async_crc.h`) through an interactive console CLI. The Async CRC driver provides hardware-accelerated CRC calculation using the General DMA (GDMA) peripheral.
The example provides a `crc` command that allows users to:
- Calculate CRC-8, CRC-16, and CRC-32 checksums
- Customize CRC parameters (polynomial, initial value, final XOR, bit reversal)
- Test various CRC algorithms interactively
## Hardware Required
Any board with a supported ESP target that mentioned in the above table can be used.
## Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
### Check help
```bash
crc> help
crc [--width=<width>] [--poly=<hex>] [--init=<hex>] [--xor=<hex>] [--reverse-input=<0|1>] [--reverse-output=<0|1>] <data>
Calculate CRC checksum using hardware async CRC driver
--width=<width> CRC width (8, 16, or 32). Default: 8
--poly=<hex> CRC polynomial in hex. Default: 0x07
--init=<hex> Initial CRC value in hex. Default: 0x00
--xor=<hex> Final XOR value in hex. Default: 0x00
--reverse-input=<0|1> Reverse input bits (0 or 1). Default: 0
--reverse-output=<0|1> Reverse output bits (0 or 1). Default: 0
<data> Input data string
```
### Calculate CRC-8 (default)
```bash
crc> crc "test"
CRC result: 0xB9
```
### Calculate CRC-16/CCITT
```bash
crc> crc --width 16 --poly 0x1021 "test"
CRC result: 0x9B06
```
### Calculate CRC-32
```bash
crc> crc --width 32 --poly 0x04C11DB7 --init 0xFFFFFFFF --xor 0xFFFFFFFF --reverse-input 1 --reverse-output 1 "test"
CRC result: 0xD87F7E0C
```
## Troubleshooting
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
@@ -0,0 +1,4 @@
idf_component_register(SRCS "async_crc_example_main.c"
"cmd_crc.c"
PRIV_REQUIRES console esp_driver_dma
INCLUDE_DIRS ".")
@@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_console.h"
#include "cmd_crc.h"
void app_main(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.prompt = "crc>";
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
// initialize the CRC engine and register the CRC command
register_crc();
printf("\n ==============================================================\n");
printf(" Steps to Use CRC \n");
printf(" \n");
printf(" 1. Try 'help', check all supported commands \n");
printf(" 2. Try 'crc \"test\"' to calculate the default 8-bit CRC \n");
printf(" 3. Or customize: 'crc --width=16 --poly=0x1021 \"test\"' \n");
printf(" \n");
printf(" ==============================================================\n\n");
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}
@@ -0,0 +1,140 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include "argtable3/argtable3.h"
#include "esp_console.h"
#include "esp_log.h"
#include "esp_async_crc.h"
#include "cmd_crc.h"
static const char *TAG = "crc";
static async_crc_handle_t s_crc_handle = NULL;
static struct {
struct arg_int *width; /*!< CRC width (8, 16, or 32) */
struct arg_str *poly; /*!< CRC polynomial (hex string) */
struct arg_str *init; /*!< Initial CRC value (hex string) */
struct arg_str *xor; /*!< Final XOR value (hex string) */
struct arg_int *reverse_input; /*!< Reverse input bits */
struct arg_int *reverse_output; /*!< Reverse output bits */
struct arg_str *data; /*!< Input data string */
struct arg_end *end; /*!< argtable end marker */
} crc_args;
static uint32_t parse_hex_value(const char *str)
{
uint32_t value = 0;
if (str && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
value = (uint32_t)strtoul(str, NULL, 16);
} else {
value = (uint32_t)strtoul(str, NULL, 0);
}
return value;
}
static int do_crc_cmd(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&crc_args);
if (nerrors != 0) {
arg_print_errors(stderr, crc_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
if (!s_crc_handle) {
ESP_LOGE(TAG, "CRC driver not initialized");
return ESP_ERR_INVALID_STATE;
}
// By default, calculate an 8-bit CRC with polynomial 0x07, initial value 0x00, and final XOR value 0x00, without bit reversal
async_crc_params_t params = {
.width = 8,
.polynomial = 0x07,
.init_value = 0x00,
.final_xor_value = 0x00,
.reverse_input = false,
.reverse_output = false,
};
if (crc_args.width->count) {
int width = crc_args.width->ival[0];
if (width != 8 && width != 16 && width != 32) {
ESP_LOGE(TAG, "Invalid width %d. Must be 8, 16, or 32", width);
return ESP_ERR_INVALID_ARG;
}
params.width = width;
}
if (crc_args.poly->count) {
params.polynomial = parse_hex_value(crc_args.poly->sval[0]);
}
if (crc_args.init->count) {
params.init_value = parse_hex_value(crc_args.init->sval[0]);
}
if (crc_args.xor->count) {
params.final_xor_value = parse_hex_value(crc_args.xor->sval[0]);
}
if (crc_args.reverse_input->count) {
params.reverse_input = crc_args.reverse_input->ival[0] ? true : false;
}
if (crc_args.reverse_output->count) {
params.reverse_output = crc_args.reverse_output->ival[0] ? true : false;
}
const char *data_str = crc_args.data->sval[0];
size_t data_len = strlen(data_str);
uint32_t result = 0;
esp_err_t err = esp_crc_calc_blocking(s_crc_handle, data_str, data_len, &params, -1, &result);
if (err != ESP_OK) {
ESP_LOGE(TAG, "CRC calculation failed: %s", esp_err_to_name(err));
return err;
}
if (params.width == 8) {
printf("CRC result: 0x%02" PRIX32 "\n", result);
} else if (params.width == 16) {
printf("CRC result: 0x%04" PRIX32 "\n", result);
} else {
printf("CRC result: 0x%08" PRIX32 "\n", result);
}
return ESP_OK;
}
void register_crc(void)
{
async_crc_config_t config = {
.backlog = 1, // backlog of 1 is sufficient since we wait for each calculation to finish before starting the next one
.dma_burst_size = 32,
};
ESP_ERROR_CHECK(esp_async_crc_install_gdma_ahb(&config, &s_crc_handle));
crc_args.width = arg_int0(NULL, "width", "<width>", "CRC width (8, 16, or 32). Default: 8");
crc_args.poly = arg_str0(NULL, "poly", "<hex>", "CRC polynomial in hex. Default: 0x07");
crc_args.init = arg_str0(NULL, "init", "<hex>", "Initial CRC value in hex. Default: 0x00");
crc_args.xor = arg_str0(NULL, "xor", "<hex>", "Final XOR value in hex. Default: 0x00");
crc_args.reverse_input = arg_int0(NULL, "reverse-input", "<0|1>", "Reverse input bits (0 or 1). Default: 0");
crc_args.reverse_output = arg_int0(NULL, "reverse-output", "<0|1>", "Reverse output bits (0 or 1). Default: 0");
crc_args.data = arg_str1(NULL, NULL, "<data>", "Input data string");
crc_args.end = arg_end(7);
const esp_console_cmd_t crc_cmd = {
.command = "crc",
.help = "Calculate CRC checksum using hardware async CRC driver",
.hint = NULL,
.func = &do_crc_cmd,
.argtable = &crc_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&crc_cmd));
}
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void register_crc(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from crc import Calculator
from crc import Configuration
from pytest_embedded_idf import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
EXPECT_TIMEOUT = 20
def calculate_crc8(data: str) -> int:
config = Configuration(8, 0x07, init_value=0x00, final_xor_value=0x00, reverse_input=False, reverse_output=False)
calculator = Calculator(config)
return int(calculator.checksum(data.encode('utf-8')))
def calculate_crc16_ccitt(data: str) -> int:
config = Configuration(
16, 0x1021, init_value=0x0000, final_xor_value=0x0000, reverse_input=False, reverse_output=False
)
calculator = Calculator(config)
return int(calculator.checksum(data.encode('utf-8')))
def calculate_crc32(data: str) -> int:
config = Configuration(
32, 0x04C11DB7, init_value=0xFFFFFFFF, final_xor_value=0xFFFFFFFF, reverse_input=True, reverse_output=True
)
calculator = Calculator(config)
return int(calculator.checksum(data.encode('utf-8')))
def calculate_crc32_no_reflect(data: str) -> int:
config = Configuration(
32, 0x04C11DB7, init_value=0x00000000, final_xor_value=0x0000, reverse_input=False, reverse_output=False
)
calculator = Calculator(config)
return int(calculator.checksum(data.encode('utf-8')))
@pytest.mark.generic
@idf_parametrize('target', soc_filtered_targets('SOC_GDMA_SUPPORT_CRC == 1'), indirect=['target'])
def test_async_crc_example(dut: IdfDut) -> None:
dut.expect_exact('crc>', timeout=EXPECT_TIMEOUT)
# Test CRC-8 (default)
dut.write('crc "test"')
expected_crc8 = calculate_crc8('test')
dut.expect(f'CRC result: 0x{expected_crc8:02X}', timeout=EXPECT_TIMEOUT)
# Test CRC-8 with explicit polynomial
dut.write('crc --width 8 --poly 0x07 "test"')
expected_crc8 = calculate_crc8('test')
dut.expect(f'CRC result: 0x{expected_crc8:02X}', timeout=EXPECT_TIMEOUT)
# Test CRC-16/CCITT
dut.write('crc --width 16 --poly 0x1021 "test"')
expected_crc16 = calculate_crc16_ccitt('test')
dut.expect(f'CRC result: 0x{expected_crc16:04X}', timeout=EXPECT_TIMEOUT)
# Test CRC-32 without reflection
dut.write('crc --width 32 --poly 0x04C11DB7 --init 0 --xor 0 --reverse-input 0 --reverse-output 0 "test"')
expected_crc32 = calculate_crc32_no_reflect('test')
dut.expect(f'CRC result: 0x{expected_crc32:08X}', timeout=EXPECT_TIMEOUT)
# Test CRC-32 with reflection (standard CRC-32)
dut.write(
'crc --width 32 --poly 0x04C11DB7 --init 0xFFFFFFFF --xor 0xFFFFFFFF '
'--reverse-input 1 --reverse-output 1 "test"'
)
expected_crc32 = calculate_crc32('test')
dut.expect(f'CRC result: 0x{expected_crc32:08X}', timeout=EXPECT_TIMEOUT)
@@ -203,7 +203,5 @@ This image is also used as a reference, you can check output image without ISP a
## Reference
- Link to the ESP-IDF camera controller driver API reference, [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
- Link to the ESP-IDF ISP driver API reference, [ESP-IDF: Image Signal Processor](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/isp.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Camera Controller Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/camera_driver.html)
- [ESP-IDF: Image Signal Processor](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/isp.html)
+1 -3
View File
@@ -123,6 +123,4 @@ I (10085) main_task: Returned from app_main()
## Reference
- Link to the ESP-IDF feature's API reference, for example [ESP-IDF: Pixel-Processing Accelerator](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ppa.html)
- [ESP-IDF Getting Started](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html#get-started)
- [Project Configuration](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/kconfig.html) (Kconfig Options)
- [ESP-IDF: Pixel-Processing Accelerator](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/ppa.html)
@@ -18,3 +18,6 @@ pyecharts
# for twai tests, communicate with socket can device (e.g. Canable)
python-can
# Calculate CRC checksums
crc