news 2026/10/6 1:28:39

STM32 FMAC实战:用硬件加速器卸载FIR滤波,释放CPU性能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32 FMAC实战:用硬件加速器卸载FIR滤波,释放CPU性能

接触 STM32 FMAC 这个外设,完全是被采样率逼出来的。去年做电机带负载的振动监测,三路加速度传感器,每路跑 64 阶 FIR 低通滤波,采样率提到 10kHz 之后,ADC 中断里的软件滤波直接吃掉了将近一半的 CPU 时间,PID 控制周期的抖动肉眼可见。后来翻参考手册才发现,G4 系列里藏着一个 Filter Math Accelerator,也就是 FMAC,一个专门跑 FIR/IIR 滤波的硬件加速器。这篇文章把我折腾 FMAC 期间整理出来的硬件原理、寄存器操作流程、实测数据和踩过的坑一次性讲清楚,给同样被滤波耗 CPU 困扰的朋友一个能直接参考的方案。

1. FMAC 是什么,ST 为什么要在 MCU 里塞一个滤波专用硬件

1.1 从一次性能瓶颈说起

很多做嵌入式的人可能和我一样,一开始接触数字滤波就是直接在中断里写循环。一个 64 阶 FIR,每个输出点要做 64 次乘法和 64 次加法,放在 Cortex-M4F 上,即便编译器开了最高优化,也还是几百个周期砸进去。采样率一高,或者通道数一多,CPU 就被这些机械的乘累加操作拖住了。

这里要理解一个关键点:FIR 和 IIR 这类数字滤波,本质上就是一个乘累加运算。虽然 ARM 内核有 DSP 指令,比如 SMLAL、SMLALD,但每一条指令还是得由 CPU 去取指、译码、执行,寄存器组的压力也大。当滤波任务占了 40% 以上 CPU 时间时,其他实时任务就会开始出现抖动,这在电机控制、逆变器、PFC 这类对控制周期敏感的场景里是绝对不能接受的。

所以 ST 在 STM32G4 系列里做了一个很“偏科”的硬件外设,FMAC,专门用于加速 FIR 和 IIR 滤波。它不参与通用计算,也不替代 CPU 做控制逻辑,只做一件事:让滤波运算从 CPU 里卸载出去,用纯硬件状态机自动完成每一次乘累加。

1.2 FMAC 的硬件定位:不是 DSP,而是“滤波专用计算单元”

很多人一听滤波加速器,会想到外接 DSP 芯片或者 FPGA,但 FMAC 和它们完全不同。它是一个集成在 MCU 内部的外设,不需要外部总线、不需要双芯片通信、也不需要额外加载代码,CPU 只需要把采样数据写到 FMAC 的缓冲区,配置好滤波器参数,然后启动计算,之后就可以去干别的事。

从硬件组成上看,FMAC 内部包含了一个 16 位乘法器,配合足够宽度的累加器,能够在一个时钟周期内完成一次乘累加操作。它的核心数据结构是几个专用的存储缓冲区,分别是输入样本缓冲区、系数缓冲区、输出缓冲区以及中间结果缓冲区。这样的结构本质上是把 FIR/IIR 的数学运算映射成了硬件流水线操作,数据在缓冲区里流动,乘法器持续工作,整个过程不需要 CPU 一条一条指令去控制。

从设计目标来看,FMAC 更像是一个“胶水外设”,它服务于有实时信号调理需求的系统。G4 系列定位是数字电源和电机控制,这类系统里电流采样、电压采样之后通常需要低通滤波,如果滤波用硬件完成,CPU 就能把全部精力放在闭环控制和通信协议上。这也是为什么 FMAC 在 G4 系列上出现频率很高,却经常被开发者忽略。

1.3 FMAC 能做什么,不能做什么

FMAC 能做的事情非常集中,但做得很深。它支持常见的 FIR 滤波器,也支持 IIR 滤波器,包括直接 I 型和直接 II 型转置结构。这些滤波结构覆盖了绝大多数实时信号调理需求,比如低通滤波、高通滤波、带通滤波、陷波滤波,只要你能计算出系数,FMAC 就能替你算卷积。

