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,选择HSE为Crystal/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浮点单元
相关推荐
TechVoyager_82461 天前
多形态输入与12 通道音频:IT6615 这颗 HDMI 发射器的接口宽度
音视频·音频芯片·芯片选型·视频发射
Raymond16881 天前
AI配音工具长文本合成架构技术解析:全局音色锚定与声学状态跟踪
人工智能·架构·实时互动·视觉检测·音视频·语音识别
沉淀的.晴天1 天前
can总线
stm32
桀人1 天前
认识AT24C02
stm32·单片机·嵌入式硬件
hflmwl81 天前
Wise Data Recovery找回误删的文档照片音乐和视频
音视频·datarecovery·找回误删的文档照片音乐和视频·找回误删的文档照片音乐视频
技灵AI1 天前
AI 视频转场提示词怎么写?让前后画面衔接更自然
人工智能·prompt·aigc·音视频
youandyouyou1 天前
跨区培训的屏幕共享怎么做才稳?同屏同步与弱网优化实践
音视频·网络通信
开开心心就好1 天前
课堂随机点名工具,绿色版免安装双击即用
java·服务器·前端·ffmpeg·音视频·perl·symfony
美狐美颜SDK开放平台1 天前
直播APP集成美颜SDK,如何梳理第三方组件的数据权限?
开发语言·人工智能·深度学习·音视频·第三方美颜sdk
inrichtt1 天前
第十一课时:串口中断接收与回调函数实战解析
c语言·数据结构·stm32·单片机·嵌入式硬件