STM32 ADC音频采样与FFT频谱分析实现

一、系统架构设计

1.1 硬件配置

复制代码
STM32F103C8T6最小系统板
├── 音频输入:驻极体麦克风 + 偏置电路
├── 信号调理:LM358运放放大 + 抗混叠滤波
├── ADC采样:STM32内部ADC1
├── 数据处理:CMSIS-DSP库FFT
└── 结果显示:OLED显示屏 / 串口输出

1.2 技术参数

参数 数值 说明
采样率 16kHz 满足语音频率范围
FFT点数 256/512 平衡精度和速度
频率分辨率 62.5Hz 16kHz/256
音频范围 100Hz-8kHz 语音主要频段
ADC分辨率 12位 4096级量化

二、源码实现

2.1 主程序(main.c)

c 复制代码
/**
  * @file main.c
  * @brief STM32 ADC音频采样与FFT频谱分析
  * @version 2.0
  */
#include "stm32f10x.h"
#include "delay.h"
#include "adc_audio.h"
#include "fft_process.h"
#include "oled.h"
#include "uart.h"
#include "arm_math.h"

/* 系统参数 */
#define SAMPLE_RATE         16000       // 采样率16kHz
#define FFT_SIZE            256         // FFT点数
#define ADC_BUFFER_SIZE    (FFT_SIZE * 2) // 双缓冲

/* 全局变量 */
volatile uint8_t adc_dma_complete = 0;
uint16_t adc_buffer[ADC_BUFFER_SIZE];
float32_t fft_input[FFT_SIZE];
float32_t fft_output[FFT_SIZE];
float32_t magnitude[FFT_SIZE/2];
AudioData audio_data;

/* FFT实例 */
arm_rfft_instance_f32 fft_instance;
arm_cfft_radix4_instance_f32 cfft_instance;

int main(void)
{
    /* 1. 系统初始化 */
    System_Init();
    
    /* 2. 外设初始化 */
    Delay_Init();
    OLED_Init();
    UART_Init(115200);
    ADC_Audio_Init(SAMPLE_RATE, adc_buffer, ADC_BUFFER_SIZE);
    FFT_Init(&fft_instance, FFT_SIZE);
    
    /* 3. 显示启动信息 */
    OLED_ShowString(0, 0, "Audio FFT Analyzer");
    OLED_ShowString(0, 2, "Sample: 16kHz");
    OLED_ShowString(0, 4, "FFT: 256 pts");
    OLED_Refresh();
    Delay_Ms(2000);
    
    /* 4. 启动ADC DMA采样 */
    ADC_Start_DMA();
    
    /* 5. 主循环 */
    while(1)
    {
        /* 5.1 等待ADC DMA完成 */
        if(adc_dma_complete)
        {
            adc_dma_complete = 0;
            
            /* 5.2 处理音频数据 */
            Process_AudioData(adc_buffer, FFT_SIZE);
            
            /* 5.3 执行FFT */
            FFT_Process(&fft_instance, fft_input, fft_output, magnitude);
            
            /* 5.4 显示频谱 */
            Display_Spectrum(magnitude, FFT_SIZE/2);
            
            /* 5.5 串口输出峰值频率 */
            float peak_freq = Find_PeakFrequency(magnitude, FFT_SIZE/2, SAMPLE_RATE);
            UART_SendFreqData(peak_freq, audio_data.rms_level);
            
            /* 5.6 重启ADC采样 */
            ADC_Start_DMA();
        }
        
        /* 5.7 其他任务 */
        Handle_UserInput();
    }
}

/* 系统初始化 */
void System_Init(void)
{
    /* 系统时钟配置:72MHz */
    SystemClock_Init();
    
    /* 中断优先级分组 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    /* 初始化全局变量 */
    memset(&audio_data, 0, sizeof(AudioData));
    audio_data.sample_rate = SAMPLE_RATE;
    audio_data.fft_size = FFT_SIZE;
}

2.2 ADC音频采样驱动(adc_audio.c)

c 复制代码
/**
  * @file adc_audio.c
  * @brief ADC音频采样驱动(带DMA)
  */
#include "adc_audio.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_tim.h"

/* 外部变量 */
extern volatile uint8_t adc_dma_complete;