但要注意,FMAC 不是通用的 DSP 加速器。很多新手容易误解,觉得既然能加速滤波,那 FFT、矩阵运算、PID 参数计算是不是也能加速?答案是行不通的。FMAC 的硬件状态机是按照滤波器的数据流模式设计的,它只认识“样本数组 + 系数数组 = 输出数组”这种模式。想要它去算 FFT,那种蝶形运算的跳变访问模式,硬件本身就不支持。

此外,FMAC 的工作数据格式是定点数,通常是 Q1.15 这种左对齐格式,不是浮点数。这意味着在放入数据之前,要把浮点采样值转换成定点表示。这个限制在实际项目中其实还好,因为 ADC 采出来的数据本质上是整数,转换成 Q15 格式并不困难,而且定点计算的时序确定性比浮点更好,这对于实时控制系统反而是个优点。

2. FMAC 核心机理与寄存器级原理

2.1 一切的核心:16x16 乘加阵列与 Q15 左对齐数据格式

FMAC 内部的乘加阵列是理解它的钥匙。简单说,它一个周期能做一次 16 位乘 16 位的乘法,并把结果累加到一个高精度累加器中。FIR 滤波的每个输出点就是一组连续乘加,硬件自动循环执行,直到一个样本窗口处理完。

数据格式是 FMAC 最容易出错的地方。FMAC 内部使用的定点格式一般是 Q1.15 左对齐,也就是 16 位数据中,最高位是符号位,接下来 15 位是小数位,且数据要放在 16 位寄存器的高 16 位部分。这个“左对齐”听起来简单,实际操作时经常被忽略。比如 ADC 采集到一个 12 位数据,右对齐放在一个 16 位变量里,如果直接丢给 FMAC,硬件的解释和你预期的数值会有 4 个比特的差异,最终滤波结果会整体偏小或者出现奇怪的增益问题。

实际转换时,要把 ADC 数据左移,让有效位顶到最高位。例如 12 位采样值,需要左移 4 位并配合符号扩展,如果 ADC 是 16 位差分模式,则可能要保持原生有符号格式。在工程上我习惯把这种转换封装成一个宏,统一管理左移位数,免得每个通道各写一遍。

2.2 四个缓冲区、地址通道和自动计算状态机

FMAC 外设内部有多个存储区,分别是输入样本区、系数区、输出区和中间结果区。我从参考手册和实际调试中理解的逻辑是,样本区存放待滤波的原始数据序列,系数区存放滤波器系数,输出区存放计算得到的滤波结果,而中间结果区暂存部分积累加结果,尤其是 IIR 滤波需要保留历史状态时,这个区域特别重要。

访问这些存储区时,FMAC 提供了对应的访问通道,通过写地址寄存器来选定要访问的缓冲区,然后通过数据寄存器连续写入或读取。这种设计有点像访问内部 SRAM,但又带上了专用硬件的边界限制,地址越界会有错误标志。

更关键的是,FMAC 内部有一个自动计算状态机。一旦设置好滤波模式、配置好系数和样本,并触发启动操作,状态机就会自动从缓冲区按顺序取数、执行乘累加、写回结果,全部流程不需要 CPU 中途介入。整个过程结束后,硬件会拉高一个 DONE 标志,并可以产生中断通知 CPU。这个异步工作模型让 CPU 可以在 FMAC 忙碌时去处理其他实时任务。

2.3 从 FIR 数学公式到硬件流水线

我习惯用 FIR 滤波来解释 FMAC 的工作过程,因为它的结构最直观。假设一个 N 阶 FIR 滤波器的系数是 h(0) 到 h(N-1),输入样本序列是 x(k),那么输出 y(k) 的计算公式是:

y(k) = h(0) * x(k) + h(1) * x(k-1) + ... + h(N-1) * x(k-N+1)

