基于 MAX32690 与 MAX9867 的实时音频频谱及波形显示系统
一、项目概述
本项目基于 Analog Devices MAX32690 EVKIT 开发板,在官方 I2S DMA 示例的基础上,整合了 CMSIS-DSP FFT 示例和板载 TFT 示例,实现一套可连续运行的实时音频分析系统。
系统上电后由 MAX32690 通过 I2C 初始化 MAX9867 音频 CODEC,随后利用 I2S 和 DMA 连续采集立体声音频,同时把接收到的数据回送到播放通路用于耳机监听。主循环从采集数据中提取单声道音频帧,完成去直流、Hann 加窗、实数 FFT、幅值和 dBFS 计算,再将结果绘制到 128×128 TFT。按下并松开 SW2,可以在频域柱状图和时域曲线之间切换。
项目还针对嵌入式系统中常见的“外设等待无超时”和“显示刷新占用过高”问题进行了专门处理:MAX9867 的所有寄存器访问均设置软件截止时间;TFT 则通过连续 RAMWR、整帧保持 CS 有效以及去除位间空循环,再加上编译优化,把整帧传输时间从约 268 ms 降低到约 48 ms。
二、项目目标
本项目要完成以下功能:
- MAX32690 上电后自动探测并配置 MAX9867。
- 使用 I2S DMA 连续接收双声道 24 位音频数据。
- 保持音频采集和耳机监听,不因 TFT 刷屏而停止 DMA。
- 以宏
FFT_FRAME_SIZE定义分析帧长,默认值为 1024。 - 使用 CMSIS-DSP 完成实数 FFT,显示峰值频率和峰值 dBFS。
- 在 TFT 上显示 32 根频谱柱、峰值频率标线和幅值信息。
- 在时域模式下显示连续音频曲线。
- SW2 在按键松开时切换时域/频域模式,并进行 100 ms 防抖。
- 分阶段统计采集、预处理、FFT、绘图、TFT 和总处理时间,每秒打印一次。
- 统计 DMA 错误、丢帧数量以及 DMA 回调平均/最大耗时。
FFT模式

时域模式

串口输出