/* ADC初始化 */
void ADC_Audio_Init(uint32_t sample_rate, uint16_t* buffer, uint16_t buffer_size)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    
    /* 1. 使能时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
    /* 2. 配置ADC引脚(PA0) */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 3. 配置定时器触发ADC */
    TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / sample_rate - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    
    /* 选择TIM2触发输出 */
    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    TIM_Cmd(TIM2, ENABLE);
    
    /* 4. ADC配置 */
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  // 定时器触发
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    /* 配置ADC通道 */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    
    /* 5. DMA配置 */
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = buffer_size;
    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);
    
    /* 6. 使能DMA传输完成中断 */
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    
    /* 7. 使能ADC DMA */
    ADC_DMACmd(ADC1, ENABLE);
    
    /* 8. 使能ADC */
    ADC_Cmd(ADC1, ENABLE);
    
    /* 9. ADC校准 */
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

/* 启动ADC DMA采样 */
void ADC_Start_DMA(void)
{
    DMA_Cmd(DMA1_Channel1, ENABLE);
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);
}

/* DMA中断处理函数 */
void DMA1_Channel1_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC1))
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);
        adc_dma_complete = 1;
    }
}

2.3 FFT处理模块(fft_process.c)

c 复制代码
/**
  * @file fft_process.c
  * @brief FFT频谱分析处理
  */
#include "fft_process.h"
#include "arm_math.h"
#include "math.h"

/* FFT初始化 */
void FFT_Init(arm_rfft_instance_f32* instance, uint16_t fft_size)
{
    /* 初始化RFFT */
    arm_rfft_init_f32(instance, fft_size, 0, 1);
    
    /* 初始化CFFT(用于幅度计算) */
    arm_cfft_radix4_init_f32(&cfft_instance, fft_size, 0, 1);
}

/* 处理音频数据 */
void Process_AudioData(uint16_t* adc_buffer, uint16_t fft_size)
{
    /* 1. 将ADC数据转换为浮点数并归一化 */
    for(uint16_t i = 0; i < fft_size; i++)
    {
        /* ADC值范围:0-4095,转换为:-1.0 到 1.0 */
        fft_input[i] = ((float32_t)adc_buffer[i] - 2048.0f) / 2048.0f;
    }
    
    /* 2. 应用汉宁窗减少频谱泄漏 */
    Apply_Hanning_Window(fft_input, fft_size);
    
    /* 3. 计算RMS电平 */
    audio_data.rms_level = Calculate_RMS(fft_input, fft_size);
}

/* 执行FFT */
void FFT_Process(arm_rfft_instance_f32* instance, 
                 float32_t* input, 
                 float32_t* output, 
                 float32_t* magnitude)
{
    uint16_t half_size = instance->fftLen >> 1;
    
    /* 1. 执行RFFT */
    arm_rfft_f32(instance, input, output);
    
    /* 2. 计算复数模值(幅度谱) */
    for(uint16_t i = 0; i < half_size; i++)
    {
        float32_t real = output[2*i];
        float32_t imag = output[2*i+1];
        magnitude[i] = sqrtf(real*real + imag*imag) / (float32_t)half_size;
    }
}

/* 应用汉宁窗 */
void Apply_Hanning_Window(float32_t* data, uint16_t size)
{
    for(uint16_t i = 0; i < size; i++)
    {
        float32_t window = 0.5f * (1.0f - cosf(2.0f * M_PI * i / (size - 1)));
        data[i] *= window;
    }
}

/* 计算RMS电平 */
float32_t Calculate_RMS(float32_t* data, uint16_t size)
{
    float32_t sum = 0.0f;
    
    for(uint16_t i = 0; i < size; i++)
    {
        sum += data[i] * data[i];
    }
    
    return sqrtf(sum / size);
}

/* 寻找峰值频率 */
float32_t Find_PeakFrequency(float32_t* magnitude, uint16_t size, uint32_t sample_rate)
{
    uint16_t peak_index = 0;
    float32_t peak_value = 0.0f;
    
    /* 跳过直流分量(前几个点) */
    for(uint16_t i = 3; i < size; i++)
    {
        if(magnitude[i] > peak_value)
        {
            peak_value = magnitude[i];
            peak_index = i;
        }
    }
    
    /* 计算频率 */
    return (float32_t)peak_index * sample_rate / (size * 2);
}

