DAC-ADC-DSP库FFT获取正弦波频率

DSP与FFT

DSP库的添加

cubemx配置
下载DSP库

在这里插入图片描述
在这里插入图片描述

添加DSP库到工程中

在这里插入图片描述在这里插入图片描述

工程配置
添加宏

在这里插入图片描述

ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING

第一个宏:

M3:ARM_MATH_CM3

M4:ARM_MATH_CM4

H7:ARM_MATH_CM7

include

在这里插入图片描述

#include "arm_math.h"
#include "math.h"
#include "arm_const_structs.h"

#include "arm_const_structs.h" 如果cubemx生成的库没有,可以找板子的例程,将DSP的lib文件添加到工程中
在这里插入图片描述

测试
/* USER CODE BEGIN PV */
uint8_t lcd_id[12];				//存放LCD ID字符串
float data = 0;//定义一个float型变量
float fft_inputbuf[FFT_LENGTH*2];	//FFT输入数组
float fft_outputbuf[FFT_LENGTH];	//FFT输出数组
/* USER CODE END PV */

/* USER CODE BEGIN 1 */
	arm_cfft_radix4_instance_f32 scfft;
	uint8_t key,t=0;
	float time; 
	uint8_t buf[50]; 
	uint16_t i; 
	char send[15];
/* USER CODE END 1 */

/* USER CODE BEGIN 2 */
	LCD_Init();
	POINT_COLOR=RED;      //画笔颜色:红色
	sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将LCD ID打印到lcd_id数组。		
	LCD_ShowString(30,40,210,24,24,"mcudev STM32F4");	
	LCD_ShowString(30,70,200,16,16,"TFTLCD TEST");
	LCD_ShowString(30,90,200,16,16,"mcudev.taobao.com");
	arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数
  /* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
	  data = arm_sin_f32(PI/6);					//计算sin30°
	  sprintf((char*)lcd_id,"LCD ID:%.5f",data);//0.49999
	  LCD_ShowString(30,110,200,16,16,lcd_id);
		for(i=0;i<FFT_LENGTH;i++)//生成信号序列
		{
			 fft_inputbuf[2*i]=100+
							   10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
							   30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
							   50*arm_cos_f32(2*PI*i*8/FFT_LENGTH);	//生成输入信号实部
			//printf("%.2f\r\n",fft_inputbuf[2*i]);
			sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
			HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
			 fft_inputbuf[2*i+1]=0;//虚部全部为0
		}
		printf("\r\n");
		arm_cfft_radix4_f32(&scfft,fft_inputbuf);	//FFT计算(基4)
		printf("after:");
		for(i=0;i<FFT_LENGTH;i++)//生成信号序列
		{
			sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
			HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
		}
		arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//把运算结果复数求模得幅值
		printf("\r\n%d point FFT runtime:%0.3fms\r\n",FFT_LENGTH,time/1000);
		printf("FFT Result:\r\n");
		for(i=0;i<FFT_LENGTH;i++)
		{
			printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf[i]);
		}		
    /* USER CODE END WHILE */

如果没有LCD

/* USER CODE BEGIN PV */
float data = 0;//定义一个float型变量
float fft_inputbuf[FFT_LENGTH*2];	//FFT输入数组
float fft_outputbuf[FFT_LENGTH];	//FFT输出数组
/* USER CODE END PV */

/* USER CODE BEGIN 1 */
	arm_cfft_radix4_instance_f32 scfft;
	uint8_t key,t=0;
	float time; 
	uint8_t buf[50]; 
	uint16_t i; 
	char send[15];
/* USER CODE END 1 */

/* USER CODE BEGIN 2 */
	arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数
  /* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
  while (1)
  {
	  data = arm_sin_f32(PI/6);					//计算sin30°
      //开调试看data值或串口输出
		for(i=0;i<FFT_LENGTH;i++)//生成信号序列
		{
			 fft_inputbuf[2*i]=100+
							   10*arm_sin_f32(2*PI*i/FFT_LENGTH)+
							   30*arm_sin_f32(2*PI*i*4/FFT_LENGTH)+
							   50*arm_cos_f32(2*PI*i*8/FFT_LENGTH);	//生成输入信号实部
			//printf("%.2f\r\n",fft_inputbuf[2*i]);
			sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
			HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
			 fft_inputbuf[2*i+1]=0;//虚部全部为0
		}
		printf("\r\n");
		arm_cfft_radix4_f32(&scfft,fft_inputbuf);	//FFT计算(基4)
		printf("after:");
		for(i=0;i<FFT_LENGTH;i++)//生成信号序列
		{
			sprintf(send,"%.2f\r\n",fft_inputbuf[i*2]);
			HAL_UART_Transmit(&huart1,(uint8_t*)send,8,0xff);
		}
		arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//把运算结果复数求模得幅值
		printf("\r\n%d point FFT runtime:%0.3fms\r\n",FFT_LENGTH,time/1000);
		printf("FFT Result:\r\n");
		for(i=0;i<FFT_LENGTH;i++)
		{
			printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf[i]);
		}		
    /* USER CODE END WHILE */

DAC

cubemx配置
DAC

在这里插入图片描述

  • Trigger:Timer2 Trigger Out event

在这里插入图片描述

DMA点ADD添加,Circular模式

在这里插入图片描述

确认配置无误

TIM2

在这里插入图片描述

  • 时钟频率为168MHz
  • PSC取168-1
  • ARR取5-1

输出频率为:168000000/168/5 = 200000Hz

