YOUXIU0125 2026-01-27 11:04 采纳率: 0%
浏览 1

stm32 fft数字滤波 实际出错

问题背景

本人大一电子类专业,有过一些stm32代码经验,目前在尝试stm32 fft算法的数字滤波,我尝试自己写了一个,但是实际测试时看不到目标现象

基本设置

我用cubemx帮我生成和配置一些基本的架构

img

img

img

img

img

img

img

main代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dac.h"
#include "dma.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "math.h"
#include "arm_math.h"
#include "arm_const_structs.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define adc_value_size 1024
#define fft_sample 1024
#define Fs 400000
#define FFT_LENGTH 1024
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint16_t adc1_buffer[adc_value_size];


float fft_inputbuf1[fft_sample * 2];  
float fft_inputbuf2[fft_sample * 2];
float fft_outputbuf1[fft_sample];
float fft_outputbuf2[fft_sample];

uint16_t  dac_out1[fft_sample];
uint16_t  dac_out2[fft_sample];
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint8_t dmaCompleteFlag=0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
    if(hadc==&hadc1 )
    {
        dmaCompleteFlag = 1;        
    }    
}
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
arm_cfft_radix4_instance_f32 scfft;
arm_cfft_radix4_instance_f32 scifft;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_DAC_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  /* USER CODE BEGIN 2 */
    
    arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);
    arm_cfft_radix4_init_f32(&scifft,FFT_LENGTH,1,1);
    
    HAL_TIM_Base_Start (&htim3 );
    HAL_TIM_Base_Start (&htim4 );
    
    HAL_ADC_Start_DMA (&hadc1 ,(uint32_t*)adc1_buffer,adc_value_size );
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
                if(dmaCompleteFlag)
        {
            dmaCompleteFlag=0;
            HAL_TIM_Base_Stop (&htim3 );
            HAL_ADC_Stop_DMA (&hadc1 );
            HAL_TIM_Base_Stop (&htim4 );
            
                    for (int i = 0; i < FFT_LENGTH; i++)
        {
            fft_inputbuf1[i * 2] = adc1_buffer[i] * 3.3f / 4096.0f;
            fft_inputbuf1[i * 2 + 1] = 0.0f;
            fft_inputbuf2[i * 2] = adc1_buffer[i] * 3.3f / 4096.0f;
            fft_inputbuf2[i * 2 + 1] = 0.0f;
        }
        arm_cfft_radix4_f32(&scfft,fft_inputbuf1);
        arm_cfft_radix4_f32(&scfft,fft_inputbuf2);
//        arm_cmplx_mag_f32(fft_inputbuf1,fft_outputbuf1,FFT_LENGTH);
//        arm_cmplx_mag_f32(fft_inputbuf1,fft_outputbuf2,FFT_LENGTH);    
        
        for (uint16_t i = 14; i <(FFT_LENGTH - 13); i++)
        {
            fft_inputbuf1[2 * i] = 0.0f;
            fft_inputbuf1[2 * i + 1] = 0.0f;
        }
        arm_cfft_radix4_f32 (&scifft ,fft_inputbuf1 );
        for (uint16_t i = 0; i < FFT_LENGTH; i++)
{
    fft_inputbuf1[2 * i] /= (float)FFT_LENGTH; 
 
}
for (uint16_t i = 0; i < FFT_LENGTH; i++)
{
    float temp = fft_inputbuf1[2 * i] * 4096.0f / 3.3f;
    if (temp < 0.0f) temp = 0.0f;                        
    if (temp > 4095.0f) temp = 4095.0f;                  
    dac_out1[i] = (uint16_t)temp;                        
}
        
        
        
        for (uint16_t i = 1; i < 14; i++)
        {
            fft_inputbuf2[2 * i] = 0.0f;
            fft_inputbuf2[2 * i + 1] = 0.0f;
            
            uint16_t sym_idx = FFT_LENGTH - i;
            fft_inputbuf2[2 * sym_idx] = 0.0f;
            fft_inputbuf2[2 * sym_idx + 1] = 0.0f;
        }
        arm_cfft_radix4_f32 (&scifft ,fft_inputbuf2 );
        for (uint16_t i = 0; i < FFT_LENGTH; i++)
{
    fft_inputbuf2[2 * i] /= (float)FFT_LENGTH;

}
for (uint16_t i = 0; i < FFT_LENGTH; i++)
{
    float temp = fft_inputbuf2[2 * i] * 4096.0f / 3.3f; 
    if (temp < 0.0f) temp = 0.0f;                        
    if (temp > 4095.0f) temp = 4095.0f;                 
    dac_out2[i] = (uint16_t)temp;                       
}
            
        HAL_TIM_Base_Start (&htim4 );
        HAL_DAC_Start_DMA (&hdac ,DAC_CHANNEL_1 ,(uint32_t *)dac_out1 ,FFT_LENGTH ,DAC_ALIGN_12B_R );
        HAL_DAC_Start_DMA (&hdac ,DAC_CHANNEL_2 ,(uint32_t *)dac_out2 ,FFT_LENGTH ,DAC_ALIGN_12B_R );

            
            
            
            
            
            
        
            
            HAL_TIM_Base_Start (&htim3  );
            HAL_ADC_Start_DMA (&hadc1 ,(uint32_t *)adc1_buffer ,adc_value_size );
        }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */



