基于STM32的频谱分析和波形识别系统

目录

1、概述

2、硬件设计

3、软件设计

 4、测试结果


1、概述

本篇介绍了以STM32F103单片机为核心的频谱分析和波形识别系统,并对其硬件组成和软件设计做了详细讲解。该系统通过STM32F103ZET6主控芯片进行ADC采样,再使用DSP库提供的FFT函数对采集到的信号进行处理,最后将输入信号的频谱图显示在TFTLCD液晶屏上,同时显示波形相关参数以及波形种类。

2、硬件设计

该装置由于是直接使用单片机开发板进行二次开发,所用单片机的芯片引脚图如下图3-2所示。因此整体上硬件较为简单。硬件主要包括信号发生器,正点原子精英板,4.3’TFTLCD,两根杜邦线,我选择使用PC1引脚接信号发生器的输出,作为该系统的输入信号,另一根杜邦线接信号发生器的GND。

本系统以STM32F103单片机为控制核心,对系统进行初始化,主要完成对开关的响应、发送指令等功能的控制,起到总控和协调各模块之间工作的作用。接入信号发生器后,单片机内部自带的ADC进行采样,经过FFT变换后变成频谱图显示在液晶屏上。系统主要包括主控器STM32F103ZET6,数模转换电路,LCD液晶屏显示电路,晶振电路以及外加复位电路组成。本设计的特点是装置一体化,外接输入信号后,便可直接经过单片机处理,实时显示频谱图以及相关参数,真正做到一步到位。

基于STM32的频谱分析和波形识别系统_第1张图片

3、软件设计

 该系统软件程序主要实现的就是对输入波形进行采样,然后通过FFT函数对采集到的信号进行处理,最后量化、频谱显示。还有一点就是分析不同输入信号在频域上的特征,利用该特征进行波形识别。

我直接使用了DSP库里面有FFT函数库,主要调用了里面的几个函数进行频谱分析,例如GetPowerMag()函数,其作用就是提取各次谐波的频率和对应的幅值,效率非常高,使用也方便,直接调用库函数即可。

至于波形识别的原理则是根据不同波形的特点来区分。正弦波只有基波分量,基本无谐波分量;方波除了基波,还有357次谐波分量,且3次谐波分量为基波分量的1/3;三角波除了基波,还有357次谐波分量,且3次谐波分量为基波分量的1/9;锯齿波除了基波,还有234次谐波分量,不同波形的特征分析如下图所示。

基于STM32的频谱分析和波形识别系统_第2张图片

基于STM32的频谱分析和波形识别系统_第3张图片

基于STM32的频谱分析和波形识别系统_第4张图片

基于STM32的频谱分析和波形识别系统_第5张图片

主函数

#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "stm32_dsp.h"
#include "lcd.h"
#include "adc.h"
#include "fft.h"

/*   AD采样引脚:PC1      */
/*   采样频率  :2000Hz   */
/*   幅度3.0VPP  Z直流偏移量1.5V */

