坐标旋转数字计算方法 (CORDIC)
概述
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。其对应关系如下表所示。
函数 |
输入 |
输出 |
可缩放指数 \(n\) |
输入输出范围(缩放为1时) |
|---|---|---|---|---|
\(\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)\) |
|
\(\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)\) |
|
\(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)\) |
|
\(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)\) |
|
\(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)\) |
|
|
\(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]\) |
|
\(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]\) |
|
\(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]\) |
|
\(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]\) |
\(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_Q15Q15 格式
在 Q15 格式下,数据由一个符号位加15位小数位组成,其范围为 [-1(0x8000), 1 - 2^-15(0x7FFF)]。
Q15 格式
ESP_CORDIC_FORMAT_Q31Q31 格式
在 Q31 格式下,数据由一个符号位加31位小数位组成,其范围为 [-1(0x80000000), 1 - 2^-31(0x7FFFFFFF)]。
Q31 格式
精度
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_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
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_cordiccomponent. To declare that your component depends onesp_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_IRAMto 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
-
cordic_clock_source_t 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_func_t function
CORDIC HAL API
Header File
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_cordiccomponent. To declare that your component depends onesp_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.
-
struct cordic_output_buffer_desc_t
Output buffer descriptor for CORDIC operations.
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)
-
enumerator ESP_CORDIC_FUNC_COS
-
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)
-
enumerator ESP_CORDIC_FORMAT_Q15
CORDIC 类型
Header File
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_cordiccomponent. To declare that your component depends onesp_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.