在STM32单片机上使用傅里叶解析信号
关于傅里叶变换的知识,可以看我之前的文章:傅里叶变换记录,里面记录了一些参数的含义还有使用python和matlab进行傅里叶分析,提取信号的一些过程,希望能对读者产生一点帮助。
下面记录使用stm32来对信号进行分析,这里一般是指一些adc采样的信号,对信号进行分析一方面是获取信号的一些成分,另一方面是对有可能信号中含有很多造成,通过分析可以对噪声进行处理。
注:本文采用单片机为stm32f103RCT6,后续可能会考虑使用F4的平台进行测试,这里暂时仅测试F1,相信如果F1都没什么问题,F4效果应该会更好的。
1、导入分析工具
写这篇文章之前也参考了一些文章,这里先贴一下大佬的帖子,F1的话需要导入一些数学库的
https://blog.csdn.net/weixin_43368814/article/details/103552114
https://shequ.stmicroelectronics.cn/thread-632949-1-1.html
https://blog.csdn.net/Simon223/article/details/105728567
这里目前看有两种方案,不过他们的测试方法好像也基本都差不多,互相借鉴吧应该hhh,第一种是直接导入需要的库,就是下面的几个文件了,把他们导入到keil的路径下即可
下面是导入.s文件
下面是引入.h文件
下面是第二种方案,也基本是一样的,我个人比较推荐第二种方案
在安装hal库的位置。就是下载了库的包的位置,有下面的目录,找到这个目录即可,找到下面这个lib文件,复制出来
再进入下面的目录,找到下面的3个.h文件也是一样的复制出来
把这四个文件一起复制到我们的工程目录下
在工程目录里面添加lib文件
在我们编译宏参数中加入
,ARM_MATH_CM3,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING
编译的宏参数加入还有将刚才复制的文件路径添加到我们的目录下即可
2、信号生成与查看
先用我们之间提到过的分析工具查看下信号是什么样的,生成信号的函数如下所示,还是三个正弦函数叠加,这里采样率设置为100khz,这样比较贴合实际,采到后通过串口把数据发送出去,我们使用matlab来对捕捉的数据进行分析。
#define Fs 100000
void InitBufInArray(void)
{
unsigned short i;
float fx;
for(i = 0; i < FFT_LENGTH; i++)
{
fx = 15 * sin(PI * 2 * i * 13500.0 / Fs) +
27 * sin(PI * 2 * i * 45000.0 / Fs) +
40 * sin(PI * 2 * i * 8500.0 / Fs);
// lBufInArray[i] = ((signed short)fx) << 16;
printf("%f\n", fx);
}
}
使用matlab对采集到的数据,可以看到叠加起来的数据还是很乱的,这里基本看不出来原始数据有什么特征了,是很乱的一团信号,不过直接使用matlab还是分析出来他由三组信号组成,下面有三个很明显的尖峰。
这里把刚才截取的放大看下,这个部分的标准值应该是8500,40,是非常接近的,这个误差肯定是会有的,因为单片机进行sin计算等肯定进行了逼近等,本身计算就会存在误差。
3、使用STM32读取数据
1、使用第一种库
下面就使用stm32来进行分析,这里需要说明就是使用stm32来进行分析肯定是离散分析的,就是一段段的信号进行分析,那么如果对应到我们的实际采样中其实就是采样一段信号分析一段信号。
首先还是构建我们需要分析的数组,源码如下,下面进行介绍
#define Fs 100000
#define FFT_LENGTH 1024
int32_t lBufInArray[FFT_LENGTH] = {0};
uint16_t ADC_Value[FFT_LENGTH] = {0};
int32_t lBufOutArray[FFT_LENGTH / 2] = {0};
int32_t lBufMagArray[FFT_LENGTH / 2] = {0};
void InitBufInArray(void)
{
unsigned short i;
float fx;
for(i = 0; i < FFT_LENGTH; i++)
{
fx = 1024*sin(2*PI*i * 13500.0 / Fs)
+512*sin(2*PI*i * 8500.0 / Fs)
+512*sin(2*PI*i * 3500.0 / Fs);
ADC_Value[i] = fx+2048;
lBufInArray[i] = ((signed short)ADC_Value[i]) << 16;
// printf("%d\n", ADC_Value[i]);
}
}
这里为了适应stm32的采集方式,把生成的序列转换为0-4096的整数格式,这个过程肯定会产生精度损失,所以后面结果应该是会变得不太准确,这里要注意,但是实际真实采样就不用担心。
这里是根据转换函数,需要把采样数据做下处理,32位的整形数据,高16位放实部,低16位放虚部,虚部一般都是0 ,就不处理了,默认即可。
这样就可以用下面的函数计算了,就可以生成FFT的序列了
InitBufInArray();
cr4_fft_1024_stm32(lBufOutArray, lBufInArray, FFT_LENGTH);
但是这个结果还不是我们最终要的,最终结果还需要进行一下优化的处理,这里就是提取他的谐波幅值
void GetPowerMag(void)
{
signed short lX, lY;
float X, Y, Mag;
unsigned short i;
for(i = 0; i < FFT_LENGTH / 2; i++)
{
lX = (lBufOutArray[i] << 16) >> 16;
lY = (lBufOutArray[i] >> 16);
X = FFT_LENGTH * ((float)lX) / 32768;
Y = FFT_LENGTH * ((float)lY) / 32768;
Mag = sqrt(X * X + Y * Y) / FFT_LENGTH;
if(i == 0)
lBufMagArray[i] = (unsigned long)(Mag * 32768);
else
lBufMagArray[i] = (unsigned long)(Mag * 65536);
}
}
最后加上这个函数就行了,并加上结果打印函数
源码如下
void FFT(void)
{
InitBufInArray();
cr4_fft_1024_stm32(lBufOutArray, lBufInArray, FFT_LENGTH);
GetPowerMag();
for(int16_t i = 1; i < FFT_LENGTH/2; i++)
{
printf("%d\n",lBufMagArray[i]);
}
}
将程序下载到开发板,最终的结果如下所示,这个结果准不准确呢,不如直接用matlab来进行一下对比试验就知道了。
这里我们先把这个数据放到matlab中来查看下
再把我们生成的数据也导入matlab来进行一下分析,可以看到结果如下,还是比较准确的,这里的误差是上面的强制转换带来的
2、使用第二种库
第二种为arm自带的fft函数,函数为arm_cfft_f32,这里首先要注意的就是他的一个实部虚部的数据位置,这里发生了变化,不再是高16位和低16位存储的数据发生变化,而是在数组缓冲区里面间隔存在,如下图所示:
下面进行整个流程的介绍,首先还是生成需要的采样数据
下面就是傅里叶分析的部分了
最终结果如下所示:
完整代码:
#include "fft_test.h"
#include "arm_math.h"
#include "arm_const_structs.h"
#include "stdio.h"
#define Fs 100000
#define FFT_LENGTH 1024
float32_t lBufInArray[FFT_LENGTH*2] = {0};
float32_t lBufOutArray[FFT_LENGTH / 2] = {0};
uint16_t ADC_Value[FFT_LENGTH] = {0};
void InitBufInArray(void)
{
unsigned short i;
float fx;
for(i = 0; i < FFT_LENGTH; i++)
{
fx = 1024 * sin(2 * PI * i * 13500.0 / Fs)
+ 512 * sin(2 * PI * i * 8500.0 / Fs)
+ 512 * sin(2 * PI * i * 3500.0 / Fs);
ADC_Value[i] = fx + 2048;
}
}
void FFT(void)
{
InitBufInArray();
for (int i = 0; i < FFT_LENGTH; i++)
{
lBufInArray[i * 2] = ADC_Value[i]; //实部赋值
lBufInArray[i * 2 + 1] = 0; //虚部赋值
}
arm_cfft_f32(&arm_cfft_sR_f32_len1024, lBufInArray, 0, 1);
arm_cmplx_mag_f32(lBufInArray, lBufOutArray, FFT_LENGTH);
for (int i = 1; i < FFT_LENGTH/2; i++)
{
lBufOutArray[i] /= 512;
}
for(int16_t i = 1; i < FFT_LENGTH/2; i++)
{
printf("%f\n", lBufOutArray[i]);
}
}
更多推荐
所有评论(0)