一、系统架构设计
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);
}