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库源码和库的途径有:

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)
      }
      
  • 请确保系统的ICacheDCache都已经开启

  • 针对正余弦函数计算,为了获取更高的精度, 用户可通过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))