# 坐标旋转数字计算方法 (CORDIC)

[English]

## 概述

CORDIC 是一种坐标旋转数字计算方法，用于计算三角函数、双曲函数和反正切函数等数学运算。CORDIC 算法通过迭代旋转和缩放操作，将复杂的数学运算转换为简单的加法和移位操作，从而实现高效的计算。

CORDIC 主要通过硬件进行计算，其特点主要包括：

- 计算速度快

- 计算精度高

- 计算资源消耗低

下表对比了使用 CORDIC 驱动和软件 iqmath 库计算 1 个随机值的余弦值的性能，单位 CPU 周期。

函数 | iqmath | cordic
--- | --- | ---
cos | 29640 | 8080

## 驱动介绍

在使用 CORDIC 驱动之前，需要先了解一些关于该驱动的计算方法及参数含义。

### 计算方法

CORDIC 驱动所计算的函数通过 `cordic_calculate_config_t::function` 参数进行选择，其支持的函数如下表所示。其中，有部分函数需要两个参数，有部分函数需要一个参数。有部分函数可以生成两个结果，有部分函数只能生成一个结果。

此外，CORDIC 驱动还可以通过设置 `cordic_calculate_config_t::scale_exp` 参数来设置输入数据的缩放因子（有效缩放为 \(2^n\)），在下面表格中用 \(n\) 表示该参数的值。

\(arg_{1}\) 和 \(arg_{2}\) 表示输入数据，分别对应 `cordic_input_buffer_desc_t::p_data_arg1` 和 `cordic_input_buffer_desc_t::p_data_arg2` ，\(res_{1}\) 和 \(res_{2}\) 表示输出数据，分别对应 `cordic_output_buffer_desc_t::p_data_res1` 和 `cordic_output_buffer_desc_t::p_data_res2`。其对应关系如下表所示。

CORDIC 支持函数及公式
函数 | 输入 | 输出 | 可缩放指数 \(n\) | 输入输出范围（缩放为1时）
--- | --- | --- | --- | ---
`ESP_CORDIC_FUNC_COS` 余弦 | \(\frac{\theta}{\pi}, \quad \theta \text{ in radians}\) | \(res_1 = \cos(\theta)\), \(res_2 = \sin(\theta)\) | 0 | \(arg_1, res_1, res_2 \in \left[-1, 1\right)\)
`ESP_CORDIC_FUNC_SIN` 正弦 | \(\frac{\theta}{\pi}, \quad \theta \text{ in radians}\) | \(res_1 = \sin(\theta)\)，\(res_2 = \cos(\theta)\) | 0 | \(arg_1, res_1, res_2 \in \left[-1, 1\right)\)
`ESP_CORDIC_FUNC_PHASE` 相位 | \(arg_1 = x \cdot 2^{-n}\)，\(arg_2 = y \cdot 2^{-n}\) | \(res_1 = \arctan(y/x) \cdot 2^{-n}/\pi\)，\(res_2 = \sqrt{x^2+y^2} \cdot 2^{-n}\) | [0, 15] | \(arg_{1,2} \in \left[-1, 1\right)\)，\(res_1 \in \left[-1, 1\right)\)，\(res_2 \in \left[0, 1\right)\)
`ESP_CORDIC_FUNC_MODULUS` 模长 | \(arg_1 = x \cdot 2^{-n}\)，\(arg_2 = y \cdot 2^{-n}\) | \(res_1 = \sqrt{x^2+y^2} \cdot 2^{-n}\)，\(res_2 = \arctan(y/x) \cdot 2^{-n}/\pi\) | [0, 15] | \(arg_{1,2} \in \left[-1, 1\right)\)，\(res_1 \in \left[0, 1\right)\)，\(res_2 \in \left[-1, 1\right)\)
`ESP_CORDIC_FUNC_ARCTAN` 反正切 | \(arg_1 = x \cdot 2^{-n}\) | \(res_1 = \arctan(x) \cdot 2^{-n}/\pi\) | [0, 15] | \(arg_1, res_1 \in \left[-1, 1\right)\)
`ESP_CORDIC_FUNC_COSH` 双曲余弦 | \(arg_1 = x \cdot 2^{-n}\) | \(res_1 = \cosh(x) \cdot 2^{-n}\)，\(res_2 = \sinh(x) \cdot 2^{-n}\) | 1 | \(arg_1 \in \left[-0.559, 0.559\right]\)，\(res_1 \in \left[0.5, 0.846\right]\)，\(res_2 \in \left[-0.683, 0.683\right]\)
`ESP_CORDIC_FUNC_SINH` 双曲正弦 | \(arg_1 = x \cdot 2^{-n}\) | \(res_1 = \sinh(x) \cdot 2^{-n}\)，\(res_2 = \cosh(x) \cdot 2^{-n}\) | 1 | \(arg_1 \in \left[-0.559, 0.559\right]\)，\(res_1 \in \left[-0.683, 0.683\right]\)，\(res_2 \in \left[0.5, 0.846\right]\)
`ESP_CORDIC_FUNC_ARCHTANH` 反双曲正切 | \(arg_1 = x \cdot 2^{-n}\) | \(res_1 = \mathrm{arctanh}(x) \cdot 2^{-n}\) | 1 | \(arg_1 \in \left[-0.403, 0.403\right]\)，\(res_1 \in \left[-0.559, 0.559\right]\)
`ESP_CORDIC_FUNC_LOGE` 自然对数 | \(arg_1 = (x-1)/(x+1) \cdot 2^{-1}\) | \(res_1 = \ln(x) \cdot 2^{-2}\) | 1 | \(arg_1 \in \left[-0.403, 0.403\right]\)，\(res_1 \in \left[-0.559, 0.559\right]\)
`ESP_CORDIC_FUNC_SQUARE_ROOT` 平方根 | \(arg_1 = x \cdot 2^{-n}\) | \(res_1 = \sqrt{x} \cdot 2^{-n}\) | [0, 4] | \(arg_1 \in \left[0.1069, 1\right)\)，\(res_1 \in \left[0.177, 1\right)\)

