SES如何使用数学库arm_math让程序起飞?
来源:技术让梦想更伟大
作者:李肖遥
最近在使用Nordic的52832开发,这也属于ARM Cortex M4架构,芯片具体不介绍了,主要是M4与M0、M3的最大不同就是具有FPU(浮点运算单元),这也正是我所需要的。
因为项目中有相关算法,涉及到卡尔曼滤波等等,需要支持浮点指令集,因此速度很重要。使用了CMSIS DSP软件库的数学库arm_math后,在处理数学运算时能比M0/M3高出数十倍甚至上百倍的性能。
CMSIS DSP软件库是什么
CMSIS DSP软件库是一套用于基于Cortex-M和Cortex-A处理器的设备的常用信号处理功能。该库涵盖以下类别的功能:
基本的数学功能
快速的数学功能
复杂的数学函数
过滤功能
矩阵函数
转换功能
电机控制功能
统计功能
支持功能
插值功能
支持向量机功能(SVM)
贝叶斯分类器功能
距离功能
该库通常具有单独的函数,用于对8位整数、16位整数、32位整数和32位浮点值进行运算。
库的使用介绍
库安装程序在Lib文件夹中包含库的预构建版本,以下是预构建库的列表:
arm_cortexM7lfdp_math.lib(Cortex-M7,小端,双精度浮点单元)
arm_cortexM7bfdp_math.lib(Cortex-M7,大字节序,双精度浮点单元)
arm_cortexM7lfsp_math.lib(Cortex-M7,小端,单精度浮点单元)
arm_cortexM7bfsp_math.lib(Cortex-M7,大字节序和单精度浮点单元打开)
arm_cortexM7l_math.lib(Cortex-M7,小端)
arm_cortexM7b_math.lib(Cortex-M7,大端)
arm_cortexM4lf_math.lib(Cortex-M4,小端,浮点单元)
arm_cortexM4bf_math.lib(Cortex-M4,大端,浮点单元)
arm_cortexM4l_math.lib(Cortex-M4,小端)
arm_cortexM4b_math.lib(Cortex-M4,大端)
arm_cortexM3l_math.lib(Cortex-M3,小端)
arm_cortexM3b_math.lib(Cortex-M3,大端)
arm_cortexM0l_math.lib(Cortex-M0 / Cortex-M0 ,小端)
arm_cortexM0b_math.lib(Cortex-M0 / Cortex-M0 ,大端)
arm_ARMv8MBLl_math.lib(Armv8-M基线,小端)
arm_ARMv8MMLl_math.lib(Armv8-M主线,小端)
arm_ARMv8MMLlfsp_math.lib(Armv8-M主线,小字节序,单精度浮点单元)
arm_ARMv8MMLld_math.lib(Armv8-M主线,小端,DSP指令)
arm_ARMv8MMLldfsp_math.lib(Armv8-M主线,小字节序,DSP指令,单精度浮点单元)
库函数在位于Include文件夹中的公共文件arm_math.h
中声明,只需包括此文件并在应用程序中链接适当的库,然后开始调用库函数就可以使用了。
该库支持带有小尾数和大尾数的Cortex-M内核,相同的头文件将用于浮点单元(FPU)变体。
使用Segger Embedded Studio如何添加配置
在tools-->Package Mangner
里面可以直接安装,CMSIS 5 CMSIS-DSP Support Package
这里我已经装了

也可以点击已经安装的库,显示如下图

然后添加到工程里面,我使用的是arm_cortexM4lf_math.lib
,如下图

使用Segger Embedded Studio
还需要在工程里设置一下如下图所示


CMSIS DSP软件库文件结构
下图所示为DSP_Lib的文件结构