int main(void)
 {
	int i;

  delay_init();	    	 //延时函数初始化	  
	uart_init(115200);	
  LCD_Init();
  TIM1_Int_Init(500-1,72-1);//2000Hz的采样频率
		//TIM1_Int_Init(1000-1,30-1);//2400Hz的采样频率
	 //TIM1_Int_Init(74,4);    //192KHz采样频率(72MHz/5=14.4MHz,计数到75,定时为5/72M*75=1/192K)
	 //TIM1_Int_Init(75-1,150-1);    //9.6kHz采样频率(72MHz/5=14.4MHz,计数到150,定时为5/72M*75=1/9.6K)
	ADC1_Configuration();   //ADC初始化
	DMA_Configuration();    //DMA初始化
	//InitBufInArray2();//测试用的
	 LCD_Clear(BLACK);

	while(1)
	{
		for(i=0;i

    fft.c

#include "fft.h"
#include "math.h"
#include "lcd.h"
#include "delay.h"
u32 lBufInArray[NPT];
u32 lBufOutArray[NPT];
u32 lBufMagArray[NPT];
u32 lBUFPHASE[NPT];
float PI2=6.28318530717959;
float PI=3.14159265358979;
u32 Fs=2000;                     
/******************************************************************
函数名称:InitBufInArray()
函数功能:模拟采样数据,采样数据中包含3种频率正弦波
参数说明:
备    注:在lBufInArray数组中,每个数据的高16位存储采样数据的实部,
          低16位存储采样数据的虚部(总是为0)
*******************************************************************/
void InitBufInArray(void)
{
    unsigned short i;
    float fx;                                       //Fn=i*Fs/NPT		//由于此处i是从0开始的,所以不需要再减1
    for(i=0; i> 16;  //lX  = lBufOutArray[i];
        lY  = (lBufOutArray[i] >> 16);
			                                 
        X = NPT * ((float)lX) / 32768;//除以32768再乘65536是为了符合浮点数计算规律,不管他
        Y = NPT * ((float)lY) / 32768;
        Mag = sqrt(X * X + Y * Y) / NPT;
        if(i == 0)
            lBufMagArray[i] = (unsigned long)(Mag * 32768);   //0Hz是直流分量,直流分量不需要乘以2
        else
            lBufMagArray[i] = (unsigned long)(Mag * 65536);
    }
}


void PowerPhase(u16 nfill)

{
    unsigned short i;
    signed short lX,lY;  

		for (i=0; i < NPT/2; i++)
		{
						lX= (lBufOutArray[i]<<16)>>16; /* 取低16bit,sine_cosine --> cos */
						lY= (lBufOutArray[i] >> 16);   /* 取高16bit,sine_cosine --> sin */    
						{
								float X=  NPT*((float)lX)/32768;
								float Y = NPT*((float)lY)/32768;
								float phase = atan(Y/X);
								 if (Y>=0)
								 {
										if (X>=0)
											;
										else
										 phase+=PI;  
								 }
								 else
								 {             
										if (X>=0)
											phase+=PI2;                  
										else 
											phase+=PI;                    
								 }                            
								lBUFPHASE[i] = phase*180.0/PI;
						}    
				}
}

void lcd_show_fft(unsigned int *p)
{
	unsigned int *pp = p+1;             //p+1相当于我直接把0HZ部分滤掉了
	unsigned int i = 0;
	for(i = 0;i<480;i++)
	{
		//分辨率hz
		//每个小矩形宽度为1,其实这里没有显示完所有的
    //512个值,频率可达到 Hz	
    //0.11是我根据屏幕显示高度调整的一个值,频谱闪的话记得改这个值!!!!! 320*240屏幕		320*=780
		LCD_Fill(0,        i, *pp*0.11, (i+1), RED);     //有效部分白色       
		LCD_Fill(*pp*0.11, i, 270,       (i+1), BLACK);   //其他就黑色
    pp++;
	}
	
}
/***********************************************
找最大值,次大值……对应的频率,分析波形
*************************************************/
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];
        }
    }
    POINT_COLOR=WHITE;	//画笔颜色
    BACK_COLOR=BLACK;  //背景色 
    LCD_ShowFloat4(270,0,fMax,4,24);
		LCD_ShowFloat3(270,16*2,aMax,4,24);
		LCD_ShowFloat4(270,16*5,fSecondMax,4,24);
		LCD_ShowFloat3(270,16*7,aSecondMax,4,24);
		LCD_ShowFloat4(270,16*10,fThirdMax,4,24);
		LCD_ShowFloat3(270,16*12,aThirdMax,4,24);
		LCD_ShowFloat4(270,16*15,fFourthMax,4,24);
		LCD_ShowFloat3(270,16*17,aFourthMax,4,24);
		
    k=fabs(2*fMax-fSecondMax);
		k1=fabs(3*fMax-fSecondMax);
		m=fabs((float)(aMax-3.0*aSecondMax));
//		LCD_ShowFloat3(270,370,k,4,24);
//		LCD_ShowFloat3(270,400,k1,4,24);
//		LCD_ShowFloat3(270,430,m,4,24);
		POINT_COLOR=RED;	//画笔颜色
		if(k<=5)
			  LCD_ShowString(270,340,24*4,24,24,"Sawtooth");
		else if(k1<=5&&m<0.3) 
			  LCD_ShowString(270,340,24*4,24,24," Square ");
		else if(k1<=5&&m>=0.3)
			  LCD_ShowString(270,340,24*4,24,24,"triangle");
		else LCD_ShowString(270,340,24*4,24,24,"  Sine  ");
}


