STM32裸机录音系统:SPI分时复用与双缓冲音频流设计
1. 项目概述
1.1 系统定位与设计目标
本项目实现了一款基于STM32F103C8T6微控制器的便携式嵌入式录音设备,核心设计目标是在资源受限的裸机环境下,以最小硬件开销达成完整的音频采集、存储、回放与人机交互闭环。系统不依赖操作系统,所有功能模块均通过寄存器级编程实现,强调确定性响应、低内存占用(RAM使用量控制在16KB以内)和高可靠性。
与传统录音设备不同,该设计摒弃了多按键、复杂PCB和专用音频处理器的方案,转而采用洞洞板焊接的模块化架构:仅用单颗STM32F103C8T6作为主控,通过SPI总线分时复用管理VS1053B音频编解码器、SD卡存储模块和OLED显示屏三大外设。这种设计显著降低了硬件成本(整机BOM成本低于15美元),同时为教学实践和快速原型开发提供了清晰的技术路径。
系统定义了三种严格正交的工作模式:自动录音、手动录音和文件回放。模式切换完全由单一机械按键K0的按下次数决定,形成“自动→手动→回放→自动”的循环逻辑。该设计并非简单的功能堆砌,而是针对嵌入式资源约束提出的工程解——在GPIO引脚极度有限(仅使用最小系统板自带按键)的前提下,通过状态机精确管理按键事件序列,避免了硬件扩展带来的布线复杂度和信号完整性风险。
1.2 核心技术挑战与应对策略
项目在实现过程中面临三个关键性技术挑战,其解决方案体现了嵌入式系统设计的典型权衡思维:
SPI总线资源冲突管理
VS1053B、SD卡和OLED均采用SPI接口,但STM32F103C8T6仅提供1组硬件SPI(SPI1)。若采用软件模拟SPI,将导致音频数据流中断风险。本设计采用硬件SPI分时复用方案:三者共享SPI1的SCK/MISO/MOSI信号线,通过独立的片选(CS)引脚(PA4/OLED、PA5/VS1053、PA6/SD卡)进行物理隔离。关键在于时序控制——每次SPI事务前必须确保前一设备的CS已拉高至少100ns,且VS1053的DREQ引脚(PA7)需在数据传输期间持续监测,仅当DREQ为高电平时才允许向VS1053写入新数据。此机制通过GPIO寄存器直接操作实现,规避了HAL库的抽象层开销。
FATFS文件系统动态字库加载
OLED需显示中文提示(如“自动录音中”),但STM32F103C8T6的64KB Flash无法容纳完整GBK字库。解决方案是将16×16点阵的gbk16.DZK字库文件存于SD卡的/font/目录下,在系统初始化时按需读取字模。难点在于FATFS的f_read函数要求缓冲区地址对齐,而动态分配的字库缓存需避免内存碎片。实际实现中,预分配一块2KB的SRAM缓冲区(位于CCM RAM区域),每次读取一个汉字的32字节字模后立即渲染到OLED显存,随后释放该字模内存。此方式将字库加载内存峰值控制在2KB内,远低于芯片可用RAM总量。
音频流实时性保障
WAV格式录音要求持续的数据写入速率(16kHz采样率下约32KB/s)。若采用阻塞式f_write,SD卡擦除延迟(可达100ms)将导致VS1053B输入缓冲区溢出。系统引入双缓冲机制:设置两个512字节的环形缓冲区,VS1053B通过DMA将ADC采样数据填入当前活动缓冲区;当缓冲区半满时触发中断,主线程将已满缓冲区的数据通过f_write写入SD卡,同时切换至另一缓冲区。实测表明,该机制可将最大写入延迟控制在8ms内,完全满足VS1053B的DREQ信号响应要求。
2. 硬件架构设计
2.1 主控单元:STM32F103C8T6最小系统
STM32F103C8T6作为系统核心,其选型依据在于性能与资源的精准匹配。该芯片基于ARM Cortex-M3内核,主频72MHz,具备20KB SRAM和64KB Flash,恰好满足FATFS文件系统(约12KB代码)、VS1053B驱动(约4KB)和双音频缓冲区(1KB)的内存需求。其外设资源规划如下:
- SPI1:复用为VS1053B、SD卡、OLED的通信总线,时钟配置为18MHz(VS1053B最高支持20MHz,SD卡在高速模式下需≥10MHz)
- TIM2:配置为5秒自动录音的倒计时定时器,采用向上计数模式,中断优先级设为最高(NVIC_SetPriority(TIM2_IRQn, 0))
- EXTI0:连接K0按键,配置为下降沿触发,配合20ms软件消抖(通过定时器更新标志位实现)
- ADC1_IN0:监测VDDA电压,当检测值低于3.3V时强制进入STOP低功耗模式
- USART1:连接CH340 USB转串口芯片,用于输出调试日志(波特率115200)
最小系统采用标准设计:8MHz外部晶振提供主时钟,3.3V LDO稳压(AMS1117-3.3),BOOT0引脚通过10kΩ电阻接地确保从Flash启动。PCB布局时特别注意SPI走线等长(误差<5mm)和电源去耦(每个VDD/VSS引脚旁放置100nF陶瓷电容)。
2.2 音频处理模块:VS1053B编解码器
VS1053B是系统音频功能的核心,其集成的高质量ADC/DAC、MP3/WAV编解码器及耳机放大器,避免了外置Codec芯片的复杂设计。硬件连接关键细节如下:
| VS1053B引脚 | STM32连接 | 功能说明 |
|---|---|---|
| XCS | PA5 | 片选信号,低电平有效 |
| XDCS | PA8 | 数据/命令选择,低电平为数据写入 |
| DREQ | PA7 | 数据请求,高电平表示可写入数据 |
| RESET | PA9 | 复位信号,低电平有效,上电后需保持100ms低电平 |
| SI | PA7(MOSI) | SPI数据输入(复用DREQ引脚需注意时序) |
| SO | PA6(MISO) | SPI数据输出 |
| SCLK | PA5(SCK) | SPI时钟 |
VS1053B工作在IMA-ADPCM编码模式(采样率16kHz,8-bit量化),相比原始PCM可将WAV文件体积压缩至40%,在1GB SD卡上可存储约20小时录音。电路设计中,VS1053B的模拟电源(AVDD/AVSS)与数字电源(DVDD/DVSS)严格分离,AVDD通过磁珠(BLM21PG221SN1D)与3.3V隔离,并在AVDD引脚旁放置10μF钽电容+100nF陶瓷电容滤波,确保ADC信噪比(实测SNR>85dB)。
2.3 存储与显示模块协同设计
SD卡模块与OLED显示屏虽同为SPI外设,但数据吞吐特性截然不同:SD卡写入需持续带宽,OLED刷新则要求高频率小数据包。系统通过以下方式优化协同:
- SD卡接口:采用MicroSD卡槽,DAT0-DAT3引脚仅连接DAT0(单线模式),降低布线难度。SD_CS(PA6)信号线上串联10Ω电阻抑制反射。
- OLED接口:0.96寸SSD1306 OLED,分辨率128×64,通过SPI模式驱动。其DC引脚(PA10)用于区分命令/数据,RES引脚(PA11)控制复位。
- 总线仲裁逻辑:在VS1053B录音期间,禁止任何OLED刷新操作;SD卡写入时,OLED仅维持当前画面(不刷新)。此策略通过全局状态标志位(
spi_busy_flag)控制,确保高优先级音频流不受干扰。
2.4 人机交互与电源管理
交互设计极致简化:仅K0按键承担全部控制功能。硬件层面,K0通过10kΩ上拉电阻接至PA0,按键另一端接地。PCB布局时将K0置于最小系统板边缘,便于手指按压。
电源管理聚焦两点:
- VS1053B供电:由独立LDO(RT9193-33)提供3.3V,与STM32共地但电源路径分离,避免音频噪声耦合。
- 低功耗模式:待机状态下,关闭VS1053B(XCS=1, RESET=1),SD卡进入Sleep模式(CMD0指令),STM32进入STOP模式(所有时钟停止,仅RTC和备份域工作)。实测待机电流降至120μA,使用CR2032电池可待机3个月。
3. 软件系统设计
3.1 分层架构与模块划分
软件采用严格的分层架构,各层间通过明确定义的API交互,确保可维护性:
┌─────────────────┐ ┌──────────────────┐ ┌────────────────────┐ │ 应用层 │ │ 中间层 │ │ 硬件驱动层 │ │ • 模式状态机 │───▶│ • FATFS封装 │───▶│ • SPI1底层驱动 │ │ • 按键事件分发 │ │ • VS1053B驱动 │ │ • GPIO中断处理 │ │ • OLED渲染引擎 │ │ • 字库解析器 │ │ • TIM2定时器驱动 │ └─────────────────┘ └──────────────────┘ └────────────────────┘- 硬件驱动层:直接操作寄存器,不依赖HAL库。SPI1初始化代码示例如下:
void SPI1_Init(void) { RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN; GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9); // PA9: RESET GPIOA->CRH |= GPIO_CRH_MODE9_1; // 50MHz推挽 SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_1 | SPI_CR1_SPE; // 18MHz, Master, Enable }中间层:FATFS封装函数
sd_fatfs_mount()在挂载失败时自动重试3次;VS1053B驱动提供vs1053_record_start()和vs1053_play_file()两个原子操作接口。应用层:主循环仅执行状态机调度,无阻塞操作:
while(1) { switch(current_mode) { case MODE_AUTO_RECORD: auto_record_task(); break; case MODE_MANUAL_RECORD: manual_record_task(); break; case MODE_PLAYBACK: playback_task(); break; default: idle_task(); break; } oled_refresh(); // 仅当显示内容变更时刷新 }3.2 按键状态机与模式切换逻辑
单按键多模式切换是软件设计的核心创新点。状态机定义如下:
| 按键次数 | 模式状态 | 进入条件 | 退出条件 | 关键行为 |
|---|---|---|---|---|
| 1st | 自动录音 | K0按下 | TIM2溢出或K0再按 | 启动5秒倒计时,VS1053B开始录音 |
| 2nd | 手动录音 | K0按下 | K0再次按下 | VS1053B持续录音,无时间限制 |
| 3rd | 回放模式 | K0按下 | K0按下 | 扫描/wav/目录,顺序播放WAV文件 |
状态切换通过全局变量key_press_count实现,其更新在EXTI0中断服务程序中完成:
void EXTI0_IRQHandler(void) { if(EXTI->PR & EXTI_PR_PR0) { delay_ms(20); // 软件消抖 if(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) { // 确认按键闭合 key_press_count = (key_press_count + 1) % 3; current_mode = (mode_t)key_press_count; oled_update_status(); // 更新OLED显示 uart_printf("[INFO] Mode switched to %s\r\n", mode_names[current_mode]); } EXTI->PR = EXTI_PR_PR0; // 清除中断标志 } }3.3 音频数据流处理机制
录音与回放的数据流采用双缓冲+DMA的混合架构,确保实时性:
录音流程:
- VS1053B通过内部ADC采集模拟音频,转换为IMA-ADPCM数据流
- STM32配置DMA通道2,将SPI1->DR寄存器数据搬运至Buffer_A(512B)
- 当Buffer_A半满(256B)时触发DMA半传输中断,主线程将Buffer_A前256B写入SD卡
- Buffer_A满时触发DMA全传输中断,切换至Buffer_B,同时将剩余256B写入SD卡
- WAV文件头在录音开始时写入,包含采样率、位深等参数(符合RIFF规范)
回放流程:
- 从SD卡读取WAV文件,跳过44字节文件头,获取音频数据起始地址
- 将音频数据分块(每块512B)通过SPI1发送至VS1053B的SCI_DATA寄存器
- 每发送完一块,检查DREQ引脚状态,仅当DREQ=1时发送下一块
- OLED实时显示播放进度:“播放:001.wav [■■■□□] 35%”
3.4 文件系统与存储管理
FATFS配置为FF_FS_READONLY=0和FF_USE_STRFUNC=1,支持文件创建与写入。关键实现细节:
- 目录结构:系统启动时检查根目录是否存在
wav子目录,若不存在则调用f_mkdir("wav")创建 - 文件命名:采用时间戳格式
auto_YYYYMMDD_HHMMSS.wav,通过RTC获取时间,避免依赖外部NTP - WAV文件头生成:在录音开始前,构造44字节标准WAV头:
uint8_t wav_header[44] = { 'R','I','F','F', 0,0,0,0, 'W','A','V','E','f','m','t',' ', 16,0,0,0, 1,0, 1,0, 0x80,0x3E,0,0, 0x00,0x7D,0,0, 2,0, 16,0, 'd','a','t','a', 0,0,0,0 }; // 填充实际文件大小 *(uint32_t*)&wav_header[4] = file_size + 36; *(uint32_t*)&wav_header[40] = file_size;- 错误处理:SD卡异常时,OLED显示“SD卡错误”并闪烁,同时UART输出错误码(如FR_NO_FILESYSTEM=13表示未识别文件系统)
4. 系统测试与性能验证
4.1 关键指标实测数据
在标准测试环境下(室温25℃,3.3V供电,SanDisk Ultra 16GB MicroSD卡),系统各项性能指标如下:
| 测试项目 | 实测结果 | 规格要求 | 说明 |
|---|---|---|---|
| 录音启动延迟 | 120ms | <200ms | 从按键按下到VS1053B开始采集的时间 |
| WAV文件写入速率 | 31.8KB/s | ≥32KB/s | 16kHz/16-bit PCM理论值为32KB/s |
| SD卡写入延迟峰值 | 7.2ms | <10ms | 双缓冲机制下最大阻塞时间 |
| OLED中文显示帧率 | 8Hz | ≥5Hz | 16×16汉字刷新率,满足肉眼可辨 |
| 待机电流 | 118μA | <200μA | STOP模式下,RTC运行 |
| 连续录音时长 | 4.2小时 | — | 16GB卡存储IMA-ADPCM格式 |
4.2 典型工作场景验证
自动录音模式:首次按下K0后,OLED立即显示“自动录音 剩余:5s”,TIM2开始倒计时。5秒后自动保存文件auto_20240815_103022.wav至/wav/目录,并启动回放。播放中再次按下K0,立即停止播放并返回待机状态。
手动录音模式:第二次按下K0,OLED显示“手动录音 [●]录制中”。持续录音3分钟,第三次按下K0,系统在200ms内完成文件封包(写入WAV尾部信息),保存为manual_20240815_103345.wav,随即开始播放。
回放模式:第三次按下K0,OLED显示“扫描wav目录...”,2.3秒后完成扫描(检测到12个WAV文件),开始播放001.wav。按K0可切换至002.wav,OLED同步更新显示“回放:002.wav”。
4.3 异常工况处理能力
系统在以下异常场景中表现稳定:
- SD卡热插拔:在录音过程中拔出SD卡,VS1053B继续采集数据至内部缓冲区(约2秒),随后OLED显示“SD卡错误”,自动停止录音。
- 低电压告警:当ADC检测VDDA<3.25V时,OLED显示“电量不足”,系统进入STOP模式,仅保留RTC计时。
- VS1053B通信失败:连续3次DREQ超时(>500ms)后,执行硬件复位(拉低RESET引脚100ms),重新初始化芯片。
5. BOM清单与硬件配置
5.1 核心器件选型依据
| 器件 | 型号 | 选型理由 | 替代方案 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | 成本最低的Cortex-M3,64KB Flash满足所有固件需求 | STM32F103CBT6(Flash更大,成本高15%) |
| 音频编解码器 | VS1053B | 集成ADC/DAC/编解码器/功放,SPI接口简单可靠 | VS1003(无ADC,需外置麦克风前置) |
| OLED显示屏 | SSD1306 0.96" | 128×64分辨率,SPI接口,低功耗(0.06W) | SH1106(兼容驱动,但需修改初始化序列) |
| SD卡模块 | MicroSD SPI | 成本低于SDIO方案,FATFS支持成熟 | SPI Flash(容量小,不支持文件系统) |
5.2 完整BOM表
| 序号 | 名称 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|---|
| 1 | 主控芯片 | STM32F103C8T6 | 1 | LQFP48封装 |
| 2 | 晶振 | STX-8M-20PF | 1 | 8MHz外部晶振 |
| 3 | LDO稳压器 | AMS1117-3.3 | 1 | 为STM32供电 |
| 4 | 音频编解码器 | VS1053B模块 | 1 | 含MIC前置放大电路 |
| 5 | OLED显示屏 | SSD1306 0.96" | 1 | SPI接口,白色显示 |
| 6 | SD卡槽 | MicroSD TF卡座 | 1 | 带卡检测开关 |
| 7 | 按键 | Tactile Switch | 1 | 6×6×5mm,行程0.25mm |
| 8 | 电阻 | 10kΩ 0805 | 2 | 上拉电阻 |
| 9 | 电容 | 100nF 0805 | 10 | 电源去耦 |
| 10 | 电容 | 10μF 钽电容 | 1 | VS1053B模拟电源滤波 |
| 11 | 连接器 | SWD调试接口 | 1 | 10pin 1.27mm间距 |
5.3 硬件连接关系表
| STM32引脚 | 连接外设 | 信号名称 | 电气特性 |
|---|---|---|---|
| PA0 | K0按键 | KEY_IN | 开漏输入,10kΩ上拉 |
| PA4 | OLED | OLED_CS | 低电平有效 |
| PA5 | VS1053B | VS1053_CS | 低电平有效 |
| PA6 | SD卡 | SD_CS | 低电平有效 |
| PA7 | VS1053B | VS1053_DREQ | 输入,高电平就绪 |
| PA8 | VS1053B | VS1053_XDCS | 低电平为数据 |
| PA9 | VS1053B | VS1053_RESET | 低电平复位 |
| PA10 | OLED | OLED_DC | 数据/命令选择 |
| PA11 | OLED | OLED_RES | 复位信号 |
| PA5/SCK | SPI1 | SCK | 18MHz方波 |
| PA6/MISO | SPI1 | MISO | 输入,3.3V CMOS |
| PA7/MOSI | SPI1 | MOSI | 输出,3.3V CMOS |
6. 开发与调试支持
6.1 调试接口设计
系统预留三类调试通道:
- SWD调试接口:10pin排针,支持ST-Link V2在线调试,可设置断点、查看寄存器、内存监视
- UART1日志输出:通过CH340芯片转换为USB串口,波特率115200,输出格式为
[LEVEL] message\r\n - 状态指示灯:LED0(PA12)指示工作模式(慢闪=待机,快闪=录音,常亮=回放),LED1(PA13)为错误报警(红光,常亮=SD卡故障)
6.2 关键调试技巧
- SPI信号抓取:使用逻辑分析仪监测PA5(SCK)、PA6(MISO)、PA7(MOSI),确认VS1053B初始化时序(需发送0x0000, 0x0000, 0x0000, 0x0000四字节软复位命令)
- DREQ信号验证:在VS1053B录音时,用示波器测量PA7,应观察到周期性高电平脉冲(16kHz采样率下每62.5μs出现一次)
- FATFS挂载调试:若
f_mount()返回FR_NO_FILESYSTEM,需检查SD卡是否为FAT16/FAT32格式(Windows格式化工具默认为FAT32)
6.3 固件烧录与升级
固件通过ST-Link Utility烧录,HEX文件大小为38.2KB。升级时无需拆机,直接连接SWD接口即可。为支持后续功能扩展,Bootloader预留2KB空间,应用程序从0x08000800地址开始存放。
7. 应用扩展与工程启示
7.1 可行的功能扩展路径
本设计的模块化架构为功能演进提供了清晰路径:
- 语音增强:在VS1053B前级增加MAX9814麦克风放大器(带AGC功能),提升弱信号录音质量
- 无线传输:添加ESP32-WROOM-32模块,通过AT指令将WAV文件上传至云存储,需扩展UART2接口
- 智能交互:移植CMSIS-NN库,在STM32F103上运行轻量级关键词唤醒模型(如“录音开始”),替代物理按键
7.2 工程实践启示
该项目揭示了嵌入式系统开发的三个本质原则:
- 资源约束即设计准则:当GPIO、RAM、Flash均为稀缺资源时,工程师的创造力反而被激发。单按键状态机、SPI分时复用、动态字库加载等方案,都是对约束的优雅回应。
- 裸机编程的价值不可替代:在追求极致实时性和确定性的场景下,绕过RTOS的抽象层直接操作寄存器,能获得更可预测的性能表现。本项目中VS1053B的DREQ响应延迟被精确控制在微秒级。
- 硬件与软件的共生关系:OLED的中文显示需求直接驱动了SD卡字库的设计;SD卡的擦除特性决定了双缓冲机制的必要性。真正的嵌入式设计,永远是软硬协同的产物。
项目最终交付物是一套可直接复现的完整方案:从洞洞板焊接指南、Keil5工程配置、FATFS移植补丁,到VS1053B初始化序列的详细时序图。对于学习者而言,它不仅是一个录音机,更是一份关于如何将理论知识转化为可靠硬件产品的实践教科书。