这组公式翻译成硬件操作,就是 FMAC 内部的乘加阵列,从缓冲区里取出系数 h(i),同时取出对应的样本 x(k-i),两者相乘后累加到累加器。每个输出点需要 N 次乘累加,硬件用一个循环计数器控制,做完 N 次之后把累加结果写到输出缓冲区。

IIR 滤波器稍微复杂一点,因为输出不仅依赖于输入样本,还依赖于之前的输出值,也就是反馈项。FMAC 对这类结构同样有硬件映射,借助中间结果缓冲区保存历史状态。我们在配置时,需要把 IIR 的系数按特定顺序写入系数区,顺序一旦错位,滤波结果会完全不对,这个后面踩坑部分会详细说。

2.4 关键寄存器速查

这里我整理了一份 FMAC 常用寄存器的简明速查表,方便大家在写驱动的时候对照,具体字段细节请以对应型号的参考手册为准:

寄存器主要作用我的使用心得
FMAC_CR外设使能、中断使能、工作模式控制启动前先清零,防止上一次的残留配置
FMAC_CFG配置滤波类型、数据格式、缓冲区大小改配置前确保 FMAC 处于禁用状态
FMAC_CSR状态标志,包括 DONE、写错误、读错误操作结果后要手动清标志,不能只读不理
FMAC_WDATA向当前选中缓冲区写入 16 位数据写入顺序要按照滤波器的数据流顺序
FMAC_RDATA从当前选中缓冲区读取 16 位数据读取前确认地址指向,否则读到脏数据
FMAC_X1ADDR选择样本区 X1 的读/写地址可设自动回卷,连续采样场景特别有用
FMAC_X2ADDR选择系数区 X2 的读/写地址系数加载前务必做好地址复位
FMAC_YADDR选择输出区 Y 的读/写地址输出读取时要按有效样本个数读,别多读
FMAC_PADDR选择中间结果区 P 的读/写地址IIR 模式下这个区很关键,不能忽略

这些寄存器看起来多,但实际驱动写起来并不复杂。FMAC 的控制思路非常模块化,只要记住“配置 — 灌数据 — 启动 — 等完成 — 取数据”这条主线,就不会乱。

3. 工程实践:手把手配置 FMAC 跑一个 64 阶 FIR

3.1 硬件与软件开发环境准备

我这次用的平台是 NUCLEO-G474RE 开发板,主控是 STM32G474RET6,主频 170MHz,内置 FMAC 外设。当然,G431 等同样带 FMAC 的型号也可以直接参考。

软件环境我用的是 STM32CubeIDE,因为从 1.13 版本开始,它对 G4 系列的支持已经很完整,调试器配置也省心。如果你习惯 Keil MDK,操作一样,无非是芯片包安装好、头文件路径配置无误即可。我建议准备一个 J-Link 或者 ST-Link,因为 FMAC 调试过程中需要频繁看寄存器和内存,一个好用的调试器能省不少时间。

除此之外,建议准备 MATLAB 或者 Python,用来生成滤波器系数和验证结果。我用 Python 的 scipy.signal 里的 firwin 函数生成 64 阶低通 FIR 系数,然后把系数转换成 Q15 定点格式,这个流程可以避免手算系数的低级错误。

3.2 用 CubeMX 完成基础时钟与中断配置

打开 STM32CubeMX,选择 STM32G474RE 芯片,先配置好时钟。我习惯让系统主频跑满 170MHz,外部晶振用开发板自带的 8MHz。时钟树的配置要点是让 HCLK 为 170MHz,APB1 和 APB2 外设时钟按默认比例即可,FMAC 挂在 AHB 总线上,一般会自动分频。

FMAC 在 CubeMX 里的位置比较隐蔽,在 Computing 分类下,我看到有些旧版本的 CubeMX 甚至不直接生成 FMAC 的初始化代码。我的做法是:CubeMX 里先不勾选 FMAC,只是把时钟、调试接口(Serial Wire)、以及会用到的串口配置好,然后生成基础工程,最后在代码里手动操作 FMAC 寄存器。这样反而更可控,因为 FMAC 的操作流程非常固定,手写驱动也没几行。