工程配置
BEGIN0 产生正弦波函数

在这里插入图片描述

/* USER CODE BEGIN 0 */
void SineWave_Data( uint16_t num,uint16_t *D,float U)
{
    uint16_t i;
    for( i=0;i<num;i++)
    {
        D[i]=(uint16_t)((U*sin(( 1.0*i/(num-1))*2*3.14159265358979)+U)*4095/3.3);
    }
}

/* USER CODE END 0 */
BEGIN1 正弦波数组

在这里插入图片描述

 /* USER CODE BEGIN 1 */
	uint16_t Sine12bit[32] = {
		2048	, 2460	, 2856	, 3218	, 3532	, 3786	, 3969	, 4072	,
		4093	, 4031	, 3887	, 3668	, 3382	, 3042	,2661	, 2255	, 
		1841	, 1435	, 1054	, 714	, 428	, 209	, 65	, 3		,
		24		, 127	, 310	, 564	, 878	, 1240	, 1636	, 2048
	};
	
  /* USER CODE END 1 */
BEGIN2 初始化

在这里插入图片描述

//DAC
	TIM2->ARR = 9;						//修改ARR 可不加
	HAL_TIM_Base_Start(&htim2);			//打开定时器中断
	//HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
	SineWave_Data(n,DualSine12bit,1.6);	//产生正弦波函数
	HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)Sine12bit, 32, DAC_ALIGN_12B_R);
输出信号

在这里插入图片描述

ADC

cubemx配置
ADC1

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 定时器3触发
  • 添加DMA
  • 打开中断
TIM3

在这里插入图片描述

在这里插入图片描述

  • 时钟频率168MHz
  • PSC:168-1
  • ARR:2-1
  • Update Event
  • 打开中断
工程配置
BEGIN PD
#define fft_adc_n 1024    // 采1024个点
BEGIN PV
float adc_data[fft_adc_n*2]={0};        // 存ADC值
uint8_t ad_flag = 0;
BEGIN2

在这里插入图片描述

HAL_TIM_Base_Start_IT(&htim3);				//打开定时器3中断
HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&adc_buffer,fft_adc_n);	//ADC采集一组数据
BEGIN4
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    ad_flag=1;
}

ADC中断,采集完成后标志位置1

while(1)
while (1)
  {
	  if(ad_flag == 1)
	  {
          //数据处理
          //......
          
          ad_flag = 0;	//标志位置0
      }
}

FFT

float fft_in_adc_data[fft_adc_n*2]={0}; // FFT输入,实部是ADC值,虚部补0
float fft_out_adc_data[fft_adc_n]={0};  // FFT输出
uint16_t flag[fft_adc_n] = {0};			// 数据处理时暂时存放数据
int k = 1;  							// 找第k大值
float32_t maxValue = 0;					// ADC最大值
uint32_t index = 0;						// 最大值下标
int i,j;
float freq = 0;							// 频率
arm_cfft_radix4_instance_f32 scfft;        //FFT对应结构体变量
arm_cfft_radix4_init_f32(&scfft,fft_adc_n,0,1); //初始化scfft结构体,设置FFT相关参数
if(ad_flag == 1)
{
    printf("finish\r\n");
    ad_flag = 0;
    for(i=0;i<fft_adc_n;i++)
    {
        fft_in_adc_data[i*2] = adc_buffer[i]*3.3/4095;		//实部
        fft_in_adc_data[i*2+1] = 0;							//虚部
    }
    printf("\r\n");
    arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_in_adc_data,0,1);
    arm_cmplx_mag_f32(fft_in_adc_data,fft_out_adc_data,fft_adc_n);
		  
    fft_out_adc_data[0]/=1024;
    for(i=1;i<fft_adc_n;i++)
        fft_out_adc_data[i]/=512;
    for(i=0;i<fft_adc_n;i++)
    {
        printf("%.5f\t",fft_out_adc_data[i]);
    }
    printf("\r\n");
    //arm_max_f32(&fft_out_adc_data[1],fft_adc_n/2-1,&maxValue,&index);

    /****查找最大值及其下标*********/
	memset(flag, 0, sizeof(flag));
  	for (i = 1; i <= k; i++)
  	{
    	maxValue = 0.0;
    	for (j = 1; j < fft_adc_n / 2; j++)
    	{
            if (flag[j] == 1)
                break;
            if (fft_out_adc_data[j] > maxValue)
            {
                maxValue = fft_out_adc_data[j];
                index = j;
            }
		}
    	flag[index] = 1;
  	}
    printf("index:%d\r\n",index);
    printf("Vmax:%.2f\r\n",maxValue);
    freq = (float)500000/fft_adc_n * (index) /2;		//500000为采样率
    
    printf("freq: %.3f",freq);
    ad_flag = 0;
}
  • arm_cfft_sR_f32_len1024arm_const_structs.h里面,如果报错,检查头文件
  • 如果更改采样点数,注意修改arm_cfft_sR_f32_len1024,如果采样512个点,则改为arm_cfft_sR_f32_len512,以此类推
  • fft_out_adc_data[0]为直流分量电压值
  • maxValue为基波电压幅值
  • fft_out_adc_data[0]+maxValue=正弦波最大值
  • fft_out_adc_data[0]-maxValue=正弦波最小值
参考文章

https://blog.csdn.net/Nothing_To_Say_/article/details/123606260

https://blog.csdn.net/qq_34022877/article/details/123190943

https://blog.csdn.net/qq_24426625/article/details/129738537

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值