it编程 > 硬件开发 > stm32

【STM32】FPU的启用和基于ARM-DSP库函数的实时信号RMS计算

65人参与 2024-08-06 stm32

概述

最近,由于项目需要,学习了一下f4的fpu浮点数运算单元的使用方法和dsp库函数的建立使用,在此简单总结记录。

项目背景分析:

目标与流程梳理:

下面基于stm32f446ret6,使用标准库函数(版本:v1.8.0),在keil mdk 5.26环境下,完成上述需求。



一、fpu的启用

fpu 即浮点运算单元(float point unit)。浮点运算,对于定点 cpu(没有 fpu 的 cpu)来说必须要按照 ieee-754 标准的算法来完成运算,是相当耗费时间的。而对于有 fpu 的 cpu来说,浮点运算则只是几条指令的事情,速度相当快。

stm32f4 属于 cortex m4f 架构,带有 32 位单精度硬件 fpu,支持浮点指令集,相对于cortex m0 和 cortex m3 等,高出数十倍甚至上百倍的运算性能。

在这里插入图片描述
打开system_stm32f4xx.c文件,在systeminit函数中,已规定了fpu的启用条件,即486行所示:

前者是要检查mcu是否拥有fpu,后者使能fpu,二者需同时满足。

打开stm32f4xx.h头文件,如下图第190行所示,文件已经在编译时将__fpu_present置为1,意思是该mcu存在fpu,那么下面只需关注如何将__fpu_used置1即可。

在这里插入图片描述

使能fpu的方法有很多,对于keil mdk环境,最方便的方法是在code generation中将floating point hardware配置为single precision,如下图所示:

在这里插入图片描述
经过这个设置,编译器会自动加入标识符__fpu_used为 1。 这样,遇到浮点运算就会使用硬件fpu相关指令,执行浮点运算,从而大大减少计算时间。

在调试窗口,可以看到对于进行浮点运算的指令,反汇编代码中存在xxx.f32的指令,说明fpu已开启,如下图所示。

在这里插入图片描述

需要注意,当运算中有浮点的数字时,数字后面要加上一个f,例如:

a = (float)para*9.1e-02f;

若不加f,keil会提示warning: #1035-d: single-precision operand implicitly converted to double-precision,意思是单精度运算隐式转换成了双精度运算了,运算速度将无法显著提升。


二、dsp库环境搭建

1、dsp标准库

对于复杂运算,例如rms计算,如果编程时还是使用math.h头文件,效率提升很有限,因为math.h头文件是针对所有arm处理器的,其运算函数都是基于定点cpu和标准算法(ieee-754),没有使用fpu。因此,要充分发挥m4f的浮点功能,需要使用固件库自带的arm_math.h,即搭建dsp运行环境。

stm32f4的dsp库源码和测试实例在st提供的标准库:stm32f4_dsp_stdperiph_lib.zip里提供,官方下载链接:

http://www.st.com/web/en/catalog/tools/fm147/cl1794/sc961/ss1743/pf257901

在这里插入图片描述

这里使用v1.8.0版本的标准函数库,下载完成后打开dsp_lib源码包的source文件夹,里面存放了所有dsp库的源码, 如上图所示。examples文件夹是相对应的一些测试实例。以下是source源码文件夹下面的子文件夹包含的dsp库的功能:

所有这些dsp库代码合在一起是比较多的,因此,st提供了.lib格式的文件,方便使用。这些.lib文件就是由 source文件夹下的源码编译生成的,如果想看某个函数的源码,可在source文件夹下面查找。

在这里插入图片描述
我们需要根据所用mcu内核类型以及端模式来选择符合要求的 .lib文件,而stm32f4属于cortexm4f内核,小端模式,应选择: arm_cortexm4lf_math.lib(浮点cortex m4,小端模式)。

2、dsp库运行环境搭建

  1. 添加文件

在工程目录下新建文件夹dsp,放置arm_cortexm4lf_math.lib和相关头文件,过程如图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
include文件夹直接拷贝stm32f4xx标准外设库_v1.8.0\libraries\cmsis\include中的所有文件。

打开工程,添加分组,将arm_cortexm4lf_math.lib添加到工程里面,如图:

在这里插入图片描述

  1. 添加头文件包含路径

添加好.lib文件后,要添加头文件包含路径,将第一步拷贝的include文件夹和dsp文件夹,加入头文件包含路径,如图:

在这里插入图片描述

  1. 添加全局宏定义

最后,为了使用dsp库的所有功能,还需要添加几个全局宏定义:

arm_math_cm4
__cc_arm
arm_math_matrix_check
arm_math_rounding

对于stm32f446ret6,需要添加的所有宏为:

use_stdperiph_driver,stm32f446xx,arm_math_cm4,__cc_arm,arm_math_matrix_check,arm_math_rounding

注意两个宏之间用,隔开,如下图所示:

在这里插入图片描述

至此,dsp库运行环境搭建完成。


三、基于dsp库的实时信号rms计算

1、rms的相关概念