下面是各个源文件的使用说明
BasicMathFunctions
提供浮点数的各种基本运算函数,如加减乘除等运算。对于M0/M3只能用Q运算,即文件夹下以_q7、_q15、_q31
结尾的文件;而M4F能直接进行硬件浮点计算,属于文件夹下以_f32
结尾的文件。
CommonTables
arm_common_tables.c文件提供位翻转参数表。
ComplexMathFunctions
复述数学功能,如向量处理,求模运算等。
ControllerFunctions
控制功能,主要为PID控制函数。
FastMathFunctions
快速数学功能函数,提供256点正余弦函数表和任意任意角度的正余弦函数值计算功能,和Q值开平方运算:
Arm_cos_f32/_q15/_q31.c:提供256点余弦函数表和任意角度余弦值计算功能。
Arm_sin_f32/_q15/_q31.c:提供256点正弦函数表和任意角度正弦值计算功能。
Arm_sqrt_q15/q31.c:提供迭代法计算平方根的函数。对于M4F的平方根运算,通过执行VSQRT指令完成。
FilteringFunctions
滤波函数功能,主要为FIR和LMS(最小均方根)滤波函数。
MatrixFunctions
矩阵处理函数。
StatisticsFunctions
统计功能函数,如求平均值、计算RMS、计算方差/标准差等。
SupportFunctions
支持功能函数,如数据拷贝,Q格式和浮点格式相互转换,Q任意格式相互转换。
TransformFunctions
变换功能,包括复数FFT(CFFT)/复数FFT逆运算(CIFFT)、实数FFT(RFFT)/实数FFT逆运算(RIFFT)、和DCT(离散余弦变换)和配套的初始化函数。
预处理器宏
ARM_MATH_BIG_ENDIAN:
定义宏ARM_MATH_BIG_ENDIAN来为大型字节序目标构建库。默认情况下,为小端目标建立库。
ARM_MATH_MATRIX_CHECK:
定义宏ARM_MATH_MATRIX_CHECK以检查矩阵的输入和输出大小。
ARM_MATH_ROUNDING:
定义宏ARM_MATH_ROUNDING来舍入支持函数。
ARM_MATH_LOOPUNROLL:
定义宏ARM_MATH_LOOPUNROLL以启用DSP函数中的手动循环展开
ARM_MATH_NEON:
定义宏ARM_MATH_NEON以启用DSP功能的Neon版本。当Neon可用时,默认情况下不启用它,因为性能取决于编译器和目标体系结构。
ARM_MATH_NEON_EXPERIMENTAL:
定义宏ARM_MATH_NEON_EXPERIMENTAL以启用某些DSP功能。
ARM_MATH_HELIUM:
意味着标志ARM_MATH_MVEF和ARM_MATH_MVEI和ARM_MATH_FLOAT16。
ARM_MATH_MVEF:
选择f32算法的,意味着ARM_MATH_FLOAT16和ARM_MATH_MVEI。
ARM_MATH_MVEI:
选择int和定点算法的版本。
ARM_MATH_FLOAT16:
一些算法的Float16实现(需要MVE扩展)。
应用以及测试
在扩展卡尔曼滤波里面有很多矩阵求逆等等运算,如下面这个函数
_Bool matrixAddF(DOUBLE_TEST* result, DOUBLE_TEST* m, unsigned int row, unsigned int col, DOUBLE_TEST add) {
DOUBLE_TEST dt[row*col];
memset(dt, add, sizeof(DOUBLE_TEST)*row*col);
unsigned int i, j;
for (i = 0; i < row; i )
for (j = 0; j < col; j )
result[i * col j] = m[i * col j] add;
return true;
}
使用数学函数加速可以更改为
_Bool matrixAddF(DOUBLE_TEST* result, DOUBLE_TEST* m, unsigned int row, unsigned int col, DOUBLE_TEST add) { DOUBLE_TEST dt[row*col]; memset(dt, add, sizeof(DOUBLE_TEST)*row*col);
arm_mat_init_f32(&mtrin1, row, col, m); arm_mat_init_f32(&mtrin2, row, col, dt); arm_mat_init_f32(&mtrout, row, col, result); arm_mat_add_f32(&mtrin1, &mtrin2, &mtrout);
再举一个例子
_Bool matrixMult(DOUBLE_TEST* result, DOUBLE_TEST* m1, unsigned int row1, unsigned int col1, DOUBLE_TEST* m2, unsigned int row2, unsigned int col2) {
if (col1 != row2)
return false;
unsigned int i, j, k;
unsigned int M, N;
M = row1;
N = col2;
DOUBLE_TEST sum = 0;
for (i = 0; i < M; i )
{
for (j = 0; j < N; j )
{
sum = 0;
for (k = 0; k < col1; k )
sum = m1[i * col1 k] * (m2[k * col2 j]);
result[i * N j] = sum;
}
}
return true;
}
可以更改为
_Bool matrixMult(DOUBLE_TEST* result, DOUBLE_TEST* m1, unsigned int row1, unsigned int col1, DOUBLE_TEST* m2, unsigned int row2, unsigned int col2) { if (col1 != row2) return false; arm_mat_init_f32(&mtrin1, row1, col1, m1); arm_mat_init_f32(&mtrin2, row2, col2, m2); arm_mat_init_f32(&mtrout, row1, col2, result); arm_mat_mult_f32(&mtrin1, &mtrin2, &mtrout);
return true;}
arm_mat_init_f32的定义在arm_math.h
里面,如下
/**
* 浮点矩阵初始化
* [in,out] S points to an instance of the floating-point matrix structure.
* [in] nRows number of rows in the matrix.
* [in] nColumns number of columns in the matrix.
* [in] pData points to the matrix data array.
*/
void arm_mat_init_f32(
arm_matrix_instance_f32 * S,
uint16_t nRows,
uint16_t nColumns,
float32_t * pData);
arm_mat_add_f32函数如下
/** * 浮点矩阵加法 * [in] pSrcA points to the first input matrix structure * [in] pSrcB points to the second input matrix structure * [out] pDst points to output matrix structure * return The function returns either */arm_status arm_mat_add_f32( const arm_matrix_instance_f32 * pSrcA, const arm_matrix_instance_f32 * pSrcB, arm_matrix_instance_f32 * pDst);
arm_mat_sub_f32函数如下
/**
* 浮点矩阵减法
* [in] pSrcA points to the first input matrix structure
* [in] pSrcB points to the second input matrix structure
* [out] pDst points to output matrix structure
* return The function returns either
*/
arm_status arm_mat_sub_f32(
const arm_matrix_instance_f32 * pSrcA,
const arm_matrix_instance_f32 * pSrcB,
arm_matrix_instance_f32 * pDst);
上面几个函数中出现的结构体arm_status
、arm_matrix_instance_f32
定义如下
/** * 库中某些函数返回的错误状态。 */typedef enum{ ARM_MATH_SUCCESS = 0, /**< No error */ ARM_MATH_ARGUMENT_ERROR = -1, /**< One or more arguments are incorrect */ ARM_MATH_LENGTH_ERROR = -2, /**< Length of data buffer is incorrect */ ARM_MATH_SIZE_MISMATCH = -3, /**< Size of matrices is not compatible with the operation */ ARM_MATH_NANINF = -4, /**< Not-a-number (NaN) or infinity is generated */ ARM_MATH_SINGULAR = -5, /**< Input matrix is singular and cannot be inverted */ ARM_MATH_TEST_FAILURE = -6 /**< Test Failed */} arm_status;
/** * @brief 实例结构为浮点矩阵结构 */typedef struct{ uint16_t numRows; /**< number of rows of the matrix. */ uint16_t numCols; /**< number of columns of the matrix. */ float32_t *pData; /**< points to the data of the matrix. */} arm_matrix_instance_f32;
经过测试,使用硬件加速之后,我的程序运行速度增加了10倍,跑一下扩展卡尔曼滤波也不算慢了。
参考:https://www.keil.com/pack/doc/CMSIS/DSP/html/index.html