/* 计算频率分量能量 */
float32_t Calculate_FrequencyEnergy(float32_t* magnitude, 
                                     uint16_t start_bin, 
                                     uint16_t end_bin)
{
    float32_t energy = 0.0f;
    
    for(uint16_t i = start_bin; i <= end_bin; i++)
    {
        energy += magnitude[i] * magnitude[i];
    }
    
    return energy;
}

2.4 OLED显示模块(oled_display.c)

c 复制代码
/**
  * @file oled_display.c
  * @brief OLED频谱显示
  */
#include "oled_display.h"
#include "oled.h"
#include "fft_process.h"

/* 显示频谱 */
void Display_Spectrum(float32_t* magnitude, uint16_t size)
{
    char str[20];
    uint8_t bar_height[16];
    uint8_t max_bar_height = 32;  // 最大条高
    
    OLED_Clear();
    
    /* 1. 显示标题 */
    OLED_ShowString(0, 0, "Audio Spectrum");
    
    /* 2. 将频谱分为16个频段显示 */
    uint16_t bins_per_bar = size / 16;
    
    for(uint8_t i = 0; i < 16; i++)
    {
        float32_t sum = 0.0f;
        for(uint16_t j = 0; j < bins_per_bar; j++)
        {
            sum += magnitude[i * bins_per_bar + j];
        }
        sum /= bins_per_bar;
        
        /* 对数缩放,增强视觉效果 */
        bar_height[i] = (uint8_t)(logf(sum * 1000.0f + 1.0f) * 8.0f);
        if(bar_height[i] > max_bar_height) bar_height[i] = max_bar_height;
    }
    
    /* 3. 绘制频谱条 */
    for(uint8_t i = 0; i < 16; i++)
    {
        uint8_t x = i * 8;
        uint8_t height = bar_height[i];
        
        /* 绘制柱状图 */
        for(uint8_t h = 0; h < height; h++)
        {
            OLED_DrawPoint(x, 63-h, 1);
            OLED_DrawPoint(x+1, 63-h, 1);
            OLED_DrawPoint(x+2, 63-h, 1);
            OLED_DrawPoint(x+3, 63-h, 1);
        }
    }
    
    /* 4. 显示频率刻度 */
    sprintf(str, "0Hz");
    OLED_ShowString(0, 7, str);
    sprintf(str, "8kHz");
    OLED_ShowString(80, 7, str);
    
    OLED_Refresh();
}

/* 显示音频信息 */
void Display_AudioInfo(float32_t peak_freq, float32_t rms_level)
{
    char str[20];
    
    OLED_Clear();
    
    /* 显示峰值频率 */
    sprintf(str, "Peak: %4.0f Hz", peak_freq);
    OLED_ShowString(0, 0, str);
    
    /* 显示RMS电平 */
    sprintf(str, "RMS: %5.3f V", rms_level);
    OLED_ShowString(0, 2, str);
    
    /* 显示音量条 */
    uint8_t volume_bar = (uint8_t)(rms_level * 20.0f);
    if(volume_bar > 16) volume_bar = 16;
    
    for(uint8_t i = 0; i < volume_bar; i++)
    {
        OLED_ShowString(i * 8, 4, "|");
    }
    
    OLED_Refresh();
}

2.5 串口输出(uart_output.c)

c 复制代码
/**
  * @file uart_output.c
  * @brief 串口输出频谱数据
  */
#include "uart_output.h"
#include "uart.h"
#include "fft_process.h"

/* 发送频谱数据(CSV格式) */
void UART_SendSpectrumData(float32_t* magnitude, uint16_t size, uint32_t sample_rate)
{
    char buffer[128];
    
    /* 发送CSV头部 */
    UART_SendString("Frequency(Hz),Magnitude\n");
    
    /* 发送每个频点的数据 */
    for(uint16_t i = 0; i < size; i++)
    {
        float32_t freq = (float32_t)i * sample_rate / (size * 2);
        sprintf(buffer, "%.1f,%.6f\n", freq, magnitude[i]);
        UART_SendString(buffer);
    }
}

/* 发送音频信息 */
void UART_SendFreqData(float32_t peak_freq, float32_t rms_level)
{
    char buffer[128];
    
    sprintf(buffer, "Peak Frequency: %.1f Hz, RMS Level: %.3f V\n", 
            peak_freq, rms_level);
    UART_SendString(buffer);
}