如果是用 HAL 库,也可以直接调用 stm32g4xx_hal_fmac.h 里的接口。个人建议新手从寄存器版本入手,可以更直观地理解硬件工作过程,等理解透了再切到 HAL 库。

3.3 初始化 FMAC 与滤波器参数

初始化 FMAC,第一步是使能外设时钟。在 G4 系列中,需要操作 RCC 寄存器把 FMAC 的时钟打开,对应 HAL 库是 __HAL_RCC_FMAC_CLK_ENABLE()。然后,先配置 FMAC_CFG,设置 FIR 滤波模式、Q1.15 左对齐数据格式、以及缓冲区大小。配置完成后,往 FMAC_CR 里写使能位。

关于缓冲区和滤波阶数的关系,我建议预留足够余量。比如要做 64 阶 FIR,样本区至少要能容纳 64 个历史样本,加上本次要处理的新样本,我一般配置成 128 个字,这样处理完一批还能继续往里面写。系数区同样至少 64 个字。如果缓冲区配置过小,FMAC 会报错误状态,这个错误不好排查,因为现象往往是输出数据全零或者干脆不触发完成中断。

配置完基础参数后,要把滤波器系数写入系数区。我实现的流程是:先设置 FMAC_X2ADDR 指向系数区的起始地址,然后通过 FMAC_WDATA 逐个写入系数。注意系数本身是 Q15 格式,也就是把浮点系数乘以 32768 再取整,范围在 -1 到 1 之间。写入顺序必须和 FIR 数学公式中的 h(0), h(1), ..., h(N-1) 一致,顺序反了滤波频率特性会完全变样。

3.4 数据写入与启动滤波

系数加载完成后,就可以写输入样本了。写样本前,先设置 FMAC_X1ADDR 指向样本区起始位置,然后把 ADC 采样值转换成 Q15 左对齐格式,逐个写入 FMAC_WDATA。写满一批样本后,触发启动。在寄存器层面,启动滤波通常是往 FMAC_CR 里设置启动位,也可以理解为开启状态机运行。

滤波过程中,CPU 不要傻等。我一般的做法是使能 FMAC 的完成中断,然后 CPU 去处理其他任务,比如更新显示器、处理通信协议,或者干脆进入休眠等待。等到 FMAC 完成后触发中断,在中断服务函数里读取输出结果,读取时先设置 FMAC_YADDR 指向输出区的起始地址,然后通过 FMAC_RDATA 逐个读回滤波后的数据。

这里有个非常容易踩的坑:读取输出数据前,一定要检查 DONE 标志,确认状态机真的结束了。如果数据还没算完,中途去读输出缓冲区,读到的可能是刚刚初始化时的残留值。我建议用超时机制来等待 DONE 标志,既能保证正确性,也能避免因为硬件异常导致程序死等。超时时间可以取一个保守的值,比如 100ms,实际执行一般在微秒级就完成了。

3.5 完整例程代码与关键注意事项

下面是我实际调试通过的一段 FMAC FIR 滤波核心代码,精简掉工程无关部分,保留了关键操作流程,供大家参考:

