STM32F407FFT识别正弦波信号的频率

目录

一、硬件设备

二、大概思路


一、硬件设备

信号发生器(用于测试)或者任意其他输入信号、STM32F407ZGT6系列单片机、若干杜邦线、ST-Link烧录器

二、大概思路

1.ADC频率采样所输入信号,DMA读取传输

2.使用STM官网所给的DSP库对信号进行FFT频谱处理

3.对频谱分析得的结果进一步分析,得到输入信号正弦波的频率大小

4.OLED屏或者TFT屏显示

三、主要程序

(一)ADC采样和DMA读取传输

.c文件

1.配置ADC的GPIO

#include "adc_dma_timer.h"
u16 ADC1_ConvertedValue[ ADC1_DMA_Size ];
//配置ADC的GPIO:PB1(ADC1的通道9)
void ADC_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB , ENABLE ); 
	
	//PB0 - ADC1_IN8   
	//PB1 - ADC1_IN9   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init( GPIOB , &GPIO_InitStructure );
}

2.配置ADC的TIM3

//配置ADC的TIM3
//Fre为ADC的采样频率
void TIM3_Config( u32 Fre )
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	u32 MD;
	u16 div=1;	
	while( (SystemCoreClock/2/Fre/div)>65535 )
	{
		div++;
	}
	MD =  SystemCoreClock/2/Fre/div - 1;	
	RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3 , ENABLE );			   //开启TIM3时钟
	
	//TIM3配置
	TIM_TimeBaseStructure.TIM_Period = MD ;
	TIM_TimeBaseStructure.TIM_Prescaler = div-1;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit( TIM3 , &TIM_TimeBaseStructure );
	
	TIM_SelectOutputTrigger( TIM3 , TIM_TRGOSource_Update );
	TIM_ARRPreloadConfig( TIM3 , ENABLE );
	TIM_Cmd( TIM3 , ENABLE );							                  //使能TIM3
}

3.配置ADC的DMA

ADC1对应的DMA2通道

#define ADC1_DR_ADDRESS  ((uint32_t)0x4001204C)          //DMA读取的ADC1地址

//ADC-DMA配置
//Size为单次传输的数据量
void ADC_DMA_Trig( u16 Size )                    
{
	DMA2_Stream0->CR &= ~((uint32_t)DMA_SxCR_EN);				//将DMA2的stream0线清零
	DMA2_Stream0->NDTR = Size;                             //设置DMA2的stream0
	DMA_ClearITPendingBit( DMA2_Stream0 ,DMA_IT_TCIF0|DMA_IT_DMEIF0|DMA_IT_TEIF0|DMA_IT_HTIF0|DMA_IT_TCIF0 );
	ADC1->SR = 0;
	DMA2_Stream0->CR |= (uint32_t)DMA_SxCR_EN;//使能DMA2的stream0线
}	
 
 
//ADC—DMA相关配置
//ADC、DMA和NVIC中断的初始化
void ADC_Config(void)
{
	ADC_InitTypeDef       ADC_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	DMA_InitTypeDef       DMA_InitStructure;
	NVIC_InitTypeDef 	  NVIC_InitStructure;
 
	ADC_DeInit( );
	DMA_DeInit( DMA2_Stream0 );
	
	RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2 , ENABLE ); 
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1 , ENABLE );

	

	NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);     
 

ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;     //独立模式·1
	ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;		//2分频
	ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
	ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
	ADC_CommonInit( &ADC_CommonInitStructure );
 
	
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;		//不使能循环扫描
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//配置为单次转换
	ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;选择上升沿触发
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;    //选择TIM3的ADC触发
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//右对齐									
	ADC_InitStructure.ADC_NbrOfConversion = 1;                    
	ADC_Init( ADC1, &ADC_InitStructure );//初始化ADC
	
	 


	ADC_RegularChannelConfig( ADC1 , ADC_Channel_9 , 1, ADC_SampleTime_3Cycles);     
 
	ADC_DMARequestAfterLastTransferCmd( ADC1 , ENABLE );
	
	ADC_DMACmd(ADC1, ENABLE);
	ADC_Cmd(ADC1, ENABLE);
	ADC_SoftwareStartConv(ADC1);
	
	DMA_InitStructure.DMA_Channel = DMA_Channel_0;  
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS;//DMA读取的目的地		 
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC1_ConvertedValue; //MDA输出的地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
	DMA_InitStructure.DMA_BufferSize = ADC1_DMA_Size;                   //DMA 传输数量
	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_Normal;	//单次传输(可以改为循环)													
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;		//	分组    
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;	
	DMA_Init( DMA2_Stream0 , &DMA_InitStructure );
	DMA_Cmd( DMA2_Stream0 , DISABLE );
	
	DMA_ClearITPendingBit( DMA2_Stream0 ,DMA_IT_TCIF0 );
	DMA_ITConfig( DMA2_Stream0 , DMA_IT_TC , ENABLE );
 
	
	
}