### 数据格式

CORDIC 驱动 通过 `cordic_calculate_config_t::iq_format` 参数来选择输入和输出数据的格式，其支持的格式如下所示。

- `ESP_CORDIC_FORMAT_Q15` Q15 格式

在 Q15 格式下，数据由一个符号位加15位小数位组成，其范围为 [-1(0x8000), 1 - 2^-15(0x7FFF)]。

Q15 格式

\[value = \sum_{i=0}^{14} \frac{1}{2^{(15-i)}} \times B_i\]

- `ESP_CORDIC_FORMAT_Q31` Q31 格式

在 Q31 格式下，数据由一个符号位加31位小数位组成，其范围为 [-1(0x80000000), 1 - 2^-31(0x7FFFFFFF)]。

Q31 格式

\[value = \sum_{i=0}^{30} \frac{1}{2^{(31-i)}} \times B_i\]

### 精度

CORDIC 驱动通过 `cordic_calculate_config_t::iteration_count` 参数来设置计算的迭代周期（精度），其支持的精度如下所示。

Phase, Modulus, arctan 函数

函数 | 迭代次数 | 周期数 | 最大残差误差 (q1.31格式) | 最大残差误差 (q1.15格式)
--- | --- | --- | --- | ---
Phase, Modulus, arctan | 4 | 1 | \(2^{-4}\) | \(2^{-4}\)
8 | 2 | \(2^{-8}\) | \(2^{-8}\)
12 | 3 | \(2^{-12}\) | \(2^{-12}\)
16 | 4 | \(2^{-16}\) | \(2^{-15}\)
20 | 5 | \(2^{-20}\) | \(2^{-15}\)
24 | 6 | \(2^{-24}\) | \(2^{-15}\)

sin, cos, sinh, cosh, arctanh, ln 函数

函数 | 迭代次数 | 周期数 | 最大残差误差 (q1.31格式) | 最大残差误差 (q1.15格式)
--- | --- | --- | --- | ---
sin, cos, sinh, cosh, arctanh, ln | 4 | 1 | \(2^{-3}\) | \(2^{-3}\)
8 | 2 | \(2^{-7}\) | \(2^{-7}\)
12 | 3 | \(2^{-11}\) | \(2^{-11}\)
16 | 4 | \(2^{-14}\) | \(2^{-14}\)
20 | 5 | \(2^{-18}\) | \(2^{-15}\)
24 | 6 | \(2^{-22}\) | \(2^{-15}\)

