STM32实战:基于STM32F407的FFT频谱分析(音频信号处理)

文章目录

    • 一、项目概述
    • 二、硬件准备
      • [2.1 硬件清单](#2.1 硬件清单)
      • [2.2 硬件接线](#2.2 硬件接线)
    • 三、开发环境搭建
    • 四、STM32CubeMX工程配置
      • [4.1 新建工程](#4.1 新建工程)
      • [4.2 系统时钟配置](#4.2 系统时钟配置)
      • [4.3 ADC配置(音频信号采集)](#4.3 ADC配置(音频信号采集))
      • [4.4 串口配置(数据打印)](#4.4 串口配置(数据打印))
      • [4.5 启用CMSIS-DSP库(FFT核心)](#4.5 启用CMSIS-DSP库(FFT核心))
      • [4.6 工程生成](#4.6 工程生成)
    • 五、工程流程图
    • 六、代码实现
      • [6.1 代码文件创建说明](#6.1 代码文件创建说明)
      • [6.2 文件1:main.c](#6.2 文件1:main.c)
      • [6.3 文件2:adc.c(ADC驱动文件)](#6.3 文件2:adc.c(ADC驱动文件))
      • [6.4 文件3:usart.c(串口驱动文件)](#6.4 文件3:usart.c(串口驱动文件))
      • [6.5 文件4:dsp.h(DSP库头文件)](#6.5 文件4:dsp.h(DSP库头文件))
    • [七、Keil MDK编译配置(关键步骤)](#七、Keil MDK编译配置(关键步骤))
      • [7.1 开启浮点运算单元](#7.1 开启浮点运算单元)
      • [7.2 添加DSP库路径](#7.2 添加DSP库路径)
      • [7.3 编译工程](#7.3 编译工程)
    • 八、程序下载与调试
      • [8.1 下载程序](#8.1 下载程序)
      • [8.2 串口调试](#8.2 串口调试)
    • 九、原理讲解
      • [9.1 音频信号采集](#9.1 音频信号采集)
      • [9.2 FFT快速傅里叶变换](#9.2 FFT快速傅里叶变换)
      • [9.3 频谱数据含义](#9.3 频谱数据含义)
    • 十、项目扩展(进阶优化)
    • 十一、常见问题解决

一、项目概述

本项目基于STM32F407ZGT6开发板,实现音频信号的实时采集、FFT(快速傅里叶变换)频谱分析,并通过串口/液晶屏输出频谱数据,完整落地嵌入式音频信号处理实战。

  • 核心硬件:STM32F407、驻极体麦克风(音频采集)、ADC外设
  • 核心算法:CMSIS-DSP库FFT(STM32官方优化,速度远超手写FFT)
  • 输出方式:串口打印频谱数据、可扩展OLED/TFT液晶屏显示
  • 适用人群:零基础嵌入式开发者、电子竞赛、音频处理入门

二、硬件准备

2.1 硬件清单

  1. STM32F407ZGT6开发板(任意F407核心板均可)
  2. 驻极体麦克风模块(带放大电路,输出模拟信号)
  3. 杜邦线若干
  4. USB转TTL模块(串口调试)
  5. 5V电源(供电)

2.2 硬件接线

麦克风模块 STM32F407
VCC 3.3V
GND GND
OUT PA0

说明:PA0对应STM32F407的ADC1_IN0通道,用于采集音频模拟信号。

三、开发环境搭建

本项目使用STM32CubeMX+Keil MDK5开发,零基础也能快速配置。

  1. 安装STM32CubeMX(6.0以上版本)
  2. 安装Keil MDK5,添加STM32F4固件库
  3. 安装USB转TTL驱动,准备串口调试助手

四、STM32CubeMX工程配置

4.1 新建工程

  1. 打开STM32CubeMX,点击ACCESS TO MCU SELECTOR
  2. 搜索STM32F407ZG,选择芯片,点击Start Project

4.2 系统时钟配置

  1. 点击RCC,选择HSECrystal/Ceramic Resonator(外部晶振)
  2. 点击Clock Configuration,配置系统时钟:
    • HSE:8MHz
    • PLL锁相环:336MHz
    • System Clock:168MHz(STM32F407最大主频)
    • APB1 Prescaler:4,APB2 Prescaler:2

4.3 ADC配置(音频信号采集)

  1. 点击Analog -> ADC1
  2. 勾选IN0(对应PA0)
  3. 参数配置:
    • 分辨率:12位(0~4095)
    • 转换模式:单次转换
    • 对齐方式:右对齐
    • 不开启DMA(基础版先使用阻塞式采集)

4.4 串口配置(数据打印)

  1. 点击USART1,模式选择Asynchronous(异步通信)
  2. 波特率:115200,数据位8,停止位1,无校验
  3. 串口引脚:TX-PA9,RX-PA10(自动分配)

4.5 启用CMSIS-DSP库(FFT核心)

  1. 点击Software Packs -> Add
  2. 选择CMSIS -> DSP,勾选Library
  3. 回到主界面,点击Project Manager

4.6 工程生成

  1. 工程名称:STM32_FFT_Audio
  2. 存储路径:英文路径(禁止中文)
  3. 编译器:MDK-ARM V5
  4. 点击GENERATE CODE,生成工程文件

五、工程流程图

系统初始化
时钟初始化
ADC1初始化
USART1初始化
CMSIS-DSP FFT初始化
循环采集音频
ADC采集音频原始数据
数据存入FFT输入数组
执行FFT变换
计算幅值频谱
串口输出频谱数据

六、代码实现

6.1 代码文件创建说明

所有代码均在STM32CubeMX生成的工程中修改,无需新建工程,严格按照以下文件名和位置添加代码。


6.2 文件1:main.c

路径 :Core/Src/main.c
功能:系统初始化、主循环、ADC采集、FFT调用、串口打印

c 复制代码
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dsp.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>
#include <math.h>

/* Private defines -----------------------------------------------------------*/
// FFT点数 必须是2的N次方 1024点
#define FFT_N 1024

/* Private variables ---------------------------------------------------------*/
// FFT输入输出数组 CMSIS-DSP要求32位浮点型
float32_t fft_input[FFT_N];
float32_t fft_output[FFT_N];
// FFT实例结构体
arm_cfft_instance_f32 fft_instance;
// ADC采集缓存
uint16_t adc_value;

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void FFT_Init(void);
static void ADC_GetData(float32_t *buf, uint16_t len);
static void Calculate_FFT_Magnitude(void);

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  // 1. 系统初始化
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_USART1_UART_Init();
  
  // 2. FFT初始化
  FFT_Init();
  
  // 3. 主循环
  while (1)
  {
    // 采集1024个点音频数据
    ADC_GetData(fft_input, FFT_N);
    
    // 执行FFT变换并计算幅值
    Calculate_FFT_Magnitude();
    
    // 串口打印前100个频谱点
    for(int i=0; i<100; i++)
    {
      printf("FreqPoint[%d]=%.2f\r\n", i, fft_output[i]);
    }
    printf("-------------------------\r\n");
    
    HAL_Delay(500);
  }
}

/**
  * @brief  FFT初始化函数
  * @param  None
  * @retval None
  */
static void FFT_Init(void)
{
  // 初始化1024点浮点FFT
  arm_cfft_init_f32(&fft_instance, FFT_N);
}

/**
  * @brief  ADC采集音频数据 存入FFT输入数组
  * @param  buf: 数据存储数组
  * @param  len: 采集长度
  * @retval None
  */
static void ADC_GetData(float32_t *buf, uint16_t len)
{
  for(uint16_t i=0; i<len; i++)
  {
    // 启动ADC转换
    HAL_ADC_Start(&hadc1);
    // 等待转换完成
    HAL_ADC_PollForConversion(&hadc1, 10);
    // 获取ADC值
    adc_value = HAL_ADC_GetValue(&hadc1);
    // 转换为浮点型 并去除直流偏置
    buf[i] = (float32_t)adc_value - 2048.0f;
  }
}

/**
  * @brief  执行FFT 计算幅值频谱
  * @param  None
  * @retval None
  */
static void Calculate_FFT_Magnitude(void)
{
  // 执行复数FFT变换
  arm_cfft_f32(&fft_instance, fft_input, 0, 1);
  
  // 计算复数幅值
  arm_cmplx_mag_f32(fft_input, fft_output, FFT_N);
}

/**
  * @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 = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  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();
  }
}

/**
  * @brief  重定向printf到串口1
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
  return ch;
}

/**
  * @brief  错误处理函数
  * @retval None
  */
void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */

6.3 文件2:adc.c(ADC驱动文件)

路径 :Core/Src/adc.c
功能:STM32CubeMX自动生成,无需修改,仅确认配置正确


6.4 文件3:usart.c(串口驱动文件)

路径 :Core/Src/usart.c
功能:STM32CubeMX自动生成,无需修改


6.5 文件4:dsp.h(DSP库头文件)

路径 :Drivers/CMSIS/DSP/Include/dsp.h
功能:官方库文件,无需修改,工程已自动引用

七、Keil MDK编译配置(关键步骤)

7.1 开启浮点运算单元

  1. 打开Keil工程,点击魔法棒图标
  2. 选择Target,勾选Use FPU(F407硬件浮点单元)

7.2 添加DSP库路径

  1. 点击C/C++
  2. Include Paths中添加DSP库头文件路径:
    Drivers/CMSIS/DSP/Include

7.3 编译工程

  1. 点击Build(编译)
  2. 无报错、无警告即为配置成功

八、程序下载与调试

8.1 下载程序

  1. 用ST-Link/V2连接开发板
  2. Keil中点击Load,下载程序到STM32F407

8.2 串口调试

  1. 打开串口调试助手
  2. 配置参数:波特率115200,数据位8,停止位1,无校验
  3. 打开串口,即可看到实时打印的频谱数据

九、原理讲解

9.1 音频信号采集

驻极体麦克风将声音转换为模拟电压信号,STM32的ADC将模拟信号转换为数字信号(0~4095),我们将数据减去2048,去除直流偏置,得到正负交替的音频波形数据。

9.2 FFT快速傅里叶变换

FFT是一种快速计算傅里叶变换的算法,能把时域信号(波形)转换为频域信号(频谱)

  • 时域:信号随时间的变化
  • 频域:信号包含哪些频率、幅值多大

9.3 频谱数据含义

串口打印的FreqPoint[x]代表第x个频率点的幅值,幅值越大,说明该频率的声音信号越强。

十、项目扩展(进阶优化)

  1. DMA采集:使用DMA代替阻塞式ADC采集,提升系统实时性
  2. 液晶屏显示:将频谱数据输出到OLED/TFT屏,实现可视化频谱
  3. 滤波处理:添加数字滤波,去除噪声
  4. 频率计算:根据采样率计算实际频率值
  5. 音乐频谱灯:结合PWM控制LED,实现音乐频谱律动

十一、常见问题解决

  1. 串口无数据
    • 检查串口接线、波特率是否匹配
    • 确认printf重定向代码添加正确
  2. FFT数据全为0
    • 检查ADC接线,确认麦克风正常工作
    • 检查ADC初始化配置
  3. 编译报错
    • 确认DSP库已添加
    • 开启FPU浮点单元
相关推荐
d111111111d1 小时前
MQTT+STM32+ESP8266网络程序分层+韦老师
笔记·stm32·单片机·嵌入式硬件·学习·php
LCG元2 小时前
STM32实战:基于STM32F103的编码器电机测速与闭环控制
stm32·单片机·嵌入式硬件
平行侠2 小时前
026FFT快速乘法 - 从信号处理到大数计算的革命
数据结构·算法·信号处理
guygg883 小时前
适用于 STM32 系列单片机的 USB DFU 上位机程序
stm32·单片机·mongodb
小何开发3 小时前
ffmpeg 安装与使用: 将视频分片与组装
ffmpeg·音视频
EasyDSS3 小时前
私有化视频会议系统/智能会议管理系统EasyDSS打造全场景音视频协作新生态
音视频
淘小白_TXB21963 小时前
微博图文视频批量采集软件用户手册
音视频
kaikaile19954 小时前
基于 STM32 的双闭环控制直流无刷电机(BLDC)方案
stm32·单片机·嵌入式硬件
Heartache boy4 小时前
野火STM32_HAL库版课程笔记-DWT应用与DHT11温湿度传感器
笔记·stm32·单片机·嵌入式硬件