三、硬件平台
3.1 MAX32690 EVKIT
MAX32690 是面向低功耗连接和边缘处理应用的微控制器,主处理器为带单精度浮点单元的 Arm Cortex-M4。本项目主要使用以下片上资源:
- Cortex-M4F:运行 CMSIS-DSP 浮点 FFT 和绘图算法。
- I2S:接收 MAX9867 的 24 位立体声音频数据。
- DMA:在后台持续搬运 I2S RX/TX 数据。
- I2C2:配置 MAX9867 寄存器。
- Timer0:提供微秒级单调计时基准。
- GPIO:模拟 TFT 的 9 位串行接口,并接收 SW2 中断。
- UART2:输出初始化状态、错误诊断和性能日志。
- 片上 SRAM:存放双缓冲音频帧、FFT 工作区、TFT 帧缓冲和字节序转换缓冲区。
3.2 MAX9867 音频 CODEC
MAX9867 集成 ADC、DAC、Line-In、耳机放大器和数字音频接口。本项目使用 12.288 MHz MCLK,通过 I2C 配置其系统时钟、I2S 格式、输入增益、ADC 电平、播放音量和电源管理寄存器。
录音通路为:
J5 Line-In -> MAX9867 Line-In -> ADC -> I2S RX -> MAX32690
监听通路为:
MAX32690 I2S TX -> MAX9867 DAC -> 耳机放大器 -> J6 Headphone
3.3 TFT 显示屏
开发板通过 J4 连接 CFAF128128B1-0145T,分辨率为 128×128,像素格式为 RGB565。该连接没有复用 MAX32690 的硬件 SPI 外设,而是使用 P2.24、P2.25 和 P2.11 分别模拟 MOSI、CLK 和 CS。
每个串行字共 9 位:第 1 位为 D/CX,后 8 位为命令或数据。为了提高整帧刷新速度,像素数据在一次 TFT_SPI_Write() 中连续发送,CS 在整帧 RAMWR 期间保持低电平。
3.4 连接与跳线
项目 | 连接或设置 | 作用 |
|---|---|---|
USB/电源 | PC 连接 CN2 | 供电、下载和串口输出 |
TFT | 显示屏连接 J4 | 显示频谱或波形 |
麦克风 | 信号源连接 J5 | MAX9867 Line-In 输入 |
耳机 | 耳机连接 J6 | 监听 I2S 回送音频 |
JP9 | 设置为 SDA | MAX9867 I2C 数据线 |
JP10 | 设置为 SCL | MAX9867 I2C 时钟线 |
SW2 | 板载按键 | 松开时切换时域/频域 |
四、软件环境
- SDK:Analog Devices MSDK。
- IDE:Eclipse/CDT 或命令行 Make。
- 编译器:MSDK 自带 Arm GNU Toolchain 10.3。
- DSP 库:CMSIS-DSP 1.16.2。
- 浮点 ABI:
hard,使用 Cortex-M4F 硬件浮点单元。 - 调试器:MSDK OpenOCD 和
arm-none-eabi-gdb.exe。 - 目标板配置:
MAX32690 EvKit_V1。
五、系统结构
![]()
音频搬运位于 DMA 回调中,FFT、绘图和 TFT 发送位于前台主循环。即使前台尚未处理完上一帧,DMA 仍会继续采集和回送音频;此时只丢弃来不及显示的分析帧,并累加 drop 计数。
六、关键技术原理
6.1 MAX9867 有界 I2C 初始化
原 MSDK 阻塞式 I2C 路径包含无超时硬件轮询。若 MAX9867 未上电、跳线错误或总线被拉住,程序可能永久停在初始化阶段。
本项目改用异步事务,并在前台推进状态机。每次访问都有 100 ms 截止时间,同时用链接器 --wrap 替换以下三个可能无限等待的 SDK 函数:
MXC_I2C_StopMXC_I2C_ClearTXFIFOMXC_I2C_ClearRXFIFO
MAX32690 的 MSTCTRL_STOP 是粘滞命令位,不能直接当作总线空闲状态。本项目在新事务前清除 STOP,事务结束后等待 STATUS_MST_BUSY 清零,再清除 STOP。发生 NACK、总线错误或超时时,串口会打印操作类型、寄存器地址、中断标志和控制寄存器值。
6.2 I2S DMA 乒乓缓冲
I2S DMA 使用两个 64 字的接收区。RX DMA 完成后,回调立即执行三件事:
- 保存刚完成的缓冲区地址,并切换到另一个 RX 缓冲区。
- 重新启动 RX DMA,缩短采集空窗。
- 把完成缓冲区交给 TX DMA做耳机回送。
完整帧也采用双缓冲。主循环没有取走上一帧时,新帧不会覆盖正在处理的数据,而是增加 dropped_frame_count。这种策略优先保证连续采集和监听,允许显示按自身带宽降帧。
6.3 FFT 分析
默认参数如下:
参数 | 数值 |
|---|---|
| 48000 Hz |
| 1024 |
FFT bin 数 | 512 |
标称频率分辨率 | 46.875 Hz |
显示上限 | 12000 Hz |
频谱柱数量 | 32 |
预处理流程为:
- 24 位有符号采样归一化到约
[-1, 1)。 - 计算并减去帧均值,抑制直流分量。
- 乘 Hann 窗,减少帧边界不连续造成的频谱泄漏。
- 调用
arm_rfft_fast_f32()完成实数 FFT。 - 调用
arm_cmplx_mag_f32()计算各频点幅值。 - 在显示频率范围内搜索最大 bin,换算峰值频率。
Hann 窗相干增益约为 0.5。对于幅值为 1 的整周期正弦,单边 FFT 峰值约为 N/4,因此本项目使用以下近似计算峰值 dBFS:
amplitude_ratio = spectrum_peak / (FFT_FRAME_SIZE × 0.25)
peak_dBFS = 20 × log10(amplitude_ratio)
频谱柱和峰值读数统一限制在 -80 dBFS 到 0 dBFS。纵轴不再按每帧峰值自动缩放,因此不同帧可以直接比较。峰值读数固定绘制在屏幕 x=74 的位置,避免频率位数变化造成文字抖动。
6.4 时域显示
时域模式复用同一帧经过去直流和 Hann 加窗后的数据。系统把 1024 个点按 128 个横向像素分组求平均,再把样本绝对值转换为固定的 -80~0 dBFS 对数刻度;正负样本分别映射到中央零线的上下两侧,最后用 Bresenham 直线算法连接相邻点。该量程不随帧内容变化,因此可以直接比较连续帧的幅度。
与逐列绘制最小值/最大值包络相比,连接曲线不会形成密集竖线,更接近示波器波形。Hann 窗会使帧首尾自然衰减到零,这是当前设计的预期效果。
6.5 SW2 模式切换
SW2 接在 P4.0,使用上拉输入,按下为低电平。驱动注册上升沿和下降沿中断,但回调只在 PB_Get() 判断按键已经松开时切换模式,并用 Timer0 实现 100 ms 防抖。
中断只修改一个 volatile 模式变量。主循环在处理每帧前锁存该变量,确保同一帧不会一半按频域、一半按时域绘制。
6.6 TFT 传输优化
使用软SPI(所用MCU的唯一SPI在开发板上连接到其他设备了,因此只能使用软件bit-bang的方式模拟SPI,效率很低)向串口屏写数据是当前系统的主要性能瓶颈。优化过程如下:
方案 | 板上 TFT 时间 | 结果 |
|---|---|---|
SDK 原始逐字节、位间延时 | 约 268 ms | 稳定但刷新很慢 |
每像素两字节连续、延时减半 | 约 200 ms | 有明显改善 |
完全移除位间空循环 | 约 108 ms | 仍能稳定工作 |
整帧 RAMWR、CS 全程有效,开启O3编译 | 约 48 ms | 当前方案 |
CPU 内存中的 uint16_t RGB565 为小端序,而 TFT 要求高字节先发。驱动使用 64 KB 静态缓冲区完成整帧字节交换,再调用一次 TFT_SPI_Write()。这样把每帧约 16384 次像素级片选翻转减少为一次。
七、软件流程

