3. 算法库差异
3.1 算法库介绍
3.1.1 CMSIS-DSP库
CMSIS-DSP(Cortex Microcontroller Software Interface Standard - Digital Signal Processing)是一套针对ARM Cortex-M系列微控制器优化的软件库,专注于数字信号处理算法。它旨在为嵌入式开发者提供标准化且高度优化的DSP函数集,以便在基于ARM Cortex-M处理器的微控制器上实现高效的信号处理应用。
CMSIS-DSP库包含了广泛的功能,如:
基本数学运算:如复数运算、矩阵操作和统计函数。
快速傅立叶变换(FFT):用于频域分析。
滤波器设计:包括IIR和FIR滤波器,以及用于滤波器系数计算的工具。
谱分析:如功率谱密度估计。
控制算法:如PID控制器的实现。
数据类型支持:提供了固定点和浮点运算的支持,以适应不同性能和资源需求的平台。
使用CMSIS-DSP的好处包括:
代码重用:跨项目和处理器系列的兼容性。
性能优化:经过优化的汇编代码,充分利用处理器特性。
简化移植:标准化的API接口减少平台迁移的工作量。
减少开发时间:提供现成的高质量算法,无需从头开发。
开发者可以免费下载CMSIS-DSP库,并将其集成到自己的项目中,加速产品开发周期,同时确保软件的质量和效率。
3.1.2 C28 Math Library
- Qmath Library定点数学计算库,适用无硬件浮点计算单元的MCU
- iQmath Library高精度数学计算库(32bit实现), 支持定点和浮点
- Fast RTS Library浮点单元快速运行时支持(FASTRTS)库是为支持单精度浮点单元(FPU32)和带三角数学单元(TMU型0)的FPU32或双精度FPU (FPU64)的C2000设备编写的优化数学例程的集合。
3.1.3 ET6000 数学计算支持
支持DSP指令(SIMD指令)
支持单精度FPU单元(部分型号支持双精度)
支持CORDIC三角加速引擎(部分型号支持)
3.1.4 功能对照表
ET6000 |
C2000 |
备注 |
|---|---|---|
CMSIS-DSP 或 C库 |
iQmath |
定点库,软件实现 |
CMSIS-DSP (FPU 单精度) |
FastRTS Library( FPU32) |
浮点库 sin/cos均为查表 +线性插值实现,table size: 512 |
Cordic |
TMU |
三角函数加速引擎 |
3.2 浮点定点格式转换
3.2.1 Q格式定点
C2000 支持从Q1~Q30的定点格式,ET6000 只支持固定的Q64, Q31,Q15,Q7 四种。
定点格式定义如下:
/* C2000 */
typedef long _iq;
typedef long _iq30;
...
typedef long _iq1;
/* ET6000 */
typedef int8_t q7_t;
typedef int16_t q15_t;
typedef int32_t q31_t;
typedef int64_t q63_t;
3.2.2 浮点格式
浮点格式定义如下:
/* C2000 */
float
/* ET6000 */
typedef float float32_t;
typedef double float64_t;
3.2.3 定浮点转换函数对照表
C2000 (N=0:30) |
ET6000 (N=7,15,31) |
说明 |
|---|---|---|
float _IQNtoF(long A) |
void arm_qN_to_float(const q31_t *pSrc,float32_t *pDst,uint32_t blockSize) |
定点转浮点 |
_IQN(A) |
void arm_float_to_qN(const float32_t * pSrc, q31_t * pDst,uint32_t blockSize); |
浮点转定点 |
iqN _IQtoIQN( _iq A) |
void arm_qN_to_q15(const q31_t *pSrc, q15_t *pDst, uint32_t blockSize ); |
定点转浮点 |
详细CMSIS-DSP库格式转换详见头文件:CMSIS-DSP\Include\dsp\support_functions.h
3.3 基本数学函数
C2000 (N=0:30) |
ET6000 (N=7,15,31) |
说明 |
|---|---|---|
iq _IQmpy( _iq A, _iq B) iqN _IQNmpy( _iqN A, _iqN B) |
void arm_mult_q7(const q7_t * pSrcA,const q7_t * pSrcB, q7_t * pDst,uint32_t blockSize); |
定点乘法 |
N/A |
void arm_mult_f32(const float32_t * pSrcA, const float32_t * pSrcB, float32_t * pDst, uint32_t blockSize); |
浮点乘法 |
_IQmpyN N=2,4,8,16,32 |
void arm_shift_q7(const q7_t * pSrc, int8_t shiftBits, q7_t * pDst,uint32_t blockSize); shiftBits is positive value |
shift |
_IQdivN N=2,4,8,16,32 |
void arm_shift_q7(const q7_t * pSrc, int8_t shiftBits, q7_t * pDst,uint32_t blockSize); shiftBits is negative value |
shift |
iq _IQdiv( _iq A, _iq B) iqN _IQNdiv( _iqN A, _iqN B) |
arm_status arm_divide_q31(q31_t numerator, q31_t denominator,q31_t *quotient, int16_t *shift) |
定点除法 |
N/A |
void arm_add_q15(const q15_t * pSrc, q15_t * pDst, uint32_t blockSize); void arm_add_f32(const float32_t * pSrcA, const float32_t * pSrcB, float32_t * pDst,uint32_t blockSize); |
加法 |
N/A |
void arm_abs_q7( const q7_t * pSrc, q7_t * pDst, uint32_t blockSize); void arm_abs_f32( const float32_t* pSrc, float32_t* pDst, uint32_t blockSize); |
ABS |
详细CMSIS-DSP库格式转换详见头文件:CMSIS-DSP\Include\dsp\basic_math_functions.h
3.4 三角函数
C2000 (N=0:30) |
ET6000 (N=7,15,31) |
说明 |
|---|---|---|
iq _IQsin( _iq A) iqN _IQNsin( _iqN A) float32f sin(float32) |
q15_t arm_sin_q15(q15_t x) float32_t arm_sin_f32(float32_t x) CORDIC_FUNCTION_SINE2 |
正弦 |
iq _IQcos( _iq A) iqN _IQNcos( _iqN A) float32 cosf(float32) |
q15_t arm_cos_q15(q15_t x) float32_t arm_cos_f32(float32_t x) CORDIC_FUNCTION_COSINE2 |
余弦 |
iq _IQatan2( _iq A, _iq B) iqN _IQNatan2( _iqN A, _iqN B) float32 atan2f(float32 , float32) |
arm_status arm_atan2_f32(float32_t y,float32_t x,float32_t *result) arm_status arm_atan2_q31(q31_t y,q31_t x,q31_t *result) |
四象限反正切 |
iq _IQatan( _iq A) iqN _IQNatan( _iqN A) |
CORDIC_FUNCTION_ARCTANGENT2 |
反正切 |
iq _IQexp( _iq A) iqN _IQNexp( _iqN A) float32 expf(float32) |
void arm_vexp_f32( const float32_t * pSrc,float32_t * pDst,uint32_t blockSize) |
自然指数 |
_iq _IQlog( _iq A) _iqN _IQNlog( _iqN A) float32 logf(float32) |
void arm_vlog_q15(const q15_t * pSrc,q15_t * pDst,uint32_t blockSize) CORDIC_FUNCTION_NATURAL_LOGARITHM2 |
自然对数 |
iq _IQsqrt( _iq A) iqN _IQNsqrt( _iqN A) float32f sqrt(float32) |
arm_status arm_sqrt_q15(q15_t in, q15_t * pOut) arm_status arm_sqrt_f32(const float32_t in, float32_t * pOut) CORDIC_FUNCTION_SQUAREROOT2 |
平方根 |
详细CMSIS-DSP库格式转换详见头文件:CMSIS-DSP\Include\dsp\fast_math_functions.h
详细CORDIC模块API头文件:sdk\Drivers\ET6x_Driver\Inc\et6x_cordic.h
3.5 PID控制
C2000 (N=0:30) |
ET6000 (N=7,15,31) |
说明 |
|---|---|---|
void sincosf(float32 , float32* ,float32 * )1 |
void arm_sin_cos_f32(float32_t theta, float32_t * pSinVal,float32_t * pCosVal) void arm_sin_cos_q31(q31_t theta, q31_t * pSinVal, q31_t * pCosVal) |
以下函数仅CMSIS-DSP库支持:
arm_pid_f32(Q31)
/**
* @ingroup PID
* @brief Process function for the floating-point PID Control.
* @param[in,out] S is an instance of the floating-point PID Control structure
* @param[in] in input sample to process
* @return processed output sample.
*/
__STATIC_FORCEINLINE float32_t arm_pid_f32(
arm_pid_instance_f32 * S,
float32_t in)
arm_park_f32(Q31)
/**
* @ingroup park
* @brief Floating-point Park transform
* @param[in] Ialpha input two-phase vector coordinate alpha
* @param[in] Ibeta input two-phase vector coordinate beta
* @param[out] pId points to output rotor reference frame d
* @param[out] pIq points to output rotor reference frame q
* @param[in] sinVal sine value of rotation angle theta
* @param[in] cosVal cosine value of rotation angle theta
* @return none
*
* The function implements the forward Park transform.
*
*/
__STATIC_FORCEINLINE void arm_park_f32(
float32_t Ialpha,
float32_t Ibeta,
float32_t * pId,
float32_t * pIq,
float32_t sinVal,
float32_t cosVal)
arm_inv_park_f32(Q31)
/**
* @ingroup inv_park
* @brief Floating-point Inverse Park transform
* @param[in] Id input coordinate of rotor reference frame d
* @param[in] Iq input coordinate of rotor reference frame q
* @param[out] pIalpha points to output two-phase orthogonal vector axis alpha
* @param[out] pIbeta points to output two-phase orthogonal vector axis beta
* @param[in] sinVal sine value of rotation angle theta
* @param[in] cosVal cosine value of rotation angle theta
* @return none
*/
__STATIC_FORCEINLINE void arm_inv_park_f32(
float32_t Id,
float32_t Iq,
float32_t * pIalpha,
float32_t * pIbeta,
float32_t sinVal,
float32_t cosVal)
arm_clarke_f32(Q31)
/**
*
* @ingroup clarke
* @brief Floating-point Clarke transform
* @param[in] Ia input three-phase coordinate <code>a</code>
* @param[in] Ib input three-phase coordinate <code>b</code>
* @param[out] pIalpha points to output two-phase orthogonal vector axis alpha
* @param[out] pIbeta points to output two-phase orthogonal vector axis beta
* @return none
*/
__STATIC_FORCEINLINE void arm_clarke_f32(
float32_t Ia,
float32_t Ib,
float32_t * pIalpha,
float32_t * pIbeta)
arm_inv_clarke_f32(Q31)
/**
* @ingroup inv_clarke
* @brief Floating-point Inverse Clarke transform
* @param[in] Ialpha input two-phase orthogonal vector axis alpha
* @param[in] Ibeta input two-phase orthogonal vector axis beta
* @param[out] pIa points to output three-phase coordinate <code>a</code>
* @param[out] pIb points to output three-phase coordinate <code>b</code>
* @return none
*/
__STATIC_FORCEINLINE void arm_inv_clarke_f32(
float32_t Ialpha,
float32_t Ibeta,
float32_t * pIa,
float32_t * pIb)
3.6 快速傅里叶变换
以下函数仅CMSIS-DSP库支持:
arm_cfft_f32(Q31,Q15)
void arm_cfft_f32(
const arm_cfft_instance_f32 * S,
float32_t * p1,
uint8_t ifftFlag,
uint8_t bitReverseFlag);
void arm_cfft_q15(
const arm_cfft_instance_q15 * S,
q15_t * p1,
uint8_t ifftFlag,
uint8_t bitReverseFlag);
arm_rfft_f32(Q31,Q15)
void arm_rfft_f32(
const arm_rfft_instance_f32 * S,
float32_t * pSrc,
float32_t * pDst);
arm_rfft_fast_f32(Q31,Q15)
void arm_rfft_fast_f32(
const arm_rfft_fast_instance_f32 * S,
float32_t * p, float32_t * pOut,
uint8_t ifftFlag);
详细CMSIS-DSP库格式转换详见头文件:CMSIS-DSP\Include\dsp\controller_functions.h
3.7 移植步骤
获取CMSIS-DSP库源码和库的途径有:
通过ET6000系列提供的SDK软件包内置获取
通过ARM-MDK IDE的包管理插件获取
通过github官网获取:https://github.com/ARM-software/CMSIS-DSP
3.7.1 目录结构
Source(CMSIS-DSP 库源码)
Include(CMSIS-DSP 库头文件)
PrivateInclude(CMSIS-DSP 库内部使用)
ComputeLibrary (ET6000 不支持,可忽略)
其他一些目录为测试代码及示例。
3.7.2 添加库及编译
详细的CMSIS-DSP库添加和编译指南请参考应用笔记CMSIS-DSP库移植指南_V1.0.0.pdf [22]。
用户工程可以通过添加源码的方式引用DSP库,也可以通过直接添加预编译好的库来添加,两种方法各有好处。
引用源代码 |
引用预编译库 |
|---|---|
优势:用户可自行修改优化代码 |
优点:方便开发,工程维护方便 |
缺点:所依赖的源码文件需要逐一添加到工程 |
缺点:不能及时更新,影响优化受限 |
预编译库文件说明
arm_cortexM7lfdp_math.lib (Cortex-M7, Little endian, Double Precision Floating Point Unit)
arm_cortexM7bfdp_math.lib (Cortex-M7, Big endian, Double Precision Floating Point Unit)
arm_cortexM7lfsp_math.lib (Cortex-M7, Little endian, Single Precision Floating Point Unit)
arm_cortexM7bfsp_math.lib (Cortex-M7, Big endian and Single Precision Floating Point Unit on)
默认使用小端库,使用单精度还是双精度由芯片支持的精度确定。
3.8 使用示例
3.8.1 三角函数使用
计算 \(sin^2x+ cos^2x = 1\)
#include "arm_math.h"
#define DELTA (0.0001f)
const float32_t testInput_f32 = -1.244916875853235400;
const float32_t testRefOutput_f32 = 1.000000000;
float32_t testOutput;
float32_t cosOutput;
float32_t sinOutput;
float32_t cosSquareOutput;
float32_t sinSquareOutput;
cosOutput = arm_cos_f32(testInput_f32);
sinOutput = arm_sin_f32(testInput_f32);
arm_mult_f32(&cosOutput, &cosOutput, &cosSquareOutput, 1);
arm_mult_f32(&sinOutput, &sinOutput, &sinSquareOutput, 1);
arm_add_f32(&cosSquareOutput, &sinSquareOutput, &testOutput, 1);
/* absolute value of difference between ref and test */
diff = fabsf(testRefOutput_f32 - testOutput);
/* Comparison of sin_cos value with reference */
status = (diff > DELTA) ? ARM_MATH_TEST_FAILURE : ARM_MATH_SUCCESS;
if ( status == ARM_MATH_TEST_FAILURE)
{
break;
}
3.8.2 CORDIC使用
根据角度计算sin和cos值,CORDIC引擎的使用示例如下:
float arm_sin_res;
float arm_cos_res;
double cordic_res[2];
/* CORDIC initialization */
CORDIC_InitParams init;
CORDIC_initStruct(&init);
init.function = CORDIC_FUNCTION_SINE;
init.scale = CORDIC_SCALE_0;
init.argDataFormat = CORDIC_DATA_FORMAT_BIT32;
init.resDataFormat = CORDIC_DATA_FORMAT_BIT32;
init.numberWrite = CORDIC_ARG_RES_NUM1;
init.numberRead = CORDIC_ARG_RES_NUM2;
init.workMode = CORDIC_WORK_MODE_POLLING;
init.precision = CORDIC_PRECISIO_8;
init.floatDataEn = ENABLE;
CORDIC_init(&init);
// 弧度值除以PI, 因为CORDIC输入角度范围为[-1,1], 代表[-PI,PI]
float arg1_in = 1 / 180.0f;
/* Calculate sine and cosine using ARM CMSIS-DSP functions */
// 输入角度为1度
arm_sin_res = arm_sin_f32(PI/180.0f);
arm_cos_res = arm_cos_f32(PI/180.0f);
/* Calculate sine and cosine using CORDIC */
CORDIC_setFloatArg(&arg1_in);
while((CORDIC_getStatus() & CORDIC_STS_CALCULATE_COMPLETE) == 0);
cordic_res[0] = CORDIC_getFloatResult1(); // sin result
cordic_res[1] = CORDIC_getFloatResult2(); // cos result
3.9 优化指南
软件开发前期,建议保持打开
USE_FULL_ASSERT宏用于检查调用驱动API的参数的合法性,但是程序调试稳定后,应关闭该宏。可有效减少代码尺寸及运行效率。为了获取更好的执行速度,建议开启编译选项
-ofast使用时,请确保MCU的硬浮点处理单元FPU已开启
若使用ET6000 SDK开发,系统初始化函数
SystemInit已开启#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ #endif
为了进一步提升执行速度, 建议将CMSIS-DSP库使用的数据和常量表映射在
DTCM内存中如使用ET6000 SDK开发, 分散加载文件已经预先添加layout, 去掉注释”;”即可
ER_ROM2 0x00000000 0x00008000 { *(.RamFunc) ; place RamFunc to ITCM so speed up execution ; place cmsis-dsp function to ITCM ; arm_common_tables.o(+RO) ; arm_cos_f32.o(+RO) ; arm_sin_f32.o(+RO) }
请确保系统的
ICache和DCache都已经开启针对正余弦函数计算,为了获取更高的精度, 用户可通过matlab生成更大尺寸的表(CMSIS-DSP默认table size:512), 或者使用CORDIC加速引擎
3.10 宏定义头文件
#include <math.h> // libc math include file
#include <arm_math.h> // CMSIS-DSP include file
#include <string.h>
#define SF_MEMSET(pDst,val,size) memset(pDst,val,size)
#define SF_DIVF32(a, b) (float)((doubule)(a)/(doubule)(b))
//#define SF_SQRT(x) sqrtf(x)
#define SF_SQRT(x) \
({ \
float32_t y = 0.0f; \
arm_sqrt_f32(x, &y); \
y; \
})
#define SF_SIN(x) arm_sin_f32(x) //sinf(x)
#define SF_COS(x) arm_cos_f32(x) //cosf(x)
#define SF_FABS(x) fabsf(x)
#define SF_FMIN(a, b) (((a) < (b)) ? (a) : (b))
#define SF_FMAX(a, b) (((a) > (b)) ? (a) : (b))
#define SF_FSAT(x, max, min) (((x) > (max)) ? (max) : (((x) < (min)) ? (min):(x)))
#define SF_FSWAP(a, b) do {float temp = (a); (a) = (b); (b) = temp;} while(0)
#define SF_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define SF_MAX(a, b) (((a) > (b)) ? (a) : (b))
#define float32_frac(x) (x) - floorf(x)
#define float32_atan2(n, d) atan2f(n, d)
#define int32_min(a, b) (((a) < (b)) ? (a) : (b))
#define int32_max(a, b) (((a) > (b)) ? (a) : (b))