Sqrt 函数

函数 | 迭代次数 | 周期数 | 最大残差误差 (q1.31格式) | 最大残差误差 (q1.15格式)
--- | --- | --- | --- | ---
Sqrt | 4 | 1 | \(2^{-7}\) | \(2^{-7}\)
8 | 2 | \(2^{-14}\) | \(2^{-14}\)
12 | 3 | \(2^{-22}\) | \(2^{-15}\)

备注

迭代周期越多，计算精度越高，但计算时间也会越长。当运算精度到达数据格式所能表达的精度极限时，继续增加迭代周期将不会产生更好的效果，反而会使时间变长。

## 快速入门

本节将带你快速了解如何使用 CORDIC 驱动。通过实际的使用场景，展示如何创建 CORDIC 引擎并进行计算。一般的使用流程如下：

### 创建 CORDIC 引擎

首先，需要创建一个 CORDIC 引擎。以下代码展示了如何创建一个 CORDIC 引擎：

    cordic_engine_handle_t engine = NULL;
    cordic_engine_config_t config = {
        .clock_source = CORDIC_CLK_SRC_DEFAULT,  // 选择时钟源
    };
    ESP_ERROR_CHECK(cordic_new_engine(&config, &engine));

### 使用 CORDIC 引擎进行计算

使用 CORDIC 引擎进行计算的流程如下：

    cordic_calculate_config_t calc_config = {
        .function = ESP_CORDIC_FUNC_COS,           // 选择计算的函数
        .iq_format = ESP_CORDIC_FORMAT_Q15,       // 选择输入和输出数据的格式
        .iteration_count = 4,                     // 选择计算的迭代周期（精度）
        .scale_exp = 0,                           // 输入缩放指数 n，有效缩放为 2^n
    };

    size_t buffer_depth = 60;

    uint32_t data_x[buffer_depth] = {};  // 输入数据1，长度为60
    uint32_t data_y[buffer_depth] = {};  // 输入数据2，长度为60

    uint32_t res1[buffer_depth] = {};  // 输出数据1，长度为60
    uint32_t res2[buffer_depth] = {};  // 输出数据2，长度为60

    // 配置输入数据
    cordic_input_buffer_desc_t input_buffer = {
        .p_data_arg1 = data_x,   // 输入数据1
        .p_data_arg2 = data_y,   // 输入数据2
    };

    // 配置输出数据
    cordic_output_buffer_desc_t output_buffer = {
        .p_data_res1 = res1,   // 输出数据1
        .p_data_res2 = res2,   // 输出数据2
    };

    // 开始计算
    ESP_ERROR_CHECK(cordic_calculate_polling(engine, &calc_config, &input_buffer, &output_buffer, buffer_depth));

### 格式转换

CORDIC 驱动中提供了定点数与浮点数之间的转换函数，用户可以通过 `cordic_convert_fixed_to_float()` 和 `cordic_convert_float_to_fixed()` 函数实现定点数与浮点数之间的转换。

    // 将定点数转换为浮点数
    // res1[0] res2[0] 为 cordic 输出的定点数
    float res1_float = cordic_convert_fixed_to_float(res1[0], ESP_CORDIC_FORMAT_Q15);
    float res2_float = cordic_convert_fixed_to_float(res2[0], ESP_CORDIC_FORMAT_Q15);

    // 将浮点数转换为定点数
    uint32_t res1_fixed = cordic_convert_float_to_fixed(res1_float, ESP_CORDIC_FORMAT_Q15);
    uint32_t res2_fixed = cordic_convert_float_to_fixed(res2_float, ESP_CORDIC_FORMAT_Q15);

### 资源回收

当不再需要之前安装的 CORDIC 引擎，请调用 `cordic_delete_engine()` 来回收资源，以释放底层硬件。

    ESP_ERROR_CHECK(cordic_delete_engine(engine));

## 高级功能

### 线程安全

CORDIC 驱动程序的以下函数是线程安全的，可以从不同的 RTOS 任务调用，无需额外的锁保护：

工厂函数：

- `cordic_new_engine()`

- `cordic_delete_engine()`

备注

