目录
1、概述
2、硬件设计
3、软件设计
4、测试结果
本篇介绍了以STM32F103单片机为核心的频谱分析和波形识别系统,并对其硬件组成和软件设计做了详细讲解。该系统通过STM32F103ZET6主控芯片进行ADC采样,再使用DSP库提供的FFT函数对采集到的信号进行处理,最后将输入信号的频谱图显示在TFTLCD液晶屏上,同时显示波形相关参数以及波形种类。
该装置由于是直接使用单片机开发板进行二次开发,所用单片机的芯片引脚图如下图3-2所示。因此整体上硬件较为简单。硬件主要包括信号发生器,正点原子精英板,4.3’TFTLCD,两根杜邦线,我选择使用PC1引脚接信号发生器的输出,作为该系统的输入信号,另一根杜邦线接信号发生器的GND。
本系统以STM32F103单片机为控制核心,对系统进行初始化,主要完成对开关的响应、发送指令等功能的控制,起到总控和协调各模块之间工作的作用。接入信号发生器后,单片机内部自带的ADC进行采样,经过FFT变换后变成频谱图显示在液晶屏上。系统主要包括主控器STM32F103ZET6,数模转换电路,LCD液晶屏显示电路,晶振电路以及外加复位电路组成。本设计的特点是装置一体化,外接输入信号后,便可直接经过单片机处理,实时显示频谱图以及相关参数,真正做到一步到位。
该系统软件程序主要实现的就是对输入波形进行采样,然后通过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
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
实验数据分析
参数 波形 |
基波 频率 理论值 |
基波 频率 测量值 |
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下载