/* 发送实时频谱(简化版) */
void UART_SendRealTimeSpectrum(float32_t* magnitude, uint16_t size)
{
    char buffer[256];
    uint16_t index = 0;
    
    /* 格式化频谱数据 */
    index += sprintf(buffer + index, "FFT:");
    for(uint16_t i = 0; i < size; i += 4)  // 每4个点取一个
    {
        index += sprintf(buffer + index, "%.3f ", magnitude[i]);
    }
    index += sprintf(buffer + index, "\n");
    
    UART_SendString(buffer);
}

2.6 音频信号处理头文件(audio_process.h)

c 复制代码
/**
  * @file audio_process.h
  * @brief 音频信号处理头文件
  */
#ifndef __AUDIO_PROCESS_H
#define __AUDIO_PROCESS_H

#include <stdint.h>
#include <stdbool.h>

/* 音频数据结构 */
typedef struct {
    float32_t sample_rate;        // 采样率
    float32_t rms_level;          // RMS电平
    float32_t peak_frequency;     // 峰值频率
    float32_t fundamental_freq;   // 基频
    float32_t harmonic_ratio;     // 谐波比
    uint16_t fft_size;            // FFT大小
    bool voice_detected;          // 语音检测
} AudioData;

/* 频率范围定义 */
#define VOICE_LOW_FREQ      300.0f   // 语音低频
#define VOICE_HIGH_FREQ     3400.0f  // 语音高频
#define MUSIC_LOW_FREQ      20.0f    // 音乐低频
#define MUSIC_HIGH_FREQ     8000.0f  // 音乐高频

/* 函数声明 */
void Process_AudioData(uint16_t* adc_buffer, uint16_t fft_size);
void FFT_Process(arm_rfft_instance_f32* instance, 
                 float32_t* input, 
                 float32_t* output, 
                 float32_t* magnitude);
void FFT_Init(arm_rfft_instance_f32* instance, uint16_t fft_size);
float32_t Find_PeakFrequency(float32_t* magnitude, uint16_t size, uint32_t sample_rate);
float32_t Calculate_FrequencyEnergy(float32_t* magnitude, 
                                     uint16_t start_bin, 
                                     uint16_t end_bin);
void Apply_Hanning_Window(float32_t* data, uint16_t size);
float32_t Calculate_RMS(float32_t* data, uint16_t size);

#endif /* __AUDIO_PROCESS_H */

三、Keil工程配置

3.1 工程设置

复制代码
Target:
  - Device: STM32F103C8T6
  - Clock: 8MHz HSE → 72MHz PLL
  - RAM: 20KB
  - Flash: 64KB

C/C++:
  - Define: USE_STDPERIPH_DRIVER, STM32F103C8T6
  - Include Paths:
    .\
    ..\Libraries\CMSIS\Include
    ..\Libraries\STM32F10x_StdPeriph_Driver\inc
    ..\Libraries\CMSIS\DSP\Include
    ..\User
    ..\User\drivers
    ..\User\dsp

Linker:
  - IROM1: 0x08000000, 0x10000  (64KB Flash)
  - IRAM1: 0x20000000, 0x5000   (20KB RAM)

3.2 CMSIS-DSP库配置

复制代码
1. 下载CMSIS-DSP库
2. 将以下文件添加到工程:
   - arm_common_tables.c
   - arm_const_structs.c
   - arm_rfft_f32.c
   - arm_cfft_f32.c
   - arm_math.c
3. 在工程选项中添加DSP库路径

四、硬件电路设计

4.1 音频输入电路

复制代码
麦克风电路:
    驻极体麦克风
        │
        ├── 10kΩ偏置电阻 → 3.3V
        │
        ├── 0.1μF耦合电容
        │
        ├── LM358放大电路(增益20倍)
        │
        ├── RC低通滤波(8kHz截止)
        │
        └── STM32 PA0 (ADC输入)

4.2 信号调理电路

复制代码
LM358放大电路:
        R1 = 10kΩ
        R2 = 100kΩ
        增益 = 1 + R2/R1 = 11倍
        