#include "stm32g4xx.h" #define FIR_ORDER 64 #define FIR_BLOCK_SIZE 64 /* Q15系数,由scipy.signal.firwin生成后转换 */ extern int16_t fir_coeffs[FIR_ORDER]; /* 左对齐Q15转换:12位ADC数据左移4位 */ static int16_t adc_to_q15(uint16_t adc_value) { return (int16_t)(adc_value << 4); } void FMAC_FIR_Init(void) { /* 1. 使能FMAC时钟 */ __HAL_RCC_FMAC_CLK_ENABLE(); /* 2. 先禁用FMAC,准备配置 */ FMAC->CR = 0x0; /* 3. 配置FIR模式、Q1.15左对齐、X1/X2/Y缓冲区大小 */ FMAC->CFG = FMAC_CFG_FIR_MODE | FMAC_CFG_DATAFMT_Q15_LEFT | FMAC_CFG_X1SIZE_128 | FMAC_CFG_X2SIZE_128 | FMAC_CFG_YSIZE_128; /* 4. 使能FMAC,关闭中断,后续单独开 */ FMAC->CR = FMAC_CR_EN; /* 5. 加载系数到X2缓冲区 */ FMAC->X2ADDR = 0x0; for (int i = 0; i < FIR_ORDER; i++) { FMAC->WDATA = fir_coeffs[i]; } /* 6. 地址指针复位 */ FMAC->X1ADDR = 0x0; FMAC->YADDR = 0x0; } uint32_t FMAC_FIR_Process(int16_t *input, int16_t *output, uint32_t block_size) { /* 1. 复位指针,写输入样本 */ FMAC->X1ADDR = 0x0; FMAC->YADDR = 0x0; for (uint32_t i = 0; i < block_size; i++) { FMAC->WDATA = input[i]; } /* 2. 清DONE标志,再启动 */ FMAC->CSR = FMAC_CSR_DONE; /* 3. 启动滤波 */ FMAC->CR |= FMAC_CR_START; /* 4. 等待完成,增加超时保护 */ uint32_t timeout = 10000; while (!(FMAC->CSR & FMAC_CSR_DONE)) { if (--timeout == 0) { return 0xFFFFFFFF; /* 超时错误 */ } } /* 5. 自动读取输出 */ for (uint32_t i = 0; i < block_size; i++) { output[i] = (int16_t)(FMAC->RDATA); } return 0; }

从这段代码能看到,FMAC 的驱动核心并不复杂,但顺序错了就全盘崩溃。有几个细节提醒一下:

第一,写配置寄存器之前一定要先失能 FMAC,不要在使能状态下改 CFG,否则行为不可预知。第二,启动前必须清除老的 DONE 标志,否则启动后会立刻误判为完成,我第一次调试就栽在这里。第三,如果使用 DMA 来搬运数据,要等 DMA 完全停止之后再去配置 FMAC 的地址寄存器,否则总线仲裁阶段可能出现请求乱序。

4. 与 CMSIS-DSP 协作,以及另一条更省事的路

4.1 CMSIS-DSP 如何自动识别并使用 FMAC

手工操作寄存器能让人彻底理解 FMAC,但产品开发追求效率,ST 官方其实提供了更省事的接口,那就是 CMSIS-DSP 库。CMSIS-DSP 是 ARM 官方的数字信号处理函数库,在 STM32G4 系列上,它对 FIR 和 IIR 滤波函数做了优化,底层会检测当前芯片是否支持 FMAC,如果支持,就自动把滤波计算映射到 FMAC 硬件上。

这意味着我们几乎可以不关心 FMAC 寄存器的细节,只要调用标准的经典接口,比如 arm_fir_f16 或者 arm_biquad_cascade_df2T_f16,并确保项目开启了 FMAC 支持,CMSIS-DSP 就会选择合适的后端实现。这个策略我特别喜欢,因为它的上层 API 完全标准化,今天用 G4 的 FMAC,明天换到不带 FMAC 的 F4 系列,代码不用改,只是执行速度不同。

不过要注意,CMSIS-DSP 对 FMAC 的加速主要体现在数据路径上,它仍然会占用一个 DMA 通道或者内核少量开销来做数据调度。我实测发现,用 CMSIS-DSP 的 f16 FIR 函数时,数据格式必须匹配硬件要求,依然是左对齐格式,内部会做格式转换,但用户层的 API 可以设计得比较友好。

4.2 实测数据:三种实现方式对比

为了让大家对 FMAC 的实际收益有直观感受,我把三种实现方式放在同一块 NUCLEO-G474RE 上做了对比。测试条件是 64 阶 FIR 低通滤波器,处理 1024 个采样点,输入数据为 Q15 格式,CPU 主频 170MHz:

实现方式耗时CPU 占用说明
纯 C 软件循环约 1.8ms计算期间 CPU 被占满编译器优化 -O2,最朴素实现
CMSIS-DSP 软件优化约 0.9ms计算期间 CPU 被占满带 Cortex-M4 DSP 指令优化
FMAC 硬件加速约 0.4msCPU 只在搬运和等待时短暂占用启动后可做其他任务,完成产生中断