void lcd_print_fft(unsigned int *p)
{
	unsigned int *pp = p;             //p+1相当于直接把0HZ部分滤掉了(改成了不过滤)
	unsigned int i = 0,j = 0;
  float f[NPT/2]={0.00},a[NPT/2]={0.00};
	for(i=0;i80)//看情况调,若是数字太跳就调大,把小的幅值过滤,以防干扰
		{
			f[j]=(float)i*Fs/NPT;
			//LCD_ShowFloat4(0,j*12,f[j],6,12);
			a[j]=(float)*pp*(3.3/4096);
			//LCD_ShowFloat4(100,j*12,a[j],2,12);
			j++;
		}
		pp++;
  }
	select_max(f,a);
}

   adc.c

#include 
#include "string.h"
#include "adc.h"
#include "sys.h"
#include "delay.h"
#include "lcd.h"
#include "stm32_dsp.h"
#include "fft.h"

u16 ADC_Value[NPT];


void ADC1_Configuration(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO ,ENABLE ); //使能 ADC1 通道时钟,各个管脚时钟
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC 最大时间不能超过 14M
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
																		
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;//PC1 作为模拟通道输入引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
	GPIO_Init(GPIOC, &GPIO_InitStructure);

	ADC_DeInit(ADC1); //将外设 ADC1 的全部寄存器重设为缺省值
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1 和 ADC2工作在独立模式
	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 数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的 ADC 通道的数目
	ADC_Init(ADC1, &ADC_InitStructure); //根据 ADC_InitStruct 中指定的参数初始化外设ADCx 的寄存器

	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5 );
	
	ADC_ExternalTrigConvCmd(ADC1, ENABLE);   //外部触发	
  
	ADC_DMACmd(ADC1, ENABLE);// 开启 ADC 的 DMA 支持(要实现 DMA 功能,还需独立配置 DMA 通道等参数)
	
	ADC_Cmd(ADC1, ENABLE); //使能指定的 ADC1
	ADC_ResetCalibration(ADC1); //复位指定的 ADC1 的校准寄存器
	while(ADC_GetResetCalibrationStatus(ADC1)); //获取 ADC1 复位校准寄存器的状态,设置状态则等待
	ADC_StartCalibration(ADC1); //开始指定 ADC1 的校准状态
	while(ADC_GetCalibrationStatus(ADC1)); //获取指定 ADC1 的校准程序,设置状态则等待
}


void DMA_Configuration(void)
{
		
	DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

	DMA_DeInit(DMA1_Channel1); //将 DMA 的通道 1 寄存器重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA 外设 ADC 基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value; //DMA 内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //内存作为数据传输的目的地
	DMA_InitStructure.DMA_BufferSize = NPT; //DMA 通道的 DMA 缓存的大小(1024)
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为 16 位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16 位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA 通道 x 拥有高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA 通道 x 没有设置为内存到内存传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据 DMA_InitStruct 中指定的参数初始DMA 的通道
	
/*  因为要显示刷屏,所以没用DMA中断  */	
//	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
//	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//	NVIC_Init(&NVIC_InitStructure);	
//	DMA_ITConfig(DMA1_Channel1, DMA_IT_TC , ENABLE);       //开启转换完成中断
		
	DMA_Cmd(DMA1_Channel1, ENABLE);
}


/*  因为要显示刷屏,所以没用DMA中断  */	
void ADC1_DMA1_IT_Hander(void)
{
	int i;
	if(DMA_GetFlagStatus(DMA1_FLAG_TC1))
	{
		for(i=0;i

 4、测试结果

基于STM32的频谱分析和波形识别系统_第6张图片基于STM32的频谱分析和波形识别系统_第7张图片

 基于STM32的频谱分析和波形识别系统_第8张图片基于STM32的频谱分析和波形识别系统_第9张图片

 基于STM32的频谱分析和波形识别系统_第10张图片基于STM32的频谱分析和波形识别系统_第11张图片

 基于STM32的频谱分析和波形识别系统_第12张图片基于STM32的频谱分析和波形识别系统_第13张图片

 实验数据分析

参数

波形

基波

频率

理论值

基波

频率

测量值

2次谐波频率

量值

3次谐波频率

测量值

4次谐波频率

测量值

5次谐

波频率

测量值

7次谐

波频率

测量值

基波频率测量误差Δ

Sine

90

89.84

0

0

0

0

0

0.17%

Square

120

119.14

0

359.37

0

599.60

839.84

0.71%

Triangle

120

119.14

0

359.37

0

0

0

0.71%

Sawtooth

200

199.21

400.39

599.60

800.78

0

0

0.39%

工程源码:基于STM32的频谱分析和波形识别系统-单片机文档类资源-CSDN下载

你可能感兴趣的:(STM32实战分享,stm32,单片机,arm)