运行结果

运行没有报错没有警告,能够正常烧录进单片机

代码思路

单片机进行adc采集,采集结果fft后将对应频率范围的数组变量置零实现滤波,再通过dac输出滤波后的结果

我想要达到的结果

我不知道是哪里出了问题,想知道问题所在和怎样用fft实现数字滤波

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-01-27 11:15
    关注

    上午好☀️☀️☀️️
    本答案参考通义千问

    你目前的STM32项目中,尝试使用FFT进行数字滤波,但实际测试时没有看到目标现象。这是一个常见的问题,可能涉及多个方面,包括信号采集、FFT配置、滤波逻辑、输出处理等。


    ✅ 一、问题分析

    1. 信号采集问题

    • 你通过DMA从ADC获取数据,但未检查是否正确读取了ADC值。
    • ADC的采样率(Fs)为400000Hz,而FFT长度为1024,这意味着每秒有400个完整的FFT帧。这个频率是合理的,但需确认ADC是否稳定工作。

    2. FFT输入格式错误

    • 你在 fft_inputbuf1[i * 2]fft_inputbuf1[i * 2 + 1] 中写入了实部和虚部,但实际上应将ADC值作为实部,虚部设为0。这部分是正确的,但需要确保数组大小足够。

    3. FFT初始化与计算

    • 你使用了 arm_cfft_radix4_init_f32 初始化FFT结构体,但未调用 arm_cfft_radix4_f32() 函数进行实际的FFT运算。
    • 你定义了两个FFT实例:scfftscifft,但只在代码中初始化了它们,并未实际使用。

    4. FFT输出处理

    • FFT的结果是复数形式,你需要对实部和虚部进行平方求和得到幅值,再根据频率分辨率确定滤波范围。

    5. DAC输出未实现

    • 你定义了 dac_out1dac_out2,但没有实际将处理后的信号送入DAC,导致无法看到滤波效果。

    ✅ 二、解决方案(有序列表)

    1. 确保ADC数据正确采集

    • 检查DMA是否正常工作,可以添加调试打印或LED指示灯来判断是否完成一次ADC转换。
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    {
        if (hadc == &hadc1) {
            dmaCompleteFlag = 1;
            // 可以在此处添加调试信息
            // printf("ADC data received\n");
        }
    }
    

    2. 执行FFT计算

    • 在DMA回调函数中,调用 arm_cfft_radix4_f32() 进行FFT计算。
    if (dmaCompleteFlag) {
        dmaCompleteFlag = 0;
    
        // 执行FFT
        arm_cfft_radix4_f32(&scfft, fft_inputbuf1);
    
        // 或者如果你打算做IFFT
        // arm_cfft_radix4_f32(&scifft, fft_outputbuf1);
    }
    

    3. 处理FFT结果并进行滤波

    • 对FFT结果进行幅值计算,然后进行滤波。
    for (int i = 0; i < FFT_LENGTH / 2; i++) {
        float real = fft_inputbuf1[i * 2];
        float imag = fft_inputbuf1[i * 2 + 1];
        float magnitude = sqrtf(real * real + imag * imag);
        
        // 根据频率范围进行滤波(例如保留0~1kHz)
        if (i < 100) { // 假设100对应1kHz
            // 保留该频段
            fft_outputbuf1[i] = magnitude;
        } else {
            // 高通滤波,置零
            fft_outputbuf1[i] = 0.0f;
        }
    }
    

    4. IFFT还原信号

    • 如果你想从频域返回时域信号,可以调用IFFT。
    // IFFT
    arm_cfft_radix4_f32(&scifft, fft_outputbuf1);
    
    // 然后归一化
    for (int i = 0; i < FFT_LENGTH; i++) {
        fft_outputbuf1[i] /= FFT_LENGTH;
    }
    

    5. 将处理后的信号输出到DAC

    • 将FFT处理后的信号写入DAC缓冲区,并通过DMA发送。
    // 将处理后的信号转为DAC输出
    for (int i = 0; i < FFT_LENGTH; i++) {
        dac_out1[i] = (uint16_t)(fft_outputbuf1[i] * 4096.0f / 3.3f);
    }
    
    // 发送DAC数据
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_out1[0]);
    HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)dac_out1, FFT_LENGTH, DAC_OUTPUT_BUFFER_ENABLE);
    

    ✅ 三、修改后的完整代码片段(关键部分)

    /* USER CODE BEGIN PFP */
    void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
    {
        if (hadc == &hadc1) {
            dmaCompleteFlag = 1;
        }
    }
    /* USER CODE END PFP */
    
    /* USER CODE BEGIN 0 */
    void ProcessSignal(void) {
        if (dmaCompleteFlag) {
            dmaCompleteFlag = 0;
    
            // 填充FFT输入
            for (int i = 0; i < FFT_LENGTH; i++) {
                fft_inputbuf1[i * 2] = adc1_buffer[i] * 3.3f / 4096.0f;
                fft_inputbuf1[i * 2 + 1] = 0.0f;
            }
    
            // 执行FFT
            arm_cfft_radix4_f32(&scfft, fft_inputbuf1);
    
            // 处理FFT结果(简单低通滤波)
            for (int i = 0; i < FFT_LENGTH / 2; i++) {
                float real = fft_inputbuf1[i * 2];
                float imag = fft_inputbuf1[i * 2 + 1];
                float magnitude = sqrtf(real * real + imag * imag);
                
                if (i < 100) {
                    fft_outputbuf1[i] = magnitude;
                } else {
                    fft_outputbuf1[i] = 0.0f;
                }
            }
    
            // IFFT还原
            arm_cfft_radix4_f32(&scifft, fft_outputbuf1);
    
            // 归一化
            for (int i = 0; i < FFT_LENGTH; i++) {
                fft_outputbuf1[i] /= FFT_LENGTH;
            }
    
            // 转换为DAC输出
            for (int i = 0; i < FFT_LENGTH; i++) {
                dac_out1[i] = (uint16_t)(fft_outputbuf1[i] * 4096.0f / 3.3f);
            }
    
            // 发送DAC数据
            HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, dac_out1[0]);
            HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)dac_out1, FFT_LENGTH, DAC_OUTPUT_BUFFER_ENABLE);
        }
    }
    /* USER CODE END 0 */
    

    ✅ 四、建议与注意事项

    • 调试方法:可以在每次DMA完成后打印ADC值或FFT结果,观察是否符合预期。
    • FFT长度选择:确保FFT长度是2的幂次方,如1024、2048等。
    • DAC精度:确保DAC输出的数值在0~4095范围内,避免溢出。
    • 滤波算法:可以根据需求设计更复杂的滤波器(如带通、高通、低通),而不是简单的阈值过滤。

    ✅ 五、总结

    你的代码基本结构是正确的,但缺少FFT的实际计算和DAC输出部分。重点在于:

    1. 调用 arm_cfft_radix4_f32() 执行FFT计算。
    2. 对FFT结果进行滤波处理。
    3. 将处理后的信号送入DAC输出。

    如果你按照上述步骤修改代码,应该可以看到预期的滤波效果。如果仍有问题,建议逐步调试,比如先验证ADC是否正常,再逐步增加FFT和滤波功能。

    评论

报告相同问题?

问题事件

  • 创建了问题 1月27日