从这个表能看出,FMAC 的速度优势并不是一个数量级,它的价值更多体现在“释放 CPU”,我在测试时特意把 FMAC 和 ADC 采集、串口打印并行运行,整个系统运行的流畅度提升非常明显。

还有一种情况,如果你的系统里滤波任务占 CPU 的比例原本就不高,那 FMAC 的收益感受不会太明显。这很正常,FMAC 是给特定场景准备的,用对了地方是宝藏,用不对地方就是多余外设。

4.3 两条路径怎么选

从我自己的项目经验出发,如果目标是快速完成项目、代码可维护性好,首选 CMSIS-DSP 库的 FMAC 后端,节省开发时间,后续移植也方便。

如果你想做深度优化,比如把 FMAC 和 ADC 采集、DMA 传输、双缓冲机制整合成一个完整的流水线,建议手工操作寄存器,因为这种级别的定制需要精确控制每个时间片。我在振动监测项目里最终选择的是寄存器方式,因为需要把 FMAC 的完成事件通过中断直接触发 FOC 控制周期的计算,中间不能再隔一层 CMSIS-DSP 的封装。

5. 实际项目中踩过的坑与排查实录

5.1 左对齐数据处理不当导致输出错误

这是 FMAC 项目里最容易翻车的地方。有一位朋友在群里问过我,为什么同样的系数,MATLAB 仿真没问题,放进 FMAC 里波形增益差了 16 倍。问题就出在 ADC 数据是右对齐,而 FMAC 要求左对齐,直接喂进去,每次乘法都少了有效位数。

解决思路很明确:进入 FMAC 之前统一做左移处理。如果 ADC 是 12 位,左移 4 位;如果是 16 位差分,确认符号扩展正确后直接使用。我建议在数据采集的结构体里就把采样格式标准化,不要在滤波函数里再做转换,否则极易遗漏。

5.2 DONE 标志与中断的处理顺序

FMAC 的 DONE 标志处理不当,会产生两种现象:要么程序卡死在等待循环里,要么中断服务函数被执行了无数次。原因是 DONE 标志是写 1 清除的,如果中断一进来就立刻清除标志,但数据还没来得及全部读走,下一次状态机又已经启动,时序就乱套了。

我的习惯是先读数据,全部读完后清 DONE 标志,再启动下一批。这样保证数据读取过程的完整性,也不会误触发下一次中断。如果是手动等待方式,启动前再清一次 DONE,双保险。

5.3 DMA 与 FMAC 并发时的优先级问题

在高速采样系统里,ADC 的数据通常用 DMA 搬运到内存,再由 CPU 或 FMAC 处理。稍微复杂一点的系统还有 DAC 输出 DMA,这就可能出现多条 DMA 数据流同时访问内存。FMAC 的缓冲区和 AHB 总线共享带宽,如果 DMA 优先级设置不当,FMAC 读取数据时可能被反复插入等待周期,导致滤波周期不稳定。

我踩过一次这样的坑:系统里有一条高优先级 DMA 在做 ADC 搬运,另一条 DMA 做串口发送,FMAC 滤波周期出现了周期性抖动。排查到最后才发现是串口 DMA 优先级设得比预期高。调整方案是:ADC DMA 保持最高优先级,FMAC 相关 DMA 配置为次高优先级,串口这类非实时传输降为最低优先级,问题立刻消失。

5.4 不要指望 FMAC 解决所有滤波问题

FMAC 虽好,但硬件实现决定了它只能按照固定数据流处理。我尝试过用 FMAC 做自适应滤波器,需要在线更新系数,每次更新都要重新加载系数区,结果系数更新的开销比滤波本身还大,还不如查表切换系数。另外,如果滤波器的阶数非常低,比如 2 阶 IIR,FMAC 的启动和配置开销可能比软件跑一次还慢,这种情况用软件滤波更合适。