对于电压信号来说,rms是其方均根值,即有效值。与平均值的概念不同,rms直接反映信号的能量特征,对于连续的模拟信号而言,其定义计算式如下:

在这里插入图片描述
而对于经过adc采样后的数字信号而言,其计算式可表示为:

result = sqrt(((psrc[0] * psrc[0] + psrc[1] * psrc[1] + ... + psrc[blocksize-1] * psrc[blocksize-1]) / blocksize));

其中,result为rms计算结果,psrc[]为采样后的数字量,blocksize为处理的数据量。

2、程序编写

打开statisticsfunctions文件夹中的arm_rms_f32.c文件,可见dsp库对于rms计算的函数定义,如下图所示:

在这里插入图片描述

因此,要使用arm_rms_f32函数,需要向其传递三个参数:

程序编写的整体思路是:

  1. 定义数据存储数组和相关指针、变量;
  2. adc对模拟差压信号采样,将数字量转换为实际电压值;
  3. 将实际电压值存入数组;
  4. 将数据数组传递至rms计算函数;
  5. 打印输入、输出数据,于上位机查看;
  6. 以上流程封装为函数flow_rms,放在定时器中断服务函数中,控制采样率和输出;
  7. 在此基础上,完善其它控制功能。

另外,若要实现实时信号rms计算处理,则要每存一个数据,计算一次rms值,即不断更新数组中的元素,这一需求可通过操作数组指针完成,当指针从数组首地址移动到尾地址时,将指针归位到首地址,思路上类似于数据结构中的循环队列。

为提高程序运行效率,数组元素的赋值采用指针操作,这样往往比下标的赋值方式更快。

根据上述思路,编写程序如下:

#include "pump.h"
#include "ads1256.h"
#include <stdio.h>

float datainput[data_length] = {0}; 	//传感器电压值存储数组、rms计算输入数据存储数组初始化
float *inputpoint = datainput;			//输入数据数组指针初始化

float dataoutput;                		//计算后的rms值
float *outputpoint = &dataoutput;		//计算结果变量指针

float *ifpoint = &datainput[data_length - 1] + 1;	//判断指针,定义于输入数组的最后一个元素+1的地址处

int blocksize = 0;						//每次rms计算的数据量
int npa_ddata;  						//差压传感器adc数字值
float npa_vdata;		 				//差压传感器adc电压值

void flow_rms(void)
{	

	npa_ddata = ads1256readdata(ads1256_muxp_ain1|ads1256_muxn_aincom);    //adc采集数据
	npa_vdata = (float)npa_ddata/8388607.0f*5.0f;						   //转换为电压值
	
	*inputpoint = npa_vdata;		//把数据存入数组,指针指向数组中的某个元素地址
	inputpoint ++;					//地址+1,以存放下一个数据
	
	if(blocksize != data_length)	//若datainput数组中的元素数未满,则使rms计算的数据量+1,与当前数据量保持一致;反之则维持blocksize = data_length不变
		blocksize ++;	

	if(inputpoint == ifpoint)		//若输入数组的指针移动到数组的最后一个元素+1的地址处,则使指针重新归位
		inputpoint = datainput;
	
	
	arm_rms_f32(datainput, blocksize, outputpoint);		//对采集的数据进行rms计算
	
	printf("%f, %f\r\n", dataoutput, npa_vdata);		//打印rms计算结果和实时信号值
	
}

#ifndef __pump_h
#define __pump_h

#include <stm32f4xx.h>
#include "arm_math.h"

#define data_length  250     //数组长度

void flow_rms(void);

#endif

void tim3_irqhandler(void)  
{
	if (tim_getitstatus(tim3, tim_it_update) != reset) //检查指定的tim3中断发生与否
	{		
		tim_clearitpendingbit(tim3, tim_it_update);   //清除tim3的中断待处理位
		
		flow_rms();
	}
}

3、结果测试

使用微型气泵为差压传感器提供气压差源,配置不同占空比控制气泵抽气速率。确定条件下,使用vofa+上位机观察输出,结果如下:

在这里插入图片描述

可见,虽然输入为一系列不规则波形(与气泵引动的气流有关),但经rms计算,其均方根值基本稳定。由此可见,该方法非常适合于对不规则信号的直流化的应用中,如噪声计算等,此时应考虑硬件rms计算方案和软件哪种更合适。

后续程序可根据实际应用,加以完善。

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

【STM32-DSP库的使用】基于Keil5 + STM32CubeMX + CMSIS-DSP 手动添加、库添加方式

08-06

视觉巡线小车——STM32+OpenMV(三)

08-06

STM32 proteus + STM32Cubemx仿真教程(第五课ADC光敏电阻采样教程)

08-06

STM32 CUBEMX主从定时器配置PWM任意相位可调,占空比以及频率可调(学习记录)

08-06

第一 二章 小车硬件介绍-(全网最详细)基于STM32智能小车-蓝牙遥控、避障、循迹、跟随、PID速度控制、视觉循迹、openmv与STM32通信、openmv图像处理、smt32f103c8t6

08-06

探索STM32与OLED显示屏:SSD1306库使用指南与实战

08-06

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论