嵌入式 ANC:STM32 实战
硬件架构
实现实时 ANC 首先要选择合适的硬件平台。控制器需要在微秒级完成自适应滤波运算,同时管理多路音频数据流。
| 模块 | 功能 | 典型选型 |
|---|---|---|
| 主控制器 | 执行自适应算法 | STM32F4/F7, ESP32-S3, nRF5340 |
| 参考麦克风 | 采集环境噪声 | Knowles SPH0645, Infineon IM69D130 |
| 误差麦克风 | 采集残余噪声 | Knowles SPH0645, TDK ICS-43434 |
| 音频 DAC | 输出抗噪声信号 | ES9218, PCM5102 |
| 功放 | 驱动扬声器 | Class-D |
参考麦克风位于耳罩外侧,采集外部环境噪声作为算法参考输入。误差麦克风位于耳罩内侧,采集扬声器附近的残余噪声,用于评估降噪效果并驱动自适应更新。音频 DAC 将数字抗噪声信号转为模拟量,经功放放大后驱动扬声器。
MEMS 麦克风接口
现代 ANC 产品普遍使用数字 MEMS 麦克风,其内部集成 ADC 和 PDM(脉冲密度调制)接口。PDM 用 1 位过采样调制,主时钟频率为 1.024–3.072 MHz,双沿采样实现 64× 或 128× 过采样。
STM32 通过 DFSDM(数字滤波器 sigma-delta 调制器)外设直接连接 PDM 麦克风。DFSDM 内置 Sinc 滤波器,可将 PDM 流转换为 16 位或 24 位 PCM 数据,无需外部编解码器。
DFSDM 配置
以下代码配置 STM32 的 DFSDM 通道和滤波器,从一个数字 MEMS 麦克风采集音频数据:
| |
关键参数说明:
OutputClock.Divider = 32:将系统时钟分频至 PDM 主时钟,典型值为 2.4 MHzSincOrder = DFSDM_FILTER_SINC3_ORDER:三阶 Sinc 滤波器,通带平坦度更好Oversampling = 64:过采样率,配合 PDM 时钟决定最终采样率
flowchart TD
PDM["PDM 比特流<br/>1-bit @ 2.048MHz"] --> SINC["Sinc³ 数字滤波器<br/>降采样"]
SINC --> DEC["抽取<br/>64x → 32kHz"]
DEC --> PCM["16-bit PCM<br/>@ 16kHz"]
classDef raw fill:#f44336,color:#fff
classDef filter fill:#2196F3,color:#fff
classDef out fill:#4CAF50,color:#fff
class PDM raw
class SINC,DEC filter
class PCM outDFSDM 内部的核心处理链如上图所示。PDM 比特流经过 Sinc³ 滤波器完成降采样和抽取,最终输出 16 位 PCM 数据。
选择 Sinc³(三阶 Sinc)而非 FastSinc 滤波器,原因在于延迟与阻带衰减的权衡。Sinc³ 的传输函数为 H(z) = ((1 - z⁻ᴿ) / (1 - z⁻¹))³,在相同过采样率下提供更陡峭的滚降和更好的阻带衰减(约 -60dB/dec),适合音频应用。FastSinc 虽然计算量更低、群延迟更小,但阻带衰减性能差,通常只用于看门狗或阈值检测等辅助功能,不适合 ANC 主音频路径。
双麦克风配置(参考 + 误差)需要两个 DFSDM 通道,分别绑定不同的麦克风数据线。
CMSIS-DSP FXLMS 实现
CMSIS-DSP 是 ARM 官方提供的数字信号处理库,针对 Cortex-M 内核做了 SIMD 优化。在 STM32 上实现 FXLMS 算法时,直接使用 arm_fir_f32 完成滤波计算,避免手写循环。
arm_fir_f32 内部利用 Cortex-M4F/M7 的 DSP 扩展指令集,通过 SIMD(单指令多数据)方式完成滤波运算。在支持 FPU 的内核上,一条 VFMA 指令可在单周期内完成 2 次浮点乘加,配合循环展开(loop unrolling)和双发射(dual-issue)流水线,实际吞吐量可达每周期 2–4 次乘加。CMSIS-DSP 库对此做了深度优化,手写汇编通常无法超越其性能。
数据结构定义
| |
w_filter 是自适应控制器,其系数在运行时持续更新。s_filter 是次级路径模型 S(z) 的估计,系数通过离线辨识预先确定,运行时不调整。
初始化与处理
| |
上面的权重更新使用了归一化 LMS(NLMS)策略。标准的 FXLMS 权重递推公式为:
| |
其中 μ 为固定步长。NLMS 引入归一化因子,将步长调整为:
| |
当参考信号功率较大时自动减小步长,避免发散;信号功率小时步长增大,加快收敛。归一化因子中的小常数 ε(代码中取 1e-6)防止除以零。
fxlms_cmsis_process 的执行流程:
- 参考信号
x_ref经过 W(z) 生成抗噪声输出y_output - 参考信号经过 S(z) 估计生成滤波后的参考信号
x_filtered - 使用滤波后的参考信号更新 W(z) 系数,步长做归一化处理
权重更新使用 x_filtered(而非原始 x_ref),这是 FXLMS 与标准 LMS 的核心区别,补偿了次级路径带来的相位偏移。
FreeRTOS 实时任务框架
在实际产品中,ANC 处理作为一个实时任务运行,由定时器或 DMA 中断触发。
| |
flowchart TD
IRQ["DMA 半传输完成中断"] --> NOTIFY["ulTaskNotifyTake<br/>唤醒 ANC 任务"]
NOTIFY --> PROC["处理前半块<br/>64 samples"]
PROC --> DAC["DMA 启动 DAC 输出"]
IRQ2["DMA 全传输完成中断"] --> NOTIFY2["唤醒 ANC 任务"]
NOTIFY2 --> PROC2["处理后半块<br/>64 samples"]
PROC2 --> DAC2["DMA 启动 DAC 输出"]
DAC2 --> IRQ
classDef irq fill:#f44336,color:#fff
classDef task fill:#2196F3,color:#fff
classDef out fill:#4CAF50,color:#fff
class IRQ,IRQ2 irq
class NOTIFY,PROC,NOTIFY2,PROC2 task
class DAC,DAC2 outDMA 的半传输和全传输完成中断交替触发,驱动 ANC 任务以流水线方式处理数据块。
任务流程:
- 等待通知(
ulTaskNotifyTake)—— DMA 完成一次数据采集后发送通知 - 类型转换——Q15 到 float(
arm_q15_to_float),CMSIS-DSP 提供批量转换函数 - 执行 FXLMS——调用
fxlms_cmsis_process - 结果转回 Q15(
arm_float_to_q15),启动 DAC 的 DMA 传输
实时性优化
ANC 对延迟极其敏感。从误差麦克风采集到扬声器输出的总延迟需控制在 0.5 ms 以内,否则相移过大导致算法失效甚至正反馈啸叫。
| 优化手段 | 说明 | 典型效果 |
|---|---|---|
| 降低采样率 | 16 kHz 而非 48 kHz | 计算量降至 1/3 |
| 减小块大小 | 16–32 采样点 | 延迟降至 1–2 ms |
| 降低滤波器阶数 | 32–64 阶 | 每块节省数千次乘加 |
| 定点运算 | Q15/Q31 格式 | 取消浮点转换开销 |
| DMA 双缓冲 | 乒乓缓冲 | 消除数据传输等待 |
| 核心绑定 | ANC 任务绑到专用核心 | 避免任务切换抖动 |
定点优化
延迟计算
理解延迟需要建立直觉:16 kHz 采样率下,一个采样周期 = 1/16000 = 62.5 μs。块大小 16 → 块延迟 = 16 × 62.5 μs = 1 ms。从噪声进入麦克风到抗噪声离开扬声器的总端到端延迟由三部分构成:
| 延迟分量 | 典型值 | 说明 |
|---|---|---|
| 块延迟 | 1–4 ms | DMA 填满一个块的时间,取决于采样率和块大小 |
| 计算延迟 | 0.1–0.5 ms | FXLMS 滤波 + 权重更新耗时 |
| DAC 延迟 | 0.1–0.3 ms | DAC 转换 + 功放响应时间 |
| 总延迟 | 1.2–4.8 ms | 须 <0.5 ms 才能有效降噪 → 需要极端优化 |
表中的 0.5 ms 目标是主动降噪的经验阈值。超过此值时,1600 Hz 以上频率的相移超过 180°,反馈会从降噪变为增强(正反馈啸叫)。因此实际 ANC 系统通常采用 16 kHz 采样率 + 16 块大小 + 32 阶滤波器 + 定点运算的组合,将总延迟压到 0.3–0.4 ms。
浮点 FXLMS 在 Cortex-M4/M7 上虽然硬件支持,但定点版本仍可进一步降低延迟。CMSIS-DSP 提供 arm_fir_q15 和 arm_fir_q31 系列函数。将系数和信号转为 Q15 格式后,每次滤波运算从多条指令降为单条 SIMD 指令。
浮点 vs 定点的选择:LMS 权重更新涉及大量乘法累加操作。Q15 格式中,两个 16 位定点数相乘结果为 31 位(符号位保留),需要饱和处理(saturation)后右移回 Q15 范围,否则溢出会导致系数剧烈跳变。浮点数(float32)由硬件 FPU 直接处理,IEEE 754 标准的指数位自动处理动态范围,不存在溢出问题,代码编写更直观。
| 对比维度 | float32 | Q15/Q31 |
|---|---|---|
| 动态范围 | ±3.4×10³⁸ | ±1 (Q15) / ±2³¹ (Q31) |
| 溢出风险 | 无 | 需饱和处理 |
| 单周期 MAC | 1 (VFMA) | 2 (SMUAD) |
| 功耗 | 较高 | 较低 |
| 代码可读性 | 高 | 低 |
综合来看,浮点适合原型开发和性能充裕的 M7 系列;定点适合追求极致续航和低成本场景,但调试周期显著增加。许多产品在浮点 M7 原型验证后,再移植到定点 M4 做成本优化。
DMA 双缓冲
双缓冲(ping-pong)模式是 DMA 数据传输中的经典技巧。两个缓冲区交替角色:当 DMA 控制器向缓冲区 A 写入新数据时,CPU 处理缓冲区 B 中已就绪的数据;DMA 完成 A 的写入后立即切换到 B,同时 CPU 开始处理 A。这种交替机制消除了数据搬移的等待时间,让 CPU 和 DMA 完全并行工作。
在 16 kHz 采样率、块大小 64 的条件下,DMA 填充一个缓冲区需要约 4 ms。如果使用单缓冲,CPU 必须等待 DMA 完成后才能开始处理,浪费一半的时间。双缓冲将有效计算吞吐量提升近一倍。
| |
下面的动画演示了双缓冲的交替过程——DMA 写入和 CPU 处理并行进行,两个缓冲区不断交换角色:
异构架构(ESP32 + STM32)
在复杂 ANC 系统中,单一 MCU 难以同时满足实时音频处理和非实时系统管理的需求。一种常见的思路是采用异构架构:ESP32 负责无线连接、用户交互和系统管理,STM32 专注实时音频处理。
双芯片通过 UART、SPI 或 I2C 通信。传递的数据包括音频流和控制命令。
自定义通信帧格式
| |
header 字段用于帧同步,接收端检测到 0xAA 0x55 后开始解析。type 区分音频采样流和参数配置命令。seq 用于丢包检测和重排序。
ESP32 端处理 Wi-Fi/蓝牙协议栈、用户界面和参数更新。当用户通过手机 App 调整 ANC 模式时,ESP32 将新的滤波器系数或步长参数封装为控制帧发送给 STM32。STM32 收到后热更新算法参数,无需暂停音频处理。
Mermaid:STM32 ANC 数据流
flowchart TD
subgraph Input
REF["参考麦克风<br/>PDM"]
ERR["误差麦克风<br/>PDM"]
end
subgraph STM32
DFSDM["DFSDM<br/>PDM→PCM"]
DMA["DMA<br/>乒乓缓冲"]
FXLMS["FXLMS<br/>CMSIS-DSP"]
DAC["DAC<br/>PCM→模拟"]
TASK["FreeRTOS<br/>ANC Task"]
end
subgraph Output
SPK["扬声器<br/>抗噪声"]
end
subgraph ESP
WIFI["Wi-Fi/蓝牙<br/>参数下发"]
end
REF --> DFSDM
ERR --> DFSDM
DFSDM --> DMA
DMA --> TASK
TASK --> FXLMS
FXLMS --> DAC
DAC --> SPK
WIFI -->|UART/SPI| TASK