STM32的FFT算法及波形识别方法在电赛信号中的应用探究
注:以下内容部分摘抄自其他csdn博主,如有侵犯,请与我交流
先介绍一下啊FFT官方库的1024点和256点的傅里叶变换函数,下面有详细解释
void cr4_fft_256_stm32(void *pssOUT, void *pssIN, u16 Nbin);
void cr4_fft_1024_stm32(void *pssOUT, void *pssIN, u16 Nbin);
// *pssOUT是FFT之后输出频域的数组,*pssIN为输入的时域采样信号数组,Nbin为FFT点数
定义三种数组:输入、输出、幅值
// long是32位
long InBufArray[NPT]={0}; //定义输入数组
long OutBufArray[NPT/2]; //定义输出数组
long MagBufArray[NPT/2]; //幅值
信号首先要进行ad采样并且我们在程序中配置采样频率,要符合奈奎斯特采样定理
奈奎斯特定理内容大概是:采样频率要高于输入信号频率的二倍,所以当我们要采集最大是1200Hz时它的采样频率是大于2400hz
分辨率: 输出时,最小频率间隔(Fs/N)
比如当采样频率为1024 ,N为1024,则最小分辨率为1hz
以下是adc、tim、dma一些基本的配置设置
void TIM1_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* Time Base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* TIM1 channel1 configuration in PWM mode */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = arr;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
void ADC1_Configuration(void)
{
//PC1作为模拟输入引脚
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO ,ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1 工作在独立模式
ADC_InitStructure.ADC_ScanConvMode =DISABLE; //模数转换工作在非扫描模式
ADC_InitStructure.ADC_ContinuousConvMode =DISABLE; //模数转换工作在不连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
//Timer1触发转换开启(定时器T1的CC1通道,控制采样频率)
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5 );
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
void ADC1_DMA1_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; //DMA中断服务函数
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value; //存储数据数组地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = NPT; //数据长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC | DMA_IT_HT, ENABLE);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
得到信号幅值的函数GetPowerMag()
void GetPowerMag()
{
signed short lX,lY;
float X,Y,Mag;
unsigned short i;
for(i=0; i<NPT/2; i++)
{
lX = (OutBufArray[i] << 16) >> 16;
lY = (OutBufArray[i] >> 16);
//除以32768再乘65536是为了符合浮点数计算规律
X = NPT * ((float)lX) / 32768;
Y = NPT * ((float)lY) / 32768;
Mag = sqrt(X * X + Y * Y) / NPT;
if(i == 0)
MagBufArray[i] = (unsigned long)(Mag * 32768);
else
MagBufArray[i] = (unsigned long)(Mag * 65536);
//MagBufArray[]为计算输出的幅值数组
}
}
基本思路
配置采样频率,定时器配置
采样频率 = 72M/psc+1/arr+1
根据奈奎斯特采样定理 采样频率要高于输入信号频率的二倍
分辨率: 输出时,最小频率间隔(Fs/N)
当采样频率为1024N为1024,则最小分辨率为1hz
在使用时我们需要把ad采样得到的数组放到fft输入数组中,在进行fft的变换
for(i=0;i<NPT;i++)
{
lBufInArray[i]=ADC_Value[i]<<16;
}
cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);
正弦波:只有基波分量,基本无谐波分量,没什么好说的。
方波:除了基波,还有3,5,7次谐波分量,且3次谐波分量为基波分量的1/3.
三角波:除了基波,还有3,5,7次谐波分量,但3次谐波分量为基波分量的1/9.
锯齿波:除了基波,还有2,3,4次谐波分量.
波形识别这部分在网上摘抄的资料,非常详细
找最大值次大值以及次次和次次次大值,最后是波形的识别
/***********************************************
找最大值,次大值……对应的频率,分析波形
*************************************************/
void select_max(float *f,float *a)
{
int i,j;
float k,k1,m;
float aMax =0.0,aSecondMax = 0.0,aThirdMax = 0.0,aFourthMax=0.0;
float fMax =0.0,fSecondMax = 0.0,fThirdMax = 0.0,fFourthMax=0.0;
int nMax=0,nSecondMax=0,nThirdMax=0,nFourthMax=0;
for ( i = 1; i < NPT/2; i++)//i必须是1,是0的话,会把直流分量加进去!!!!
{
if (a[i]>aMax)
{
aMax = a[i];
nMax=i;
fMax=f[nMax];
}
}
for ( i=1; i < NPT/2; i++)
{
if (nMax == i)
{
continue;//跳过原来最大值的下标,直接开始i+1的循环
}
if (a[i]>aSecondMax&&a[i]>a[i+1]&&a[i]>a[i-1])
{
aSecondMax = a[i];
nSecondMax=i;
fSecondMax=f[nSecondMax];
}
}
for ( i=1; i < NPT/2; i++)
{
if (nMax == i||nSecondMax==i)
{
continue;//跳过原来最大值的下标,直接开始i+1的循环
}
if (a[i]>aThirdMax&&a[i]>a[i+1]&&a[i]>a[i-1])
{
aThirdMax = a[i];
nThirdMax=i;
fThirdMax=f[nThirdMax];
}
}
for ( i=1; i < NPT/2; i++)
{
if (nMax == i||nSecondMax==i||nThirdMax==i)
{
continue;//跳过原来最大值的下标,直接开始i+1的循环
}
if (a[i]>aFourthMax&&a[i]>a[i+1]&&a[i]>a[i-1])
{
aFourthMax = a[i];
nFourthMax=i;
fFourthMax=f[nFourthMax];
}
}
k=fabs(2*fMax-fSecondMax);
k1=fabs(3*fMax-fSecondMax);
m=fabs((float)(aMax-3.0*aSecondMax));
if(k<=5)
LCD_ShowString(275,230,12*4,12,12,"JvChi ");
else if(k1<=5&&m<0.4)
LCD_ShowString(275,230,12*4,12,12,"Fang ");
else if(k1<=5&&m>=0.4)
LCD_ShowString(275,230,12*4,12,12,"SanJiao");
else LCD_ShowString(275,230,12*4,12,12,"Sin ");
}
注:在调用查找到的频率最大值和幅度最大值时要在头文件中用extern关键定义,方可在主程序中调用
作者:阿林的学习日记