抗混叠滤波:
        R = 2kΩ
        C = 0.01μF
        截止频率 = 1/(2πRC) ≈ 8kHz

参考代码 通过stm32 adc采样音频信号进行fft频谱分析 www.youwenfan.com/contentcsu/56495.html

五、测试与调试

5.1 测试代码

c 复制代码
/* 测试1:ADC采样测试 */
void Test_ADC_Sampling(void)
{
    uint16_t adc_value;
    
    ADC_Audio_Init(SAMPLE_RATE, adc_buffer, ADC_BUFFER_SIZE);
    ADC_Start_DMA();
    
    while(1)
    {
        if(adc_dma_complete)
        {
            adc_dma_complete = 0;
            
            /* 打印ADC值 */
            for(int i = 0; i < 10; i++)
            {
                printf("ADC[%d] = %d\n", i, adc_buffer[i]);
            }
            
            ADC_Start_DMA();
            Delay_Ms(1000);
        }
    }
}

/* 测试2:FFT功能测试 */
void Test_FFT_Function(void)
{
    /* 生成测试信号:1kHz正弦波 */
    for(int i = 0; i < FFT_SIZE; i++)
    {
        fft_input[i] = sinf(2.0f * M_PI * 1000.0f * i / SAMPLE_RATE);
    }
    
    /* 执行FFT */
    FFT_Process(&fft_instance, fft_input, fft_output, magnitude);
    
    /* 查找峰值 */
    float peak_freq = Find_PeakFrequency(magnitude, FFT_SIZE/2, SAMPLE_RATE);
    printf("Peak Frequency: %.1f Hz\n", peak_freq);
}

5.2 调试输出示例

复制代码
Audio FFT Analyzer
Sample: 16kHz
FFT: 256 pts

Peak Frequency: 523 Hz
RMS Level: 0.245 V
Voice Detected: Yes

Spectrum Data:
0Hz:     0.001
1000Hz:  0.856  <- 峰值
2000Hz:  0.023
3000Hz:  0.012
...

六、优化建议

6.1 性能优化

c 复制代码
/* 使用双缓冲减少延迟 */
uint16_t adc_buffer_a[FFT_SIZE];
uint16_t adc_buffer_b[FFT_SIZE];
volatile uint8_t active_buffer = 0;

/* 使用硬件加速 */
#ifdef __FPU_PRESENT
    #define USE_ARM_FFT 1
#else
    #define USE_FIXED_POINT_FFT 1
#endif

6.2 功能扩展

c 复制代码
/* 语音活动检测(VAD) */
bool Voice_ActivityDetection(float32_t* magnitude)
{
    float32_t voice_energy = Calculate_FrequencyEnergy(magnitude, 
                                                       VOICE_LOW_FREQ, 
                                                       VOICE_HIGH_FREQ);
    return (voice_energy > 0.1f);
}

/* 频谱特征提取 */
void Extract_SpectralFeatures(float32_t* magnitude, AudioFeatures* features)
{
    features->centroid = Spectral_Centroid(magnitude);
    features->bandwidth = Spectral_Bandwidth(magnitude);
    features->flatness = Spectral_Flatness(magnitude);
}
相关推荐
踏着七彩祥云的小丑1 小时前
嵌入式测试学习第 4 天:集成电路、芯片、FPGA
单片机·嵌入式硬件
项目題供诗1 小时前
STM32-对射式红外传感器计次&旋转编码器计次(九)
人工智能·stm32·嵌入式硬件
wsoz2 小时前
音视频控制传输协议(AVCTP)
音视频
llilian_162 小时前
如何甄选专业级失真度测量仪校准装置
人工智能·功能测试·单片机·嵌入式硬件·测试工具·51单片机
美狐美颜sdk2 小时前
企业级美颜SDK开发全流程:AI算法、渲染链路与性能优化
人工智能·音视频·直播美颜sdk·美颜api
星空语3 小时前
音频Kernel+HAL层学习规划
学习·音视频
潜创微科技3 小时前
IT66353:3 进 1 出 HDMI2.0 18Gbps 重定时器切换芯片方案
嵌入式硬件·音视频
m0_377108143 小时前
51单片机串口
单片机·嵌入式硬件·51单片机
Deitymoon3 小时前
STM32——I2C协议
stm32·单片机·嵌入式硬件