对于一些刚接触STM32的童靴来说,DSP库一定是一个陌生的东西。通俗来说,DSP库就是为了让MCU能够使用像DSP(数字信号处理的芯片)功能弄的一些官方库函数,它是基于MCU的FPU(浮点运算功能)的,如果你连FPU都不知道咋开,我个人建议先去查一查啥是FPU以及DSP库移植的一些步骤(移植也分两种,一种是使用lib文件,另一种是直接的C语言函数库)。
这里推荐的参考资料是:安富莱的STM32-V6开发板_第二版DSP数字信号处理教程,下载链接在这里:https://download.csdn.net/download/qq_32006213/87406373
说实话,只要能认真看这个教程,也不需要看我这个文章在这里BB了,这里仅做一些整理。
【一】类型:浮点数、定点数;浮点数就有:
float32_t→float;float64_t→double;定点数就不介绍了,一般也很少用到。这里需要注意的是,如果你使用的是STM32,那么就一定要注意你的浮点计算往往在一开始就因为你传入的数值不是浮点型或者是被编译器给你整成其他类型而变慢了。例如在运算时你需要使用一个数:1.234,你直接在公式中写:
NMSL = 1.234 - 5;
或者
Fun(1.234);
那恭喜你,喜提慢速计算。为啥,因为单片机认为你传入的 1.234并不是浮点型而是double型,而单片机只有FPU却没有针对double类型快速计算的模块,就只能硬着头皮去算了。
还有一点就是,如果你使用一些math.h中的传统C库的函数,它的入参默认就是double类型,那即便你确保传入的是浮点数同样也会很慢。个人认为这就是传统库相比DSP库的劣势之一,当然不是不让你用double类型,而是都是用浮点型时,DSP库会更快罢了。为了解决以上的问题,可以改为:
NMSL = 1.234f - 5.0f;
或者
Fun(1.234f);
所以第一个需要注意点就是,确保你的变量类型没有错误,基本上使用float32_t即可。
【二】常用函数:
2.1 三角函数:arm_cos_f32/arm_sin_f32/arm_sin_cos_f32
使用案例:
#define PI 3.141592f
float32_t Angle = 60.0f;
float32_t Angle_Arc = Angle/180*PI;
float32_t Cos = 0.0f;
float32_t Sin = 0.0f;
Cos = arm_cos_f32(Angle_Arc);//注意这个函数要传入的东西是弧度制
Sin = arm_sin_f32(Angle_Arc);//注意这个函数要传入的东西是弧度制
arm_sin_cos_f32(Angle,&Sin,&Cos);//这个函数顶级就顶级在它可以直接传角度,而且角度是可以有小数的,但是使用方式就完全不同了。
2.2 平方根:arm_sqrt_f32
float32_t Input = 9.0f;
float32_t Output = 0.0f;
arm_sqrt_f32(Input,&Output);//这样Output上获得的就是平方根,暂时没试过负数能开出来不,可能有风险哦。
2.3 最大值、最小值、平均值
2.3.1最大值:arm_max_f32 最小值:arm_min_f32
float32_t Array[3]={0f,1.2f,1.3f};
float32_t Min = 0.0f;
uint32_t Min_index = 0;
float32_t Max = 0.0f;
uint32_t Max_index = 0;
arm_max_f32(Array,3,&Max,&Max_index);//数组:长度;获得的最小值;最小值在数组中的位置
arm_min_f32(Array,3,&Min,&Min_index);//最为强大的就是能找到位置啦
实际上第二个参数也可以不使用整体长度,第一个参数也可以不传入数组第一个元素的地址。如果你想要求局部的最大/最小,可以通过操控第一个参数传入你想要从哪一个数据开始计算的n地址,例如:
(float32_t *)(Array +n)→表示从第n个开始,至于为什么这么写,是因为有很多不同的类型,这样写是习惯比较稳妥,更多的可以自行去查询(uint32_t *)Array+n 这样为什么会有问题【提示:Memcpy函数也需要注意】。然后长度填5。那么现在就是求数组第n个开始五个数中间的最大/最小值啦。
2.3.2 平均值 arm_mean_f32
float32_t Array[3]={0f,1.2f,1.3f};
float32_t Mean = 0.0f;
arm_mean_f32(Array,3,&Mean);
2.4 标准差、均方根、方差
这三者在统计上具有较大的意义,在处理一系列的数据时可能用的到,至于每个数表示啥意思请自行查询。
float32_t Array[3]={0f,1.2f,1.3f};
float32_t Std = 0.0f;
float32_t Rms = 0.0f;
float32_t Var = 0.0f;
arm_std_f32(Array,3,&Std);//标准差
arm_rms_f32(Array,3,&Rms);//均方根
arm_var_f32(Array,3,&Var);//方差
2.5数据拷贝、填充
float32_t Array_A[3]={0f,1.2f,1.3f};
float32_t Array_B[3];
arm_copy_f32(Array_A,Array_B,3);//拷贝
arm_fill_f32(5.0f,Array_B,3);//将5.0f填充至Array_B
2.6基本运算:求和、求差、求乘法、求点乘、求绝对值、求相反数、求比例化
以上基本上都是面对两个数组多个数据进行的,如果本身就没多少变量/数,直接公式算也不差。
float32_t Array_A[3]={0f,1.2f,1.3f};
float32_t Array_B[3] ={3.5f,-5.0f,2.5f};
float32_t Result_Array[3];
float32_t Dot_result;
arm_add_f32(Array_A,Array_B,Result_Array,3);//求和:Result_Array[n] = Array_A[n]+Array_B[n]
arm_sub_f32(Array_A,Array_B,Result_Array,3);//求差:Result_Array[n] = Array_A[n]-Array_B[n],这个要记得是前面减去后面
arm_abs_f32(Array_B,Result_Array,3);//求绝对值Result_Array[n] =|Array_B[n]|
arm_mult_f32(Array_A,Array_B,Result_Array,3);//求乘法Result_Array[n] = Array_A[n]*Array_B[n]
arm_dot_prod_f32(Array_A,Array_B,3,&Dot_result);//点乘得到的是一个值,详情看线性代数。Dot_result = Array_A[0]*Array_B[0]+···Array_A[2]*Array_B[2]
arm_negate_f32(Array_A,Result_Array,3);//求相反数:Result_Array[n] = -Array_A[n]
arm_scale_f32(Array_A,1.5f,Result_Array,3);//求比例乘积后:Result_Array[n] = Array_A[n]*1.5f;
2.7 矩阵运算、复数运算
基本上用不到,而且拿单片机算矩阵未免有那么一点点刁难它。一般的步骤都是复杂的运算在Matlab上运行,将算法简化为单一的公式再在arm上实现,这里就不继续讲解了。如果一定要使用矩阵,这里提三点意见:
1.使用矩阵一定要严格按照Arm_math中的矩阵定义来使用,如果要秀操作搞数组,就用不了DSP库了。关于矩阵的定义和初始化,一定要详细看清楚:arm_matrix_instance_f32这个结构体的定义。
2.单片机算矩阵运算很有可能算不出结果而一直递归,别问我怎么知道的…包括你在Matlab上算一些矩阵的运算,如果它本身就不成立,那就没有意义,会一直不收敛。
3.DSP库的逆矩阵求法是有局限性的,有些你在matlab上算的出来,DSP库缺不能求解。所以如果遇到问题,建议先仿真看看,确保矩阵和数值没有问题。