//DMA中断配置
void DMA2_Stream0_IRQHandler( void )
{	
	DMA_ClearITPendingBit( DMA2_Stream0 ,DMA_IT_TCIF0|DMA_IT_DMEIF0|DMA_IT_TEIF0|DMA_IT_HTIF0|DMA_IT_TCIF0 );
	DMA2->HIFCR = 0xffff;
	DMA2->LIFCR = 0xffff;

}

.h文件

#ifndef _ADC_DMA_Timer_H
#define _ADC_DMA_Timer_H
 
#include "sys.h" 
#include "STM32F4xx_ADC.h" 

#define SAM_FRE        1024000//采样频率
#define ADC1_DMA_Size  1024 //采样点数
extern u16 ADC1_ConvertedValue[ ADC1_DMA_Size ]; 
void ADC_GPIO_Init(void);
void TIM3_Config( u32 Fre );
void ADC_Config( void );
void ADC_DMA_Trig( u16 Size );
 
#endif

(二)主函数(FFT变换)

在STM官网下载407系列单片机的DSP固件库,在Keil中添加相关的库

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "adc_dma_timer.h"
#include "key.h"
#include "timer.h" 
#include "math.h" 
#include "arm_math.h"  
#include "lcd.h"
#include "lcd_init.h"



#define FFT_LENGTH		1024 		//FFT长度
float fft_inputbuf[FFT_LENGTH*2];	//FFT输入数组
float fft_outputbuf[FFT_LENGTH];	//FFT输出数组
float fft_outputbufhalf[FFT_LENGTH/2];
u32 i=0;
int newvalue = 0;
int a;

int main(void)
{  
		arm_cfft_radix4_instance_f32 scfft;
		u8 key,t=0;
		float time; 
		u16 i; 

		int max1,max2 ;
		float max;
		int n=0;
		float A1,A2;
//相关函数的初始化		
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
		delay_init(168);  
		uart_init(115200);		
	
		LED_Init();					
		KEY_Init();							
	
		ADC_GPIO_Init();						    
		TIM3_Config( SAM_FRE );                     
		ADC_Config();                              	
		arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//³õʼ»¯scfft½á¹¹Ì壬É趨FFTÏà¹Ø²ÎÊý
	

	
	
  	while(1) 
	{		 

				key=KEY_Scan(0);
		if(key==KEY0_PRES)
		{			 ADC_DMA_Trig( ADC1_DMA_Size );          // 开启ADC采集
            


           delay_ms(20);  // 延时20ms。让采样到的数据传输到数组中

//FFT(傅里叶变换)

			for(i=0;i<FFT_LENGTH;i++)//生产信号序列
			{
			fft_inputbuf[2*i] = (float)ADC1_ConvertedValue[ i ]*3.3f/4096.0f;//ADC实部
//

				fft_inputbuf[2*i+1]=0;//虚部为0
			}
			
			arm_cfft_radix4_f32(&scfft,fft_inputbuf);	//进行FFT计算
			arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//求模值

//将FFT计算的结果通过串口显示在电脑上			
			
//			printf("%f\n",fft_outputbufhalf[0]);
			for(i = 0;i<FFT_LENGTH/2;i++)
			{
				fft_outputbufhalf[i]=fft_outputbuf[i];
				fft_outputbufhalf[0]= 0.0;
				printf("fft_outputbufhalf[%d]:%f\r\n",i,fft_outputbufhalf[i]);
			}
//	适用排序法选出两个正弦的基波分量
		for( i=0;i<512;i++)
		{
			
			if(max<fft_outputbufhalf[i])
			{
				max = fft_outputbufhalf[i];
				max1 = i;
				
			}
			
		}
			
		fft_outputbufhalf[max1]= 0.0,max = 0.0;
		for( i=0;i<512;i++)
		{
			fft_outputbufhalf[0]= 0.0;
			if(max<fft_outputbufhalf[i])
			{
				max =fft_outputbufhalf[i];
				max2= i;
			}
			
		}
}
		
		
		
		
	
	
		else delay_ms(10);
		t++;
		if((t%10)==0)LED0=!LED0;		  

		
		
		
		}
	
		
	}	

(三)LCD显示

这里用的TFT屏进行显示

主函数中的max1、max2的大小乘以1K即为输入正弦信号的频率大小

四、附件功能(单片机DAC口输入对应的波形)或DDS复现信号

运用单片机的DAC口或DDS对识别的信号进行复现,使用DAC口输出信号需要用硬件电路进行处理才能得到稳定的信号。

五、FFT原理

正弦信号进行FFT变换后会有较为明显的特征,在所对应的频率处会有一个较大的值,这个值所对应的频率即为正弦信号的频率,这里是以10K的正弦信号为例。

  • 4
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值