基于STM32的录音机设计
一、系统概述与核心功能
1. 系统定位
基于STM32的录音机以“音频采集-编码存储-回放控制-低功耗续航”为核心,实现声音信号的实时录制、WAV格式存储、播放控制及文件管理,支持麦克风输入、耳机/喇叭输出、SD卡扩展存储,适用于会议记录、语音备忘、音乐练习等场景,替代传统磁带/数字录音笔,提供高性价比嵌入式录音方案。
2. 核心功能模块
| 模块 | 功能描述 |
|---|---|
| 音频采集 | 麦克风模块(如MAX9814)采集声音,STM32通过ADC/I2S接口实现8-16kHz采样、16位量化 |
| 音频编码 | 软件编码为PCM/WAV格式(可选ADPCM压缩),添加WAV头(RIFF块、fmt块、data块) |
| 存储管理 | SD卡(FAT32格式)存储录音文件,支持文件创建/删除/重命名,FatFS文件系统管理 |
| 回放控制 | 播放/暂停/停止/快进/快退,音量调节(DAC输出/PWM调功放),支持循环播放 |
| 用户交互 | OLED显示(录音时长、文件列表、电量),按键/旋转编码器操作,LED状态指示 |
| 低功耗设计 | 待机时关闭非必要模块(显示/编码),STM32进入STOP模式,续航≥8小时(2000mAh电池) |
二、硬件设计方案
1. 核心硬件选型
| 模块 | 型号 | 关键参数 | 接口方式 |
|---|---|---|---|
| 主控MCU | STM32F407ZGT6 | 168MHz Cortex-M4,1MB Flash,192KB RAM,I2S、DAC、SDIO、DMA,支持FPU/DSP | 核心控制器 |
| 音频采集 | MAX9814 | 驻极体麦克风+自动增益控制(AGC),输出模拟信号(0-3.3V),信噪比>60dB | ADC1_IN0(PA0,12位ADC) |
| 音频输出 | STM32内部DAC+TPA6130A2 | DAC1(PA4)输出模拟音频,TPA6130A2功放(3.3V/1.5W)驱动耳机/喇叭 | DAC+ I2C(功放控制) |
| 存储模块 | Micro SD卡(TF卡) | 8GB-32GB,Class 10(高速读写),FAT32格式 | SDIO(SDIO_D0-D3+CLK+CMD) |
| 显示模块 | OLED 12864(I2C) | 0.96寸,128×64像素,低功耗(<10mA),显示文件列表、录音时长、电量 | I2C1(PB6=SCL,PB7=SDA) |
| 用户输入 | 轻触按键×4+旋转编码器 | 按键(录音/播放/停止/菜单),编码器(音量调节/快进快退) | GPIO(PC0-PC3按键,PA6-PA7编码器) |
| 电源模块 | 3.7V锂电池+TP4056+AMS1117 | 3.7V/2000mAh锂电池,TP4056充电(5V Micro USB),AMS1117-3.3V/1.8V稳压输出 | 供电一体化(带过充保护) |
| 辅助模块 | 有源蜂鸣器+LED | 蜂鸣器(操作提示音),LED(红/绿双色,电源/录音状态) | GPIO(PB0=蜂鸣器,PB1=LED) |
2. 硬件电路设计要点
2.1 核心电路连接
- STM32最小系统:8MHz外部晶振+32.768kHz RTC晶振,SWD调试接口(PA13/PA14),复位电路(10kΩ上拉+0.1μF电容)。
- 音频采集(MAX9814):VCC=3.3V,OUT接PA0(ADC1_IN0),AGC引脚悬空(默认开启),GND共地,输出经RC滤波(1kΩ+0.1μF)后接入ADC。
- 音频输出(DAC+功放):STM32 DAC1(PA4)输出→TPA6130A2 INL引脚,TPA6130A2 VCC=3.3V,OUTL/OUTR接耳机插座(3.5mm),I2C(PB6/PB7)控制音量。
- SD卡(SDIO):SDIO_D0-D3(PC8-PC11)、SDIO_CLK(PC12)、SDIO_CMD(PD2),VCC3.3V供电,GND共地。
2.2 抗干扰设计
- 电源隔离:麦克风与STM32电源通过磁珠(600Ω@100MHz)隔离,避免AGC电路噪声干扰。
- 信号滤波:MAX9814输出加二阶低通滤波(截止频率8kHz,匹配采样率),ADC输入并联10nF电容去耦。
- PCB布局:音频走线(PA0、PA4)短且远离数字信号,模拟地(AGND)与数字地(DGND)单点连接(0Ω电阻)。
三、软件设计与核心代码
1. 系统架构(FreeRTOS多任务调度)
采用FreeRTOS实时操作系统,划分5个核心任务(优先级从高到低):
- 音频采集任务(优先级4):通过ADC DMA采集麦克风信号,存储至环形缓冲区。
- 编码存储任务(优先级3):读取环形缓冲区数据,编码为WAV格式,写入SD卡(FatFS)。
- 回放控制任务(优先级3):解析WAV文件,通过DAC DMA输出音频,响应播放/暂停指令。
- 用户交互任务(优先级2):扫描按键/编码器,更新OLED显示,处理菜单操作(文件选择、删除)。
- 低功耗管理任务(优先级1):检测无操作超时(5分钟),关闭显示/功放,进入STOP模式。
2. 核心代码实现(基于HAL库)
2.1 音频采集与DMA传输(ADC)
#include "audio_capture.h"
#include "stm32f4xx_hal.h"
#define ADC_BUFFER_SIZE 1024 // 环形缓冲区大小(1024×2字节=2KB)
uint16_t adc_buffer[ADC_BUFFER_SIZE];
uint16_t dma_index = 0; // DMA传输索引
// ADC初始化(PA0,12位,DMA传输)
void ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; // 84MHz/4=21MHz
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE; // 连续转换
hadc1.Init.DMAContinuousRequests = ENABLE; // DMA连续请求
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
HAL_ADC_Init(&hadc1);
sConfig.Channel = ADC_CHANNEL_0; // PA0
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES; // 480周期采样(16kHz采样率)
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 启动DMA传输(循环模式)
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_BUFFER_SIZE);
}
// DMA传输完成中断回调(半满/全满时触发)
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) {
// 处理前半缓冲区数据(录音/存储)
process_audio_data(adc_buffer, ADC_BUFFER_SIZE/2);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
// 处理后半缓冲区数据
process_audio_data(&adc_buffer[ADC_BUFFER_SIZE/2], ADC_BUFFER_SIZE/2);
}
2.2 WAV文件编码与存储(FatFS)
#include "ff.h"
#include "wav_encoder.h"
FIL wav_file; // FATFS文件对象
uint32_t data_size = 0; // 录音数据大小
// WAV头结构(44字节)
typedef struct {
char riff[4]; // "RIFF"
uint32_t file_size; // 文件总大小-8
char wave[4]; // "WAVE"
char fmt[4]; // "fmt "
uint32_t fmt_size; // fmt块大小(16)
uint16_t audio_format; // 音频格式(1=PCM)
uint16_t channels; // 声道数(1=单声道)
uint32_t sample_rate; // 采样率(16000)
uint32_t byte_rate; // 字节率=采样率×声道数×位数/8
uint16_t block_align; // 块对齐=声道数×位数/8
uint16_t bits_per_sample; // 位深(16)
char data[4]; // "data"
uint32_t data_size; // 数据块大小
} WAV_Header_t;
// 创建WAV文件并写入头
uint8_t create_wav_file(const char* filename) {
WAV_Header_t header = {
.riff = {'R','I','F','F'},
.wave = {'W','A','V','E'},
.fmt = {'f','m','t',' '},
.fmt_size = 16,
.audio_format = 1,
.channels = 1,
.sample_rate = 16000,
.byte_rate = 16000*1*16/8,
.block_align = 1*16/8,
.bits_per_sample = 16,
.data = {'d','a','t','a'}
};
FRESULT res = f_open(&wav_file, filename, FA_CREATE_NEW | FA_WRITE);
if (res != FR_OK) return 1;
// 写入WAV头(数据大小暂设为0,后续更新)
header.data_size = 0;
header.file_size = 36; // 44-8=36(初始值)
f_write(&wav_file, &header, sizeof(header), NULL);
return 0;
}
// 写入音频数据(PCM)
void write_audio_data(uint16_t* data, uint32_t len) {
UINT bw;
f_write(&wav_file, data, len*2, &bw); // 16位数据,2字节/点
data_size += len*2;
}
// 关闭文件时更新WAV头
void close_wav_file(void) {
WAV_Header_t header;
f_lseek(&wav_file, 0);
f_read(&wav_file, &header, sizeof(header), NULL); // 读取原头
header.data_size = data_size;
header.file_size = 36 + data_size;
f_lseek(&wav_file, 0);
f_write(&wav_file, &header, sizeof(header), NULL); // 更新头
f_close(&wav_file);
}
2.3 音频回放(DAC DMA输出)
#include "audio_playback.h"
DAC_HandleTypeDef hdac;
TIM_HandleTypeDef htim6; // DAC触发定时器(16kHz)
// DAC初始化(PA4,TIM6触发)
void DAC1_Init(void) {
DAC_ChannelConfTypeDef sConfig = {0};
hdac.Instance = DAC;
HAL_DAC_Init(&hdac);
sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO; // TIM6触发
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1);
// 启动DAC DMA(循环模式)
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)playback_buffer, PLAYBACK_BUFFER_SIZE, DAC_ALIGN_12B_R);
}
// 定时器6初始化(16kHz触发DAC)
void TIM6_Init(void) {
htim6.Instance = TIM6;
htim6.Init.Prescaler = 83; // 84MHz/84=1MHz
htim6.Init.Period = 62; // 1MHz/62≈16kHz
HAL_TIM_Base_Init(&htim6);
HAL_TIM_Base_Start(&htim6); // 启动定时器
}
// 播放WAV文件(解析头+读取数据)
void play_wav_file(const char* filename) {
FIL file;
WAV_Header_t header;
UINT br;
f_open(&file, filename, FA_READ);
f_read(&file, &header, sizeof(header), &br); // 读取WAV头
// 检查格式(PCM、单声道、16位、16kHz)
if (header.audio_format != 1 || header.channels != 1 || header.bits_per_sample != 16 || header.sample_rate != 16000) {
f_close(&file);
return;
}
// 读取数据块并播放(通过DAC DMA)
uint16_t play_buffer[PLAYBACK_BUFFER_SIZE];
while (f_read(&file, play_buffer, sizeof(play_buffer), &br) == FR_OK && br > 0) {
// 等待DMA缓冲区空闲(简化处理,实际需用双缓冲)
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)play_buffer, PLAYBACK_BUFFER_SIZE, DAC_ALIGN_12B_R);
while (HAL_DAC_GetState(&hdac) != HAL_DAC_STATE_READY); // 等待传输完成
}
f_close(&file);
}
2.4 主程序框架(FreeRTOS任务调度)
#include "stm32f4xx_hal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "audio_capture.h"
#include "audio_playback.h"
#include "ff.h"
#include "oled.h"
int main(void) {
HAL_Init();
SystemClock_Config(); // 168MHz
MX_GPIO_Init(); // 按键、LED、编码器
MX_ADC1_Init(); // 音频采集ADC
MX_DAC1_Init(); // 音频输出DAC
MX_SDIO_SD_Init(); // SD卡SDIO
MX_FATFS_Init(); // FatFS初始化
OLED_Init(); // OLED显示
// FreeRTOS任务创建
xTaskCreate(AudioCapture_Task, "Capture", 256, NULL, 4, NULL); // 采集
xTaskCreate(EncodeStore_Task, "Encode", 256, NULL, 3, NULL); // 编码存储
xTaskCreate(Playback_Task, "Playback", 256, NULL, 3, NULL); // 回放
xTaskCreate(UI_Task, "UI", 128, NULL, 2, NULL); // 用户交互
xTaskCreate(LowPower_Task, "LowPower", 128, NULL, 1, NULL); // 低功耗
vTaskStartScheduler(); // 启动调度器
while (1);
}
四、关键技术与优化
1. 音频质量优化
- 采样率与位深:16kHz采样率+16位量化(满足语音清晰度),避免过高采样率浪费存储。
- AGC控制:MAX9814自动增益控制避免大信号削波,小信号放大,提升动态范围。
- 去噪处理:软件实现IIR低通滤波(截止频率3.4kHz,模拟电话音质)或FFT去噪(需STM32F4 DSP支持)。
2. 存储效率优化
- WAV格式选择:未压缩PCM(音质好,文件大)或ADPCM压缩(4:1压缩比,文件小,需软件编码)。
- 文件管理:按日期自动创建文件夹(如
RECORD/20231001/),避免单目录文件过多。
3. 低功耗设计
- 动态电源管理:录音/播放时开启功放,待机时关闭;SD卡空闲时进入睡眠模式(
f_mount(NULL, "", 0))。 - STM32低功耗模式:无操作时进入STOP模式(关闭ADC/DAC时钟),通过按键/编码器中断唤醒。
参考代码 基于STM32录音机设计 www.youwenfan.com/contentcnt/133699.html
五、系统调试与扩展
1. 调试步骤
| 阶段 | 操作 | 工具 |
|---|---|---|
| 硬件调试 | 测量MIC输出(0-3.3V)、DAC输出(0-1.2V) | 示波器、万用表 |
| SD卡挂载 | 用FatFS测试程序创建/读写文件 | 串口打印文件列表 |
| 录音测试 | 录入语音,用Audacity分析WAV文件(采样率/位深) | Audacity(音频分析软件) |
| 回放测试 | 播放录音,检查是否有杂音/失真 | 耳机/喇叭+人耳听辨 |
2. 扩展功能
- 蓝牙传输:添加HC-05模块,通过蓝牙将录音文件发送至手机APP。
- 语音识别:集成LD3320语音模块,实现“开始录音”“停止录音”语音控制。
- 云存储:通过ESP8266模块上传录音至OneNET/阿里云,支持远程下载。
浙公网安备 33010602011771号