5.5 问题排查速查表

我把 FMAC 项目里常见的现象和排查思路整理成了一张表,遇到问题可以先对照排查:

现象可能原因排查办法
输出全是 0FMAC 未使能或未配置缓冲区大小检查 FMAC_CR 的 EN 位和 FMAC_CFG 的缓冲区配置
输出整体偏小输入数据未做左对齐处理检查数据格式转换,确认有效位是否在最高位
卡在等待 DONE 标志DONE 标志没清或滤波未真正启动启动前写 1 清 DONE,确认 START 位已置位
滤波输出有毛刺样本写入和滤波读取共用地址指针每次读写前强制复位 X1ADDR / YADDR
中断一直被触发DONE 中断标志未清在中断服务函数内写 1 清除 DONE 位
系统实时性变差DMA 优先级和 FMAC 争抢总线合理设置 DMA 优先级,保证 FMAC 数据流畅通
偶尔出现读/写错误标志地址越界或缓冲区大小不足检查访问的缓冲区长度是否超过配置值

6. 什么时候用 FMAC,什么时候用软件滤波:我的选型建议

6.1 优先用 FMAC 的场景

如果项目满足以下任意几条,我建议优先把 FMAC 纳入方案:

第一,采样率高且需要连续滤波,比如音频处理里的 48kHz 采样,振动监测里的 20kHz 采样,纯软件滤波会占掉大量 CPU 时间。第二,滤波阶数较高,比如 32 阶以上的 FIR,或者多级 IIR 级联。第三,系统同时存在多个实时任务,比如电机 FOC、PFC 控制、通信协议栈,CPU 预算非常紧张。第四,需要确定性时序,FMAC 的硬件状态机完成时间基本固定,不容易出现软件中断嵌套导致的时间抖动。

6.2 继续用软件滤波的场景

反过来说,有些情况用软件滤波反而更合理。滤波阶数很低,比如 2 到 4 阶 IIR,软件计算只要几十个周期,FMAC 的配置开销反而显得笨重。采样率很低,比如温度采集每秒一次,滤波耗时完全无所谓。系统对浮点精度要求非常高,需要 FPU 直接算浮点系数,FMAC 的 Q15 定点格式可能会在连续高通滤波时产生量化噪声。

还有一种场景要考虑:项目使用的芯片不带 FMAC,比如 STM32F4 系列,这时候为了一个滤波外设换平台不划算,直接用 CMSIS-DSP 的软件优化足矣。

6.3 选型决策参考

我习惯用一个简单的判断流程来决定:先看 CPU 占用率,滤波任务占 CPU 超过 20%,再看阶数和采样率,如果阶数高或采样率高,就用 FMAC;如果 CPU 占用率低,直接软件滤波;如果项目既需要滤波又对成本敏感,优先选带 FMAC 的 G4 系列,比外挂 DSP 划算得多。

从我这一年多的使用经验来看,FMAC 是 STM32G4 系列里最容易被忽视的外设之一。很多人做电机控制,盯着 HRTIM、ADC、比较器,很少有人会去翻 FMAC 这一章。但真正跑起来之后,它带来的系统流畅度提升是非常实在的。最后再分享一个小技巧:调 FMAC 的时候,准备一个已知的脉冲或者阶跃序列,先在 MATLAB 里算出理论滤波输出,再灌进硬件跑一遍,两边波形对比,不到十分钟就能定位出格式问题还是系数顺序问题。这套方法我每次调试都在用,几乎没有失手过。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 1:28:24

Orbbec深度相机ROS2部署实战:从单设备调通到多相机配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:27:28

ADXL355高精度加速度计寄存器配置与工业级数据采集实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:26:05

FPGA实战:基于XC7A50T从零打造LED控制器

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:25:41

PCB过孔工艺全解析:通孔、盲孔、埋孔选型与成本控制指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:24:11

XC6206P332MR LDO芯片详解:从丝印识别到应用选型

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/6 1:23:44

PADS Layout导出DXF保姆级教程:从图层映射到AutoCAD兼容性全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华