电赛信号类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关键定义,方可在主程序中调用

STM32F4中,可以使用FFT算法来判断波形的种。通过对输入信号进行FFT变换,可以得到频域上的幅度谱。根据幅度谱的特征,可以判断出不同的波形型。具体的实现可以参考引用\[2\]中提供的基于STM32F4的FFT信号频率并判断波形的思路。在该示例中,通过对输入信号进行FFT变换,并根据频域上的幅度谱进行判断,可以判断出不同的波形型。你可以参考该示例中的代码来实现波形判断功能。另外,引用\[3\]中提供了一个关于使用STM32F4的FFT库进行频谱分析的示例代码,你也可以参考该示例代码来实现波形判断功能。 #### 引用[.reference_title] - *1* *3* [stm32f4进行fft运算](https://blog.csdn.net/a1240553493/article/details/119107402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [基于STM32F4的FFT+测频率幅值相位差,波形显示,示波器,时域频域分析相关工程](https://blog.csdn.net/qq_50027598/article/details/126045155)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值