请注意 `cordic_calculate_polling()` 函数是非线程安全的，在没有设置互斥锁保护的任务中，应避免从多个任务中调用此函数。

### Cache 安全

`cordic_calculate_polling()` 函数可以安全地在 ISR 中使用，但为了保证 ISR 在 cache 被禁用时也能正常运行，请启用 Kconfig 选项 CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM，将控制函数放入 IRAM 中。

### Kconfig 选项

以下 Kconfig 选项可用于配置 CORDIC 驱动：

- CONFIG_CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM - 计算函数放入 IRAM。

## API 参考

### CORDIC 驱动 API

#### Header File

- components/esp_driver_cordic/include/driver/cordic.h

- This header file can be included with:

  >     #include "driver/cordic.h"

- This header file is a part of the API provided by the `esp_driver_cordic` component. To declare that your component depends on `esp_driver_cordic`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_cordic

  > or

  >     PRIV_REQUIRES esp_driver_cordic

#### Functions

esp_err_t cordic_new_engine(const cordic_engine_config_t *cordic_cfg, cordic_engine_handle_t *ret_engine)

  Create a new CORDIC engine instance.

  This function creates and initializes a CORDIC engine with the specified configuration. The engine handle returned can be used for subsequent CORDIC calculations.

  参数:

    - cordic_cfg -- [in] Pointer to CORDIC engine configuration structure. Must not be NULL.

    - ret_engine -- [out] Pointer to store the created CORDIC engine handle. Must not be NULL.

  返回:

    - ESP_OK: CORDIC engine created successfully.

    - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer or invalid scale exponent).

    - ESP_ERR_NO_MEM: Failed to allocate memory for the engine.

esp_err_t cordic_calculate_polling(cordic_engine_handle_t engine, const cordic_calculate_config_t *calc_cfg, cordic_input_buffer_desc_t *input_buffer_desc, cordic_output_buffer_desc_t *output_buffer_desc, size_t buffer_depth)

  Start one-shot CORDIC calculation.

  This function performs CORDIC calculations on a batch of data points. It processes each data point sequentially: sets arguments, starts calculation, waits for completion, and retrieves results.

  备注

  The function can be safely used in ISR, but for the ISR run in cache-safe, please enable `CORDIC_ONESHOT_CTRL_FUNC_IN_IRAM` to place the control functions into IRAM.

  备注

  This function is not guaranteed to be thread-safe.

  参数:

    - engine -- [in] CORDIC engine handle created by cordic_new_engine(). Must not be NULL.

    - calc_cfg -- [in] Pointer to CORDIC calculation configuration structure. Must not be NULL.

    - input_buffer_desc -- [in] Pointer to input buffer descriptor containing input data. Must not be NULL.

    - output_buffer_desc -- [out] Pointer to output buffer descriptor for storing results. Must not be NULL.

    - buffer_depth -- [in] Number of data points to process.

  返回:

    - ESP_OK: All calculations completed successfully.

    - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer, zero buffer_depth, or invalid timeout).

    - ESP_ERR_TIMEOUT: Calculation timeout.

esp_err_t cordic_delete_engine(cordic_engine_handle_t engine)

  Delete a CORDIC engine instance.

  This function deletes a CORDIC engine and frees all associated resources including the mutex. After calling this function, the engine handle becomes invalid and should not be used.

  参数:

    engine -- [in] CORDIC engine handle to delete. Must not be NULL.

  返回:

    - ESP_OK: Engine deleted successfully.

    - ESP_ERR_INVALID_ARG: Invalid argument (NULL pointer).

float cordic_convert_fixed_to_float(uint32_t fixed_value, cordic_iq_format_t iq_format)

  Convert CORDIC hex value to float.

  This function converts a hexadecimal value from CORDIC hardware output to a floating-point number. The conversion depends on the IQ format specified by the iq_format parameter.

  参数:

    - fixed_value -- [in] Hexadecimal value from CORDIC hardware output.

    - iq_format -- [in] IQ data format.

  返回:

    Converted floating-point value. Returns 0.0f if iq_format is invalid.