八、开发与调试过程(遇到的一些坑)
8.1 Eclipse 找不到 GDB
初始 Eclipse 启动配置调用了环境变量中的 gdb,系统提示:
Error with command: gdb --version
Cannot run program "gdb": Launching failed
项目的 .launch 文件随后改为直接引用 MSDK 安装目录中的:
C:/MaximSDK/Tools/GNUTools/10.3/bin/arm-none-eabi-gdb.exe
C:/MaximSDK/Tools/OpenOCD/openocd.exe
8.2 CODEC 初始化卡死
调试期间曾依次出现初始化停在 MAX9867 探测、播放使能以及错误码 -9、-10 等问题。寄存器级日志还捕获到:
MAX9867 I2C STOP timeout op=W reg=0x17 ...
MAX9867 I2C timeout op=R reg=0x16 ... flags=0x00000020
最终通过异步事务、统一超时、FIFO/STOP 包装函数以及粘滞 STOP 位处理,消除了无界等待,并保留了足够的寄存器级诊断信息。
8.3 TFT 撕裂和闪烁
曾尝试局部更新柱状图、显示关闭/开启包围更新等方案,但出现明显撕裂或整屏闪烁。因此最终回到稳定的完整帧刷新,再从底层传输效率入手优化。整帧连续拉低CS 方案(ADI官方代码是每个byte拉低/拉高一次片选,很冗余)既保持画面稳定,也把传输时间降到约 48 ms(若用O0优化则为89ms)。
九、构建和运行
操作步骤
- 检查 JP9、JP10 和音频/TFT 接线。
- 编译并下载
build/max32690.elf。 - 打开 115200、8-N-1 串口终端。
- 给 J5 输入音频,观察 TFT 频谱柱和峰值信息。
- 按下并松开 SW2,确认日志中的
mode=fft/time切换。 - 在时域模式观察加窗后的连续曲线,再次松开 SW2 返回频域。
十、测试结果与性能分析
上电后串口会输出各初始化阶段,并显示参数:
sample_rate=48000Hz fft_size=1024 resolution=46Hz
当前整帧连续 CS 版本的一组板上日志为(O0等级下,若用O3会更快,每帧50ms内):
frames=1969 drop=2321 dma_err=0 mode=fft peak=46Hz
capture=41580us preprocess=448us fft=729us render=1603us
tft=89196us total=91995us rx_isr(avg/max)=23/30us
阶段 | 实测耗时 | 分析 |
|---|---|---|
音频帧采集 | 41.58 ms | 在 DMA 后台进行,不阻塞前台 |
预处理 | 0.448 ms | 均值、去直流和 Hann 窗 |
FFT 与幅值 | 0.729 ms | Cortex-M4F + CMSIS-DSP 效率较高 |
帧缓冲绘制 | 1.603 ms | 频谱柱、文字和标线 |
TFT 发送 | 89.196 ms | 占总前台时间约 97%,仍是主要瓶颈 |
总处理 | 91.995 ms | 大于音频帧到达间隔,因此会丢显示帧 |
RX DMA 回调 | 23/30 µs | 中断足够短,连续采集稳定 |
dma_err=0 说明测试期间 DMA 没有报告错误。drop 表示分析显示帧被主动丢弃,不代表 I2S 采集或耳机回送中断。
需要注意:1024 点、48 kHz 的理论帧长为 21.33 ms,而日志中的采集时间约为 41.58 ms,更接近 24 kHz。该差异来自 MAX9867 的实际 LRCLK 配置,不能只用软件宏判断。后续应使用示波器或逻辑分析仪测量 WS/LRCLK;在确认前,屏幕频率值应理解为按 SAMPLE_RATE=48000 计算的标称值。
十一、项目特点
- 以 DMA 双缓冲保证音频采集不受慢速显示影响。
- 使用 CMSIS-DSP 和硬件浮点,1024 点 FFT 小于 1 ms。
- MAX9867 驱动具备超时检测,不会因外设异常永久卡死。
- 错误日志包含 I2C 操作、寄存器和状态标志,便于板级调试。
- 同一套音频帧支持频域和时域两种观察方式。
- 按键SW2使用松手触发和软件防抖,交互行为稳定。
- 每秒输出完整性能统计,可直接定位系统瓶颈。
十二、应用场景与后续优化
应用场景
- 嵌入式音频频谱仪
- 麦克风、Line-In 和 CODEC 通路调试工具
- 音频算法、滤波器和声学实验教学平台
- 设备端音量、主频和频段能量监测
后续优化方向
- 用示波器确认 MAX9867 LRCLK信号的时序正确性,并统一 CODEC 时钟、
SAMPLE_RATE和频率标尺。 - 在硬件允许的后续版本中改用真正的 SPI/DMA 显示接口。
- 只更新发生变化的行,同时结合 TFT 扫描方向验证无撕裂。
- 降低 TFT 刷新频率,将 CPU 时间留给更多音频算法。
- 增加触发电平、时基选择和固定/自动量程,使时域显示更接近示波器。
- 使用多帧平均、峰值保持或对数频率轴改善频谱可读性。
- 对 dBFS 标定加入已知幅值正弦源和窗函数精确相干增益校准。
- 把运行统计改为滑动平均和最大值统计,区分瞬时尖峰与稳态负载。
十三、工程文件说明
文件 | 作用 |
|---|---|
| 初始化、DMA 音频管线、FFT、波形/频谱绘制、按键和性能统计 |
| MAX9867 寄存器配置及有界 I2C 事务 |
| EVKIT 引脚、控制台、SW2 和 GPIO 模拟 TFT 串行接口 |
| TFT 命令、绘图接口和整帧连续传输优化 |
| CMSIS-DSP、硬浮点和 I2C 链接器配置 |
| Eclipse GDB/OpenOCD 调试配置 |
| 快速构建和硬件连接说明 |
十四、总结
本项目完成了从模拟音频输入、CODEC 配置、I2S DMA 连续采集,到时域/频域分析和 TFT 实时显示的完整链路。FFT 本身只需要约 0.7 ms,证明 MAX32690 的 Cortex-M4F 足以承担基础实时音频分析;系统性能主要受 EVKIT 上 GPIO 模拟 TFT 接口限制。
通过有界 I2C、DMA 乒乓缓冲、主动丢弃显示帧、按阶段计时和连续 CS 传输,系统在慢速显示条件下仍能保持音频采集稳定,同时具备较强的故障可诊断性。后续工作的重点应是确认实际采样率,并进一步改善显示链路或降低显示更新负担。
参考资料
- Analog Devices MSDK User Guide
- MAX32690 数据手册及 EVKIT 原理图。
- MAX9867 数据手册。
- Arm CMSIS-DSP Real FFT 文档。
- CFAF128128B1-0145T/ST7735 显示控制器资料。