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

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

目录

1、概述

2、硬件设计

3、软件设计

 4、测试结果


1、概述

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

2、硬件设计

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

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

3、软件设计

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

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

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

主函数

#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、测试结果

 

 

 

 实验数据分析

参数

波形

基波

频率

理论值

基波

频率

测量值

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%

欢迎分享,转载请注明来源:内存溢出

原文地址:http://outofmemory.cn/langs/921866.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

随机推荐

  • 2017年谢菲尔德大学商科

      谢菲尔德大学(The University of Sheffield),英国常春藤大学之一,英国顶级大学,世界百强名校,位于英国第四大中心城市谢菲尔德市,建校历史可追溯到1828年。谢菲尔德大学是

    2022-07-07
    300
  • 2017年皇家音乐学院音乐设施

    据立思辰广州留学360江兆果老师介绍,皇家音乐学院由威尔斯亲王(后为国王爱德华七世)于1883年创立,并被接纳为皇家协会成员。学院院长常为皇室成员担任,从1952年起院长由王太后殿下担任,女王陛下为该

    2022-07-07
    000
  • 泰国诗纳卡宁威洛大学

    诗纳卡宁威洛大学好不好?立思辰留学360介绍,泰国诗纳卡宁威洛大学(SrinakharinwirotUniversity),简称“诗大”,是泰国首都曼谷一间著名国立大学。该校始创于1949年,迄今已有

    2022-07-07
    000
  • 泰国阳光按摩学校概况

      泰国阳光按摩学校概况泰式按摩在Asokananda的推广之下,由一种传统的东方按摩方式变成了一种世界公认的按摩艺术。Asokananda使泰式按摩得以踵事增华。而泰国阳光按摩学校将继承Asokan

    2022-07-07
    300
  • 2017年牛津大学好不好

    牛津教学的最大特点是“导师制”。学生的导师由研究人员担任,他们多为品学俱佳的学者,在一定的领域卓有建树。导师制要求学生每周与导师见一次面,将自己一周内研究和撰写的论文向导师宣读。此外,还有许多讲座。立

    2022-07-07
    000
  • 西雅图城市大学很水吗

      西雅图城市大学很水吗?西雅图城市大学属正规院校,具体请咨询立思辰留学360美国留学顾问,热线电话:4008-941-360院校介绍美国西雅图城市大学(City University of Seat

    2022-07-07
    300
  • 新加坡楷博高等教育有哪些专业

    新加坡楷博高等教育学院在新加坡教育部正式注册,2010年学校获得了新加坡私立教育理事会颁发的教育信托保障计划认证(EduTrust),而且还获得中国驻新加坡大使馆教育处的资格认证,为海外学生在优质教育

    2022-07-07
    300
  • 2017年伦敦大学亚非学院与伯恩茅斯大学哪个好

      伦敦大学亚非学院与伯恩茅斯大学都有自己的优势专业,具体请咨询立思辰留学360专业顾问团队,咨询电话:4008-941-360伦敦大学亚非学院东方与非洲研究学院 (School of Orienta

    2022-07-07
    300
  • 2017年美国纽约康考迪亚学院优势

    纽约康考迪亚学院优势有哪些纽约康考迪亚学院(简称CCNY) 是一所位于纽约Bronxville的文理学院,成立于1881年,具有悠久的基督教传统,校风优良。学校坐落在全美著名的富人区,环境优雅,周边人

    2022-07-07
    300

发表评论

登录后才能评论

评论列表(0条)