uint32_t cordic_convert_float_to_fixed(float float_value, cordic_iq_format_t iq_format)

  Convert float to CORDIC hex value.

  This function converts a floating-point number to a hexadecimal value suitable for CORDIC hardware input. The conversion depends on the IQ format specified by the iq_format parameter.

  参数:

    - float_value -- [in] Floating-point value to convert. Should be in the range [-1.0, 1.0].

    - iq_format -- [in] IQ data format.

  返回:

    Converted hexadecimal value. Returns 0 if iq_format is invalid.

#### Structures

struct cordic_engine_config_t

  CORDIC engine configuration structure.

  Public Members

  cordic_clock_source_t clock_source

    CORDIC clock source

struct cordic_calculate_config_t

  CORDIC calculation configuration structure.

  Public Members

  cordic_func_t function

    CORDIC calculation function type (e.g., cosine, sine, arctan)

  cordic_iq_format_t iq_format

    IQ data format

  uint16_t iteration_count

    Internal CORDIC iteration count

  uint16_t scale_exp

    Input scaling exponent n, effective scaling is 2^n (range depends on function type)

### CORDIC HAL API

#### Header File

- components/esp_hal_cordic/include/hal/cordic_types.h

- This header file can be included with:

  >     #include "hal/cordic_types.h"

- This header file is a part of the API provided by the `esp_hal_cordic` component. To declare that your component depends on `esp_hal_cordic`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_hal_cordic

  > or

  >     PRIV_REQUIRES esp_hal_cordic

#### Structures

struct cordic_input_buffer_desc_t

  Input buffer descriptor for CORDIC operations.

  Public Members

  uint32_t *p_data_arg1

    Pointer to input data argument 1 buffer

  uint32_t *p_data_arg2

    Pointer to input data argument 2 buffer (can be NULL for single argument functions)

struct cordic_output_buffer_desc_t

  Output buffer descriptor for CORDIC operations.

  Public Members

  uint32_t *p_data_res1

    Pointer to output result 1 buffer

  uint32_t *p_data_res2

    Pointer to output result 2 buffer (can be NULL for single result)

#### Type Definitions

typedef soc_periph_cordic_clk_src_t cordic_clock_source_t

  CORDIC group clock source.

#### Enumerations

enum cordic_func_t

  CORDIC calculation function type.

  Values:

  enumerator ESP_CORDIC_FUNC_COS

    Cosine function (can have both cos, sin results)

  enumerator ESP_CORDIC_FUNC_SIN

    Sine function (can have both sin, cos results)

  enumerator ESP_CORDIC_FUNC_PHASE

    Phase function (requires two arguments) (can have both phase, modulus results)

  enumerator ESP_CORDIC_FUNC_MODULUS

    Modulus function (requires two arguments) (can have both phase, modulus results)

  enumerator ESP_CORDIC_FUNC_ARCTAN

    Arctangent function.

  enumerator ESP_CORDIC_FUNC_COSH

    Hyperbolic cosine function (can have both cosh, sinh results)

  enumerator ESP_CORDIC_FUNC_SINH

    Hyperbolic sine function (can have both sinh, cosh results)

  enumerator ESP_CORDIC_FUNC_ARCHTANH

    Hyperbolic arctangent function.

  enumerator ESP_CORDIC_FUNC_LOGE

    Natural logarithm function.

  enumerator ESP_CORDIC_FUNC_SQUARE_ROOT

    Square root function.

  enumerator ESP_CORDIC_FUNC_MAX

    Maximum function type (for boundary checking)

enum cordic_iq_format_t

  CORDIC data format size.

  Values:

  enumerator ESP_CORDIC_FORMAT_Q15

    Q15 format: 16-bit fixed-point (range: -1.0 to 1.0)

  enumerator ESP_CORDIC_FORMAT_Q31

    Q31 format: 32-bit fixed-point (range: -1.0 to 1.0)

  enumerator ESP_CORDIC_IQ_SIZE_MAX

    Maximum size type (for boundary checking)

### CORDIC 类型

#### Header File

- components/esp_driver_cordic/include/driver/cordic_types.h

- This header file can be included with:

  >     #include "driver/cordic_types.h"

- This header file is a part of the API provided by the `esp_driver_cordic` component. To declare that your component depends on `esp_driver_cordic`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_cordic

  > or

  >     PRIV_REQUIRES esp_driver_cordic

#### Type Definitions

typedef struct cordic_engine_t *cordic_engine_handle_t

  CORDIC engine handle type.

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。