基于STM32F103C8T6的FFT频率计算器详解

之前项目中需要用到正弦信号的频率测量,也参考了几个大佬的博客(链接如下),但可能是由于stm32的型号不匹配,虽然也在网上查了一些需要修改的地方,但结果一直不太对,后来经过自己摸索结果终于对了,在这里给大家分享下,具体原理不在赘述。
参考的部分大佬博客(stm32f103zet6):
链接1: https://blog.csdn.net/weixin_43368814/article/details/103552114.
链接2: https://blog.csdn.net/weixin_42616791/article/details/108419412.

优缺点

1、fft方式测频率,峰峰值可以最低为20mV,输入捕获方式,峰峰值需要达到七、八百mV以上,才可以精准测量;
2、程序的测量精度利用信号发生器进行了验证(下面表格的数据基本是在峰峰值20mV下测得,峰峰值更大些精度会更高些。

信号发生器输出 实测
10khz 9961hz
15khz 15040hz
20khz 20022hz
25khz 25000hz
30khz 29987hz
35khz 34968hz

3、在峰峰值的峰谷(顶点)小于1.6v时,该方式可以测量的最小峰峰值为20mv(信号发生器的最小峰峰值为20mV),一旦峰底值(最小值)超过1.6V(峰峰值仍为20mV),测量结果就不准了,误差很大(我暂时没找到原因,有知道原因的大佬,欢迎评论区解答);

代码

头文件

#include <stdio.h>
#include "string.h"
#include "fft_calculate.h"
#include "adc.h"

#define ADC1_DR_Address    ((uint32_t)0x4001244C)
uint16_t ADC_Value[NPT];

adc(PA6,定时器2外部触发)

void Adc_Init(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		ADC_InitTypeDef ADC_InitStructure;
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);	  
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	  

		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
		GPIO_Init(GPIOA, &GPIO_InitStructure);  
		ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; 		
		ADC_InitStructure.ADC_ScanConvMode = DISABLE; 
		ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
		ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; 
		ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
		ADC_InitStructure.ADC_NbrOfChannel = 1; 
		ADC_Init(ADC1, &ADC_InitStructure);
		RCC_ADCCLKConfig(RCC_PCLK2_Div6);
		ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_1Cycles5);
		
		//使能ADC、DMA
		ADC_DMACmd(ADC1,ENABLE);
		ADC_Cmd(ADC1,ENABLE);
	 
		ADC_ResetCalibration(ADC1);	
		while(ADC_GetResetCalibrationStatus(ADC1));
	 
		ADC_StartCalibration(ADC1);				
		while(ADC_GetCalibrationStatus(ADC1));	
		ADC_ExternalTrigConvCmd(ADC1, ENABLE);		
}

DMA(一次接收1024点数据)

void DMA1_Init(void)
{
		DMA_InitTypeDef DMA_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;

		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);	  			
		DMA_DeInit(DMA1_Channel1);
		DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;			
		DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Value; 	
		DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 				
		DMA_InitStructure.DMA_BufferSize = NPT; 			
		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_Circular  ; 		
		DMA_InitStructure.DMA_Priority = DMA_Priority_High ; 		
		DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;   		
		DMA_Init(DMA1_Channel1, &DMA_InitStructure);  
		DMA_ClearITPendingBit(DMA1_IT_TC1);
		DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);	

		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_Cmd(DMA1_Channel1,ENABLE);
}

定时器

void TIM2_Init(void)
{
		TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
		TIM_OCInitTypeDef TIM_OCInitStructure;
		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 		//时钟使能

		TIM_TimeBaseStructure.TIM_Period = arr; 		//设置在下一个更新事件装入活动的自动重装载寄存器周期的值
		TIM_TimeBaseStructure.TIM_Prescaler =psc; 			//设置用来作为TIMx时钟频率除数的预分频值
		TIM_TimeBaseStructure.TIM_ClockDivision = 0; 		//设置时钟分割:TDTS = Tck_tim
		TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 		//TIM向上计数模式
		TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);			//根据指定的参数初始化TIMx的时间基数单位
		
		TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;		//选择定时器模式:TIM脉冲宽度调制模式1
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;		//比较输出使能
		TIM_OCInitStructure.TIM_Pulse = 9;
		TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;		//输出极性:TIM输出比较极性低
		TIM_OC2Init(TIM2, & TIM_OCInitStructure);		//初始化外设TIM2_CH2
		
		TIM_Cmd(TIM2, ENABLE); 			//使能TIMx
}

中断处理函数(不需要在.h文件中定义,main中直接调用)

void  DMA1_Channel1_IRQHandler(void)
{
	u16 i = 0;
	if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET)
	{
		for(i=0;i<NPT;i++)
		{
			InBufArray[i] = ((signed short)(ADC_Value[i])) << 16;	
		}
		DMA_ClearITPendingBit(DMA1_IT_TC1);
	}
}

#最后附上程序下载地址
链接: 链接:https://pan.baidu.com/s/1CuYofbGRXZIoccYkexHHzw
.提取码:fp1q

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32F103C8T6的FFT频率计算器详解

发表评论