目录

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()函数,其作用就是提取各次谐波的频率和对应的幅值,效率非常高,使用也方便,直接调用库函数即可。

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

主函数

#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<NPT;i++){lBufInArray[i]=ADC_Value[i]<<16;}cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);    GetPowerMag();lcd_show_fft(lBufMagArray);lcd_print_fft(lBufMagArray);}
}

    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开始的,所以不需要再减1for(i=0; i<NPT; i++)                            //频率分辨率Fs/1024=187.5Hz,所以每一个i就代表187.5Hz{                                               //因为采样频率=Fs,所以屏幕上的一个格子的长度代表(1/Fs)秒fx = 1000 * sin(PI2 * i * 350.0 / Fs) +     //2pi*f*t=2pi*i*f1/Fs,那么f=f13000 * sin(PI2 * i * 8400.0 / Fs) +4000 * sin(PI2 * i * 10000.0 / Fs);lBufInArray[i] = ((signed short)fx) << 16;}
} void InitBufInArray2(void)
{unsigned short i;float fx;for(i=0; i<NPT; i++){fx = 4000 * sin(PI2 * i * 375.2 / Fs) +1000 * sin(PI2 * i * 564.7 / Fs) +1500 * sin(PI2 * i * 937.1 / Fs)+1000;   //加了1000只是直流分量加了1000,其他都不变lBufInArray[i] = ((signed short)fx) << 16;}
}
/******************************************************************
函数名称:GetPowerMag()
函数功能:计算各次谐波幅值                          short 的范围,是-32767 到 32767 。也就是 -(2^15 - 1)到(2^15 - 1)。
参数说明:
备  注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
*******************************************************************/
void GetPowerMag(void)
{signed short lX,lY;                                                  //算频率的话Fn=i*Fs/NPT       //由于此处i是从0开始的,所以不需要再减1float X,Y,Mag;                                                      unsigned short i;for(i=0; i<NPT/2; i++)                                                  //经过FFT后,每个频率点处的真实幅值  A0=lBufOutArray[0]/NPT{                                                                       //                                 Ai=lBufOutArray[i]*2/NPTlX  = (lBufOutArray[i] << 16) >> 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是直流分量,直流分量不需要乘以2elselBufMagArray[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);elsephase+=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*=780LCD_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;i<NPT/2;i++){if(*pp>80)//看情况调,若是数字太跳就调大,把小的幅值过滤,以防干扰{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 <stdio.h>
#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 最大时间不能超过 14MRCC_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); //使能指定的 ADC1ADC_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<NPT;i++){lBufInArray[i]=ADC_Value[i]<<16;}}DMA_ClearITPendingBit(DMA1_FLAG_TC1);
}   

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%

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

基于STM32的频谱分析和波形识别系统相关推荐

  1. 基于STM32的多功能门禁系统(AS608指纹识别、密码解锁、刷卡解锁)

    目录 一.项目功能 二.视频 三.原理图 4.材料选择 5.部分程序 资料下载地址:基于STM32的多功能门禁系统 一.项目功能 1.AS608指纹解锁:可以录入.删除.验证指纹: 2.密码解锁:可以 ...

  2. 毕业设计 基于stm32的RFID与指纹识别的门禁系统 (项目开源)

    文章目录 1 简介 2 绪论 2.1 课题背景与目的 3 射频识别 3.1 射频识别技术 3.2 射频识别模块 3.2.1 RFID模块 3.2.2 RFID模块组成 4 系统设计 4.1 系统架构 ...

  3. 基于STM32的智能宠物看护喂养系统(程序源码+原理图+PCB+Boom表+实物操作说明)

    基于STM32的智能宠物看护喂养系统(程序源码+原理图+PCB+Boom表+实物操作说明) **0  引言** 随着我国经济的快速发展,人们的生活水平不断提高,饲养宠物已经成为成为一种潮流.目前,市场 ...

  4. B45 - 基于STM32单片机的家庭防火防盗系统的设计

    文章目录 设计要求 实物 人脸识别模块的调试 传感器采集与显示 模块设计 温湿度模块 烟雾浓度MQ2 GSM模块 人脸识别模块 语音报警模块 资源占用 源程序 主程序 处理逻辑 AD/DA转换检测烟雾 ...

  5. opencv交通标志识别_教你从零开始做一个基于深度学习的交通标志识别系统

    教你从零开始做一个基于深度学习的交通标志识别系统 基于Yolo v3的交通标志识别系统及源码 自动驾驶之--交通标志识别 在本文章你可以学习到如何训练自己采集的数据集,生成模型,并用yolo v3算法 ...

  6. android声纹识别技术,基于Android平台的声纹识别系统的研究与实现

    摘要: 社会的发展越来越快,计算机技术的应用也愈来愈广,已经渗透到生活的各个方面.在快节奏.信息化的时代,需要识别和交互的应用日益广泛,要求验证身份的场合越来越多,迅速判定一个人的身份是一个非常重要的 ...

  7. 基于opencv和pillow实现人脸识别系统(附demo)

    更多python教程请到友情连接: 菜鸟教程https://www.piaodoo.com 初中毕业读什么技校 http://cntkd.net 茂名一技http://www.enechn.com p ...

  8. 基于深度学习的单人步态识别系统

    基于深度学习的单人步态识别系统(目前数据集大小15人,准确率100%) 一.数据预处理 a.步态轮廓图 b.头部轮廓图 实现方式: 核心代码: c.骨架图 二.提取步态特征 a.角度特征 b.下肢特征 ...

  9. 基于YOLOv7的室内场景智能识别系统(源码&教程)

    1.项目背景: 近年来,随着移动互联网与定位技术的发展,基于位置服务越来越多地出现在人们的日常生活中.虽然智能手机都包含很多基于位置服务的应用,但是传统的基于位置服务常常将服务范围划分为室内与室外两种 ...

最新文章

  1. ssh实现基于密钥方式登录系统
  2. 关于微机开操作票的研究22437
  3. 限时分享:产品经理面试高频考题及答案
  4. python简单可视化聊天界面_如何用Python制作可视化输入界面
  5. 太赞了!2020年全网最新Java面试题(附答案)免费下载!超全!!
  6. python程序、画一个笑脸_如何使用canvas画一个微笑的表情(代码示例)
  7. oracle目录删除后恢复,Oracle删除后不能重新安装的解决方法
  8. shell分割参数为数组并循环执行jar,异常时退出循环
  9. cf B. Internet Address
  10. linux ^H^H^
  11. LinQ Group By
  12. 国外稳定的免费PHP空间byethost.com
  13. vod点歌系统服务器,冰河家庭VOD点歌系统
  14. unity c# 触摸屏物体识别桌算法
  15. .NET/C#大型项目研发必备(12)--使用MQ消息队列
  16. word2016拼音指南功能无效解决方案
  17. 88E1111配置及使用
  18. 大型传统企业如何向人工智能转型?
  19. springboot毕设项目公共场所安保信息管理系统v2rtn(java+VUE+Mybatis+Maven+Mysql)
  20. python小问题:依赖包/conda-forge/conda无法安装包/librosa/

热门文章

  1. 错误笔记之未报错系列
  2. 郑州微信附近推广告投放效果
  3. 达梦数据库中的nvl,ifnull,和isnull函数
  4. 微信营销推广的意义体现在哪些方面呢?
  5. 江西省2021年普通高校专升本考试招生实施方案
  6. 证件扫描OCR识别技术
  7. iOS截取正六边形图片
  8. 关于日文和中文系统乱码的问题
  9. 颠覆传统显示方式,电子墨水屏落地场景迎爆发式增长
  10. 2023年软件测试技能竞赛ERP资源协同系统测试用例模板