Funpack5-3 基于MAX32690实现实时音频频谱分析
该项目使用了ADI MAX32690EVKIT开发板,实现了音频采集和实时频谱分析的设计,它的主要功能为:使用开发板上的MAX9867音频codec芯片采集音频,通过I2S传输给MAX32690 MCU,实时使用FFT计算其频谱,并显示在串口屏上。
标签
嵌入式系统
Funpack活动
开发板
fishlovescoding
更新2026-08-20
45

基于 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模式

image.png


时域模式

image.png


串口输出

image.png

三、硬件平台

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

五、系统结构

image.png

音频搬运位于 DMA 回调中,FFT、绘图和 TFT 发送位于前台主循环。即使前台尚未处理完上一帧,DMA 仍会继续采集和回送音频;此时只丢弃来不及显示的分析帧,并累加 drop 计数。

六、关键技术原理

6.1 MAX9867 有界 I2C 初始化

原 MSDK 阻塞式 I2C 路径包含无超时硬件轮询。若 MAX9867 未上电、跳线错误或总线被拉住,程序可能永久停在初始化阶段。

本项目改用异步事务,并在前台推进状态机。每次访问都有 100 ms 截止时间,同时用链接器 --wrap 替换以下三个可能无限等待的 SDK 函数:

  • MXC_I2C_Stop
  • MXC_I2C_ClearTXFIFO
  • MXC_I2C_ClearRXFIFO

MAX32690 的 MSTCTRL_STOP 是粘滞命令位,不能直接当作总线空闲状态。本项目在新事务前清除 STOP,事务结束后等待 STATUS_MST_BUSY 清零,再清除 STOP。发生 NACK、总线错误或超时时,串口会打印操作类型、寄存器地址、中断标志和控制寄存器值。

6.2 I2S DMA 乒乓缓冲

I2S DMA 使用两个 64 字的接收区。RX DMA 完成后,回调立即执行三件事:

  1. 保存刚完成的缓冲区地址,并切换到另一个 RX 缓冲区。
  2. 重新启动 RX DMA,缩短采集空窗。
  3. 把完成缓冲区交给 TX DMA做耳机回送。

完整帧也采用双缓冲。主循环没有取走上一帧时,新帧不会覆盖正在处理的数据,而是增加 dropped_frame_count。这种策略优先保证连续采集和监听,允许显示按自身带宽降帧。

6.3 FFT 分析

默认参数如下:

参数

数值

SAMPLE_RATE

48000 Hz

FFT_FRAME_SIZE

1024

FFT bin 数

512

标称频率分辨率

46.875 Hz

显示上限

12000 Hz

频谱柱数量

32

预处理流程为:

  1. 24 位有符号采样归一化到约 [-1, 1)
  2. 计算并减去帧均值,抑制直流分量。
  3. 乘 Hann 窗,减少帧边界不连续造成的频谱泄漏。
  4. 调用 arm_rfft_fast_f32() 完成实数 FFT。
  5. 调用 arm_cmplx_mag_f32() 计算各频点幅值。
  6. 在显示频率范围内搜索最大 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 次像素级片选翻转减少为一次。

七、软件流程

image.png

八、开发与调试过程(遇到的一些坑)

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)。

九、构建和运行

操作步骤

  1. 检查 JP9、JP10 和音频/TFT 接线。
  2. 编译并下载 build/max32690.elf
  3. 打开 115200、8-N-1 串口终端。
  4. 给 J5 输入音频,观察 TFT 频谱柱和峰值信息。
  5. 按下并松开 SW2,确认日志中的 mode=fft/time 切换。
  6. 在时域模式观察加窗后的连续曲线,再次松开 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 标定加入已知幅值正弦源和窗函数精确相干增益校准。
  • 把运行统计改为滑动平均和最大值统计,区分瞬时尖峰与稳态负载。

十三、工程文件说明

文件

作用

main.c

初始化、DMA 音频管线、FFT、波形/频谱绘制、按键和性能统计

max9867.c

MAX9867 寄存器配置及有界 I2C 事务

board.c

EVKIT 引脚、控制台、SW2 和 GPIO 模拟 TFT 串行接口

tft_st7735.c

TFT 命令、绘图接口和整帧连续传输优化

project.mk

CMSIS-DSP、硬浮点和 I2C 链接器配置

I2S_DMA_Target.launch

Eclipse GDB/OpenOCD 调试配置

README.md

快速构建和硬件连接说明

十四、总结

本项目完成了从模拟音频输入、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 显示控制器资料。


附件下载
AudioFFT_demo.rar
团队介绍
嵌入式软件工程师FishLee,单兵作战
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号