2026 ADI CodeFusion 竞赛 把机械振动“看见”:一台 MEMS 频谱分析仪的完整设计过程
该项目使用了ADXL382,MAX32655,ESP32-C3,实现了3轴同步 16kHz 采样的高动态范围振动采样仪的设计,它的主要功能为:本项目设计并实现了一套基于 ADXL382 的机械振动频谱分析仪,围绕传感器选型、机械结构、数据采集、实时传输和边缘 DSP 分析完成了完整验证。系统采用金属底座提高机械刚度,并通过有限元分析评估固有频率、振动传递和应力分布;ADXL382 负责三轴宽带振动采集,MAX32655 完成高速数据读取、FFT、RMS、峭度、峰值及频带能量等特征计算,再通过 ESP32-C3 完成数据转发、显示和上位机交互。最终系统实现了 16 kSPS 三轴连续采集和 4096 点实时 FFT,在保证原始数据完整传输的同时具备本地振动特征提取能力,可用于机械状态监测、故障分析及结构振动测试。
标签
嵌入式系统
Funpack活动
显示
ADC
USB
yunswj
更新2026-09-29
86
  • 从事信号链设计的工作久了,对这个纯粹的应用特别感兴趣,首先是够精密,然后还可以在边缘做计算,另外还有现实的含义;那这次就设计了振动分析仪。
    现在振动传感器有两个路线一个是分立的:

    需要使用复杂的调理电路,而且体积较大,没有办法做到很灵活。

    体积较大,不灵活;多路采集的话还得需要同步 ADC,整体数据量对于 MCU的接口和处理都是难以接受。
    第二种选项就是使用 MEMS传感器,对整个系统做数字化处理;在 ADI 的产品库里面专门找到了 ADXL382,其定位非常明确:

    低噪声、宽带、三轴数字 MEMS 加速度计,主要面向机械振动、状态监测、结构健康监测等应用。


    官方列出的典型应用就包括 Condition-Based Monitoring、Structural Health Monitoring、Seismic Imaging 和 Robotics。

    在内部已经集成了完整的数字信号链系统:

    先看 ADXL382 最核心的性能,指标包括:

    参数

    ADXL382

    轴数

    3

    量程

    ±15 g, ±30 g, ±60 g

    HP 模式输出

    16 bit

    低功耗路径

    12 bit

    HP X/Y 噪声密度

    44 μg/√Hz

    HP Z 噪声密度

    55 μg/√Hz

    最大默认带宽

    8 kHz

    HP 默认 ODR

    16 kHz

    HP 功耗

    520 μA

    ULP 功耗

    33 μA

    Standby

    6.8 μA

    FIFO

    320 word

    SPI

    有

    I2C

    有另一型号

    I2S/TDM

    有

    PDM

    有

    温度传感器

    内置

    外部同步

    支持

    Self-Test

    支持

    冲击耐受

    10,000 g

    工作温度

    -40 °C~125 °C

    这里有四个参数特别重要:
    44 μg/√Hz、8 kHz、16 bit
    以及:
    ±15 g/±30 g/±60 g
    这四者基本确定了它是一个宽带机械振动传感器。

    可测高频振动

    例如:
    100 Hz、1 kHz、5 kHz
    机械振动。

    冲击

    因为量程最高:
    ±60 g
    而机械耐冲击达到:
    10000 g

    为什么共振频率这么重要?

    从手册里面查到机械共振频率:

    轴

    共振频率

    X/Y

    8.3 kHz

    Z

    6.2 kHz

    因为一个机械加速度计本质上是二阶系统,幅频响应大致表现为:

    如果工作频率逼近机械共振:
    f → f_r
    传感器自身结构会明显放大某些频率,所以:

    不能只看到“8 kHz bandwidth”,就把 0~8 kHz 所有点都当成天然完全平坦。

    特别是 Z 轴机械共振:
    f_r,Z ≈ 6.2 kHz
    而目标:
    f_max = 5 kHz
    已经比较接近,这就是为什么 ADXL382 内部专门做了 Equalizer。
    另外在官方技术文章里面也看到:

    有机械设计的一些指南,底座需要设计成金属:

    所以这里传感器需要放置一个硬质,平面的座子上。
    传感器的机械结构由铝制底座、金属外壳侧壁和顶部 ABS 塑料盖组成,其中金属底座负责提供足够的刚度并将被测设备的振动尽可能完整地传递到 MEMS 传感器,而 ABS 顶盖则主要用于减小金属外壳对内部 BLE 天线的屏蔽影响。
    MEMS 传感器及其电源 PCB 直接布置在底座附近,使其尽可能靠近振动源,BLE 与边缘 AI 微控制器 PCB 则采用垂直安装方式,电池通过独立支架固定;底座同时预留 M6 螺纹孔,可通过螺柱或粘接方式安装到电机等被测设备上。对于这类宽带振动传感器,机械结构本身也是测量信号链的一部分,因此设计时必须重点避免结构谐振:当外部振动频率接近外壳或底座的固有频率时,结构会产生明显的共振放大,从而使 MEMS 实际测得的振动不再等同于机器本身的真实振动。
    Voyager4 的目标测量带宽达到 8 kHz,因此要求机械结构在 8 kHz 以下尽量不出现明显的固有模态和共振峰;实际设计中可以通过降低外壳高度、提高结构刚度、减小不必要的悬臂和质量等方式提高固有频率,使机械共振远离目标测量频段。
    (有同学复现的时候,可以从这里的结论做一些修改)

    简单仿真一下

    我用 Python 编写一个轻量级的一维有限元(FEA)悬臂梁模型,来模拟传感器外壳的共振计算;根据文档,Voyager4 传感器的最小高度为 77 mm,直径为 46 mm,底座采用 3003 铝合金,并且底部通过 M6 螺柱刚性固定;可以将其等效为一个固定在底部的圆柱形悬臂梁。
    这段代码使用了 numpy 和 scipy,直接求解了文档中提到的核心模态方程:([K] - ωi²[M]){φi} = {0}。
     === 传感器结构模态分析结果 ===
     模式 1 (Mode 1) 固有频率: 5456.71 Hz
     模式 2 (Mode 2) 固有频率: 34196.66 Hz
     模式 3 (Mode 3) 固有频率: 95753.00 Hz

    这建立了质量矩阵 [M](代表外壳和组件的质量分布)与刚度矩阵 [K](代表 3003 铝合金的结构抗变形能力)。调用 eigh(K, M) 实际上就是在求解特征值 ω²。

    可以看到输出的“模式 1”频率;正如文档指出,设计人员面临的挑战是,固有频率必须大于 MEMS 加速度计测量的振动频率(8 kHz 带宽)。如果这段代码跑出来的 Mode 1 小于 8000 Hz,就意味着发生低频共振。

    后期设计的话,可以在公式 ω = √([K]/[M]) 的指导下,改动代码里的参数来“优化”设计;将高度 L = 0.077 降低到 0.050 (50 mm);将铝合金的弹性模量 E 换成更硬的材料(比如钢:200 GPa),运行后发现,由于刚度提升、质量减轻,固有频率大幅上升,可以避开了机器的运行频率。

    FreeCAD登场

    做了仿真就好了,知道了一些规律,那就使用 FreeCAD 设计一下(MAC 上面也没啥靠谱的工业设计软件):


    设计一个底座先,使用 CNC。

    使用嘉立创 CNC平台下单


    还是太方便了~

    详细仿真

    思路是按照 Voyager4 做了一轮初步机械有限元近似(我的背景不是专业搞有限元分析的),仿真真实的目的是:
    机器的真实振动,能不能不失真地传到 MEMS 加速度计。
    先分析外壳和底座的固有频率、振型和模式参与因子,再看这些模态是否会影响安装在底座上的 ADXL382,所以设计的底座同时做了两种图:和文档最接近的模态振型图+真正的 von Mises 应力云图。
    直接从 STEP 模型得到:

    参数

    结果

    外形 X

    约 52 mm

    外形 Y

    约 50 mm

    总高度

    约 8.5 mm

    实体体积

    约 15332 mm³

    假设 3003 铝质量

    约 41.9 g


    能看到几个很明显的机械设计特征:四周有较高的框架;中间是一块面积比较大的平板;四个主要安装孔分布在两侧;安装孔附近通过斜筋和外围框架连接;中间 PCB/MEMS 区域和外围固定点之间存在一定跨度。

    MEMS 必须尽量靠近真正的振动源

    底座和中部结构采用铝;MEMS 和电源 PCB 放在底座上;而且尽量靠近被测振动源;上部 BLE/AI PCB 则竖直布置;顶盖使用 ABS 塑料,避免金属对 BLE 天线产生过强屏蔽。

    第一版设计


    PCB 部分使用垂直安装:
    实物样子

    塑料底座为测试品,最终会使用 CNC 底座

    这也是最小桥的一个版本(后面有改动)

    新的设计

    仿真条件

    材料先按文档中的金属底座处理:
    E = 69 GPa,ν = 0.33,ρ = 2730 kg/m³
    也就是近似 3003 铝。
    边界条件没有粗暴地把整个底面固定,而是把四个较大的安装孔周围区域看成螺栓固定区:

    图里的星号是定义的 MEMS/PCB 中心测量点。

    底座前四阶模态

    彩色模态图:

    前四阶固有频率大约是:

    模态

    固有频率

    Mode 1

    17.25 kHz

    Mode 2

    17.83 kHz

    Mode 3

    23.05 kHz

    Mode 4

    30.25 kHz

    继续往上:

    模态

    固有频率

    Mode 5

    33.37 kHz

    Mode 6

    33.78 kHz

    Mode 7

    37.73 kHz

    Mode 8

    40.16 kHz

    这一点其实相当不错:
    f₁ ≈ 17.25 kHz
    可以看到底座本体第一阶模态已经明显高于:8 kHz

    第一阶是 Z 向强模态

    又算了一下模式有效质量,第一阶 f₁ ≈ 17.25 kHz,在 Z 方向的有效质量参与率大约:54.4%;结果来看它不是某个“无关紧要的小凸台,其局部抖动”是一个会被 Z 向底座振动明显激励的模式。
    而主要 Y 向强模态大约在:
    30.25 kHz
    X 向明显参与的模态则大约在:
    33.37 kHz
    就这个底座而言:Z 方向是目前最值得重点优化的机械方向,因为 X/Y 方向有外围框架约束,而 Z 方向最容易让中间大面积板区发生弯曲。

    进一步算0~8 kHz 的机械传递函数

    这里假设:
    ζ = 2%
    并观察底座固定端的振动传到中间 MEMS 假定位置以后,三个轴的加速度误差,结果:

    底座振动传递函数,在 8 kHz:

    方向

    约机械增益

    X

    +0.38 dB

    Y

    +0.76 dB

    Z

    +2.29 dB

    这个结果就很有意思了,如果目标也像 Voyager4 一样,要求整个:0~8 kHz,保持在大约 ±1.5 dB,那么按当前的粗模型:

    X、Y 基本比较舒服,Z 方向开始有些紧。

    Z 方向大约到:f ≈ 6.58 kHz,已经触及 +1.5 dB;不过也已经满足我的设计要求。

    静态惯性载荷分析

    四个安装区域固定,在整个底座上施加 1 g 惯性载荷,得到:

    载荷方向

    最大位移

    最大 von Mises 应力

    X,1 g

    0.287 nm

    2.39 kPa

    Y,1 g

    0.485 nm

    3.35 kPa

    Z,1 g

    1.26 nm

    4.41 kPa

    就算粗暴放大到:60 g,Z 向最大应力也只有大,σ_VM,max ≈ 0.265 MPa。
    真正的应力云图如下:

    60 g Z 向 von Mises 应力云图,这里图上的变形被我人为放大了,便于看清楚;应力数值则没有按视觉比例乱放大。

    频率响应图

    机械部分解决了,继续看 MEMS 的性能指标:
    image-20260920160147671
    X/Y/Z 的频率响应,可以明显看到:原始 MEMS 响应在高频端上翘;接近机械共振时增益增加;不同工作模式带宽不同;HP + Equalizer 可以把高频响应重新压平。

    ADXL382 最有特色的地方:两条 ADC 信号链

    这是整颗芯片的核心,ADXL382 内部有两种完全不同的采样路径:高性能路径使用 16-bit DSM;低功耗路径使用 12-bit SAR;两条路径还可以同时工作,这个设计非常有意思。

    PCB 设计

    因为现在官方的评估版小贵,而且端口做了复用,不够灵活:

    严格根据数据手册的要求设计,而且上面的机械要求也是要把传感器装到一个小板子上面,确实是有必要这样设计的。

    刷锡膏

    放器件,开始进行回流焊;另外 ADXL380 是382 更加灵敏的版本,量程缩小一倍,其它一模一样,我也装了一颗:


    然后还测了一下医疗上面的心震图:


    因为有专门的测量模式,所以这套测量系统就用处很广了,可以通过换传感器核心来测量不同的数据。

    说回 ADXL382

    另外 ADXL382 支持七种主要模式:

    模式

    ADC

    默认带宽

    典型功耗

    定位

    Standby

    —

    —

    6.8 μA

    配置/休眠

    HP

    16-bit Σ-Δ

    8 kHz

    520 μA

    振动频谱

    RBW

    16-bit Σ-Δ

    4 kHz

    350 μA

    中带宽振动

    LP

    16-bit Σ-Δ

    2 kHz

    200 μA

    低功耗连续振动

    VLP

    12-bit SAR

    500 Hz

    44 μA

    运动检测

    ULP

    12-bit SAR

    50 Hz

    33 μA

    超低功耗运动

    HS

    12-bit SAR

    400 Hz

    32 μA

    心音

    带宽和模式关系由数据手册给出;对机械振动仪,直接锁定 HP,项目要求:
    BW = 0~5 kHz

    HP 为 BW = 8 kHz,满足。

    HP 模式默认 ODR 是 16 kHz

    HP:f_s = 16 kSPS,默认带宽 BW = 8 kHz,也就是 BW = f_s/2
    对于 0~5 kHz,非常合适,而Nyquist:f_N = 16000/2 = 8000 Hz,计算下来看在 5 kHz 仍然有 3 kHz过渡带空间。

    量程三挡:

    ±15 g、±30 g、±60 g,16-bit DSM 时:

    Range

    灵敏度

    单 LSB

    ±15 g

    2000 LSB/g

    500 μg/LSB

    ±30 g

    1000 LSB/g

    1 mg/LSB

    ±60 g

    500 LSB/g

    2 mg/LSB

    换算公式非常简单: ±15 g = a[g] = Code / 2000,或者:a[m/s²] = (Code / 2000) × 9.80665

    算法设计

    因为要做两个链路,分别是在设备端忠实的采集数据,以及 DSP 核心分段的对数据做 FFT;另外一个就是上位机端对 RAW 数据做更加精细的分析了。

    核心主控使用 MAX32655,足够小巧,足够性能强大:

    但是它的显示性能不太好,而且巨大的数据流没有办法通过 BLE 和 板载的调试串口完整移走,所以我使用了未被单独指出的一路 UART 做桥接,把完整的 RAW 和 DSP 计算数据给到了 XIAO ESP32-C3,且 C3 可以做显示和更多的人机交互:

    在这里,且给每个单独的系统都配备了电池,可以最大程度的保证采集任务不掉线:

    后面新加入了不少外设,屏幕也做了更新,所以设计了新的壳子,在底座不变的情况下,优化了内部布局,每个器件的都有属于自己的位置:


    装配细节,因为是样机就都用飞线了,正式版会重新设计一块 PCB:

    皆为垂直安装,另外 C3 还接入了一块 TFT 屏幕做数据展示和上位机控制:


    上位机做不同的控制时,页面会切换。
    系统构成为:
    ADXL382 负责振动数字采集 → MAX32655 负责 DSP 与故障特征提取
    ADXL382 本身已经包含 MEMS、模拟抗混叠、16-bit 高性能 Σ-Δ 路径和数字滤波,HP 模式适合宽带振动;器件标称宽带可到 8 kHz,XY/Z 噪声密度分别约为 44/55 μg/√Hz,并支持数字校正后的宽带平坦化。 MAX32655 则是 100 MHz Cortex-M4F,带单精度 FPU、SIMD/DSP 指令、128 KB SRAM 和 DMA,很适合做这一层边缘 DSP。
    设计要求实际上对应六个技术问题:

    题目要求

    DSP问题

    同步采集

    固定 f_s,XYZ sample index 对齐

    DC~5 kHz

    Nyquist、抗混叠、传感器带宽

    1024~4096 FFT

    FFT 长度与 RAM/CPU

    ≤1 Hz 分辨率

    Δf = f_s/N

    主要振动频率

    峰值检测、插值

    故障分析

    RMS、峭度、谐波、阶次、包络等

    预警

    基线、阈值、持续性判断

    项目要求最高分析频率:
    f_max = 5 kHz
    Nyquist 要求:
    f_s > 2f_max
    因此至少:
    f_s > 10 kSPS
    工程上不会正好选 10 kSPS,因为还需要给抗混叠滤波器留下过渡带;另外ADXL382 在 HP 模式下可以工作在 16-bit 高性能路径,而且官方的 HP 串行输出配置对应 16 kHz ODR;它本身的宽带指标达到 8 kHz。

    ADXL382 推荐的工作参数

    参数

    设置

    模式

    HP

    ADC 路径

    16-bit DSM

    ODR

    16 kSPS

    量程

    初期 ±15 g

    XYZ

    全开

    EQ

    开启

    内部 HPF

    初期关闭

    内部额外 LPF

    初期关闭

    数据接口

    SPI

    MCU

    MAX32655

    DSP

    Cortex-M4F

    ±15 g 时 ADXL382 的灵敏度是:
    2000 LSB/g
    即:
    500 μg/LSB
    因此 MCU 收到 int16_t:x[n]
    转换到 g:
    a_g[n] = x[n] / 2000
    转成 SI:
    a[n] = a_g[n] × 9.80665
    单位m/s²

    调试过程

    382 是比较简单的,就是 SPI 接口,然后有 INT 引脚;最大的问题就是 ADI 的 MCU 设计电压域,默认是 1.8V,然后可以通过修改结构体里面的内容:
     mxc_spi_pins_t pins = {
        .clock = true,
        .miso = true,
        .mosi = true,
        .ss0 = false,
        .ss1 = false,
        .ss2 = false,
        .vddioh = false,
     };
    需要修改为:
     mxc_spi_pins_t pins = {
      .clock = true, .ss0 = false, .ss1 = false, .ss2 = false,
      .miso = true, .mosi = true, .sdio2 = false, .sdio3 = false,
      .vddioh = true,
      };
    MAX32655 的 GPIO 配置里有一个容易忽略的字段:
     typedef enum {
        MXC_GPIO_VSSEL_VDDIO,
        MXC_GPIO_VSSEL_VDDIOH,
     } mxc_gpio_vssel_t;
    实际控制 GPIO 的输出高电平所对应的电源域,输入高低电平判定门限,内部上拉所连接的电压域;SDK 最终会操作端口的 VSSEL 寄存器:
     case MXC_GPIO_VSSEL_VDDIO:
        port->vssel &= ~mask;
        break;
     ​
     case MXC_GPIO_VSSEL_VDDIOH:
        port->vssel |= mask;
        break;
    也就是说,同一个 P0.21 引脚,选择不同 vssel 后,虽然仍然是 SPI MOSI,但其电气特性不同。
    只修改电压域并重新烧录后,就连续收到了合理且稳定的数据:
     raw_x  = -5191
     raw_y = 1079
     raw_z = 5819
     ​
     x = -0.69213 g
     y = 0.14387 g
     z = 0.77587 g
     temperature = 29.61 °C
    而且三轴合加速度约为:
     sqrt(0.692² + 0.144² + 0.776²) ≈ 1.05 g
    这与静止状态下主要受到约 1 g 重力加速度相符,连续采样也只有正常噪声范围内的小幅波动

    软件设计

    本次是使用了 ADI 推出的全新环境CodeFusion Studio™,可以快速的建立项目和配置外设:

    而且是全平台支持,我这边使用 MAC 开发,可以看到这里打开了 SPI,GPIO,UART,在右边配置详细的细节


    时钟树也可以配置,我比较喜欢的是寄存位的查看:

    我使用了自带的一个 SPI demo 做修改,使用了ADI no-OS ADXL38x 官方驱动,后面把 no-OS 的 SPI 接口转换为 MAX32655 MSDK SPI API,并实现本板的软件片选和电压域配置。

    本工程不是代码生成器自动生成的工程,而是以 ADI MSDK 的 MAX32655 SPI 示例为基础进行集成。

    主要文件:
     SPI/
     ├── Makefile
     ├── project.mk
     ├── main.c
     ├── include/
     │   ├── adxl38x.h
     │   └── no_os_*.h
     └── src/
        ├── adxl38x.c
        ├── adxl38x_max32655_spi.c
        └── no_os_*.c
    官方参考代码:

    ADXL38x 驱动:https://github.com/analogdevicesinc/no-OS/tree/main/drivers/accel/adxl38x

    ADXL38x 示例:https://github.com/analogdevicesinc/no-OS/tree/main/projects/eval-adxl38x

    MAX32655 MSDK:https://github.com/analogdevicesinc/msdk

    整体逻辑

    SPI 和中断引脚

    ADXL382 信号

    MAX32655FTHR 引脚

    MCU 功能

    方向

    MISO

    P0.22

    SPI1_MISO

    ADXL382 → MCU

    MOSI

    P0.21

    SPI1_MOSI

    MCU → ADXL382

    SCK

    P0.23

    SPI1_SCK

    MCU → ADXL382

    CS

    P2.5

    普通 GPIO 软件片选

    MCU → ADXL382

    INT0

    P2.4

    普通 GPIO 输入

    ADXL382 → MCU

    INT1

    P2.3

    普通 GPIO 输入

    ADXL382 → MCU

    VDD/VDDIO

    3.3 V

    传感器及数字接口电源

    电源

    GND

    GND

    公共地

    电源

    ADXL382 与 MAX32655FTHR 必须共地。本文中的 P2.3 对应 INT1。

    软件片选

    MAX32655 的 SPI1 硬件片选 0 是 P0.20,但本工程不使用 P0.20;P2.5 被配置为普通 GPIO,由软件控制片选:
    传输开始:P2.5 拉低
    执行 SPI 全双工传输
    传输结束:P2.5 拉高
    因此,P0.20 不需要连接 ADXL382,也不需要在 SPI 初始化中申请硬件 SS 引脚;SPI 引脚配置中所有硬件 SS 均关闭:
     mxc_spi_pins_t pins = {
        .clock = true,
        .ss0 = false,
        .ss1 = false,
        .ss2 = false,
        .miso = true,
        .mosi = true,
        .sdio2 = false,
        .sdio3 = false,
        .vddioh = true,
     };

    SPI 参数

    当前接口参数如下:

    参数

    配置

    SPI 控制器

    MAX32655 SPI1

    工作模式

    Controller/Master

    SPI Mode

    Mode 0,CPOL=0、CPHA=0

    数据宽度

    8 bit

    位序

    MSB first

    总线宽度

    Standard,单线 MOSI/MISO

    测试频率

    100 kHz

    片选

    P2.5 软件片选,低有效

    传输方式

    阻塞式全双工传输

    SPI1 初始化使用 MSDK API:
     MXC_SPI_Init(MXC_SPI1, 1, 0, 1, 0, speed_hz, pins);
     MXC_SPI_SetDataSize(MXC_SPI1, 8);
     MXC_SPI_SetWidth(MXC_SPI1, SPI_WIDTH_STANDARD);
     MXC_SPI_SetMode(MXC_SPI1, MXC_SPI_MODE_0);
     MXC_SPI_HWSSControl(MXC_SPI1, 0);
    其中 MXC_SPI_HWSSControl(MXC_SPI1, 0) 禁用硬件 SS 控制,P2.5 完全由 GPIO 软件控制。

    ADXL382 SPI 协议

    ADI 官方 ADXL38x 驱动采用如下 SPI 命令格式。

    读取寄存器

    发送缓冲区格式为:[写命令] [数据0] [数据1]...

    软件片选时序

    每一次完整寄存器访问保持一次连续的片选:
     MXC_GPIO_OutClr(MXC_GPIO2, MXC_GPIO_PIN_5);
     MXC_SPI_MasterTransaction(&req);
     MXC_GPIO_OutSet(MXC_GPIO2, MXC_GPIO_PIN_5);
    多字节读取期间不能在命令字节和数据字节之间释放 CS。
    MSDK 负责 MAX32655 相关功能:Cortex-M4 启动和系统时钟;MAX32655FTHR 板级支持;GPIO 引脚复用和电压域;SPI1 控制器;UART 和 printf;GCC 编译、链接、HEX 生成;OpenOCD 下载和复位。
    编译时板级配置应使用:

    TARGET=MAX32655
    BOARD=FTHR_Apps_P1

    ADI no-OS ADXL38x 驱动层

    官方 adxl38x.c/.h 提供:ADXL380、ADXL382、ADXL384 设备识别;SPI/I2C 通信抽象;寄存器读写;软件复位;量程和工作模式配置;XYZ 加速度和温度读取;数据换算、自检、中断等功能。
    ADXL382 应使用对应的设备类型:

    .dev_type = ID_ADXL382

    ADXL382 的量程枚举为:
     ADXL382_RANGE_15G
     ADXL382_RANGE_30G
     ADXL382_RANGE_60G
    在 ±15 g 档位,官方驱动定义的灵敏度为:
     #define ADXL382_ACC_SENSITIVITY 2000  /* LSB/g */
    因此 ±15 g 时,原始加速度转换为 g 可使用:
     accel_g = (double)raw / 2000.0;
    其他量程还需要应用相应的量程倍数,优先使用官方驱动提供的转换函数,避免在应用层重复硬编码灵敏度。

    MAX32655 平台适配层

    src/adxl38x_max32655_spi.c 实现 no-OS 所需的接口:
     no_os_spi_init()
     no_os_spi_write_and_read()
     no_os_spi_remove()
    调用关系如下:
     main.c
      │
      ├─ ADXL38x 官方驱动 API
      │     │
      │     └─ no_os_spi_write_and_read()
      │             │
      │             ├─ P2.5 拉低
      │             ├─ MXC_SPI_MasterTransaction()
      │             └─ P2.5 拉高
      │
      └─ printf() → MAX32655 UART → USB 串口

    初始化流程

    采用以下顺序:初始化 P2.5,并先将 CS 拉高;将 INT0、INT1 配置为 VDDIOH 输入;等待传感器电源稳定;初始化 SPI1,选择 Mode 0、8 bit、MSB first 和 VDDIOH;读取并确认 ADXL382 设备标识;执行软件复位并等待复位完成;配置 ADXL382 量程;使能 X、Y、Z 和温度通道;设置工作模式;周期读取数据并通过串口输出。
    ADXL382 初始化参数示例:
     struct adxl38x_init_param init = {
        .comm_init.spi_init = {
            .device_id = 1,
            .max_speed_hz = 100000,
            .bit_order = NO_OS_SPI_BIT_ORDER_MSB_FIRST,
            .mode = NO_OS_SPI_MODE_0,
            .chip_select = 5,
        },
        .comm_type = ADXL38X_SPI_COMM,
        .dev_type = ID_ADXL382,
     };
    量程配置示例:
     adxl38x_set_range(dev, ADXL382_RANGE_15G);

    8 kHz 实时输出与 ESP32-C3 USB 桥接实测

    第一次写的时候使用了 8 kHz 的量程

    当前数据链路

     ADXL382
      └─ SPI1 2 MHz
          └─ MAX32655(8 kHz 读取,32 组 XYZ/包)
              └─ UART2 921600 8N1,P1.1 TX
                  └─ XIAO ESP32-C3 D3/GPIO5 RX
                      ├─ USB Serial/JTAG 原样二进制转发到电脑
                      └─ ST7789 显示链路状态、速率和最新 XYZ
    这里使用的是 MAX32655 的 UART2,不是连接 DAPLink 的 UART0:

    信号

    MAX32655FTHR

    XIAO ESP32-C3

    说明

    MAX → C3 数据

    P1.1 / UART2_TX

    D3 / GPIO5

    已实接并通过测试

    C3 → MAX 命令

    P1.0 / UART2_RX

    D10 / GPIO10

    当前可选,数据转发不依赖

    地

    GND

    GND

    必须共地

    二进制协议

    每包固定 206 字节,包含 32 组三轴样本:
     offset  size  字段
     0       2     sync = 0xA55A(线上字节 5A A5)
     2       1     version = 1
     3       1     type = 1
     4       4     first_sequence,little-endian
     8       2     sample_count = 32
     10     2     dropped_total
     12     192   32 × (int16 x, int16 y, int16 z),little-endian
     204     2     CRC16-CCITT,初值 0xFFFF
    数据量计算:
     8000 sample/s ÷ 32 sample/packet = 250 packet/s
     250 × 206 = 51,500 payload byte/s
     UART 8N1 线上开销:51,500 × 10 = 515,000 bit/s
     921,600 baud 理论占用率约 55.88%
    因此 921600 baud 对当前固定包协议留有约 44% 的线路余量,能够承载ADXL382 的 8 kHz XYZ 原始数据,15200 baud 则远远不够。

    测试结果

    测试方法:电脑持续打开 C3 USB 串口,先排空旧数据;通过 SWD 对 MAX32655;跳过 1.2 秒启动阶段;随后连续抓取 12 秒。接收端按同步字、版本、类型、样本数及 CRC16 校验切包,并检查相邻包的 first_sequence。
     测试日期                         2026-09-13
     抓取时间                         12.006482 s
     USB 收到原始字节                 622,592 byte
     CRC 正确的完整数据包             3,021 packet
     错误同步字候选                   0
     首包 first_sequence             9,408
     末包 first_sequence             106,048
     相邻包次数                       3,020
     序号增量恰好为 32               3,020 / 3,020
     链路缺失样本                     0
     序号倒退/源端复位               0
     MAX dropped_total(首包→末包)   0 → 0
     有效包速率                       251.614 packet/s
     折算样本速率                     8,051.651 sample/s
     有效二进制负载                   51,832.5 byte/s
     按实收字节折算 UART 占用率       56.27% @ 921600 8N1
    在 12 秒实测窗口内,MAX32655、UART2、ESP32-C3 USB Serial/JTAG桥接能够完整传出约 8.05 ksample/s 的 ADXL382 三轴数据;CRC、序号连续性和传感器端丢样计数三项均未发现丢包。

    链路压力

    通过 SWD 读取运行计数器:
     累计采样          1,640,810
     调度 Tick         1,640,810
     错过采样周期     0
     SPI 错误         0
     UART 丢弃样本     0
     已发送数据包     51,274
    约连续运行 205 秒仍无采样调度丢失。

    链路

    当前数据量/负载

    状态

    ADXL382 → SPI1

    每样本 7 字节,2 MHz,理论总线占用约 22.4%

    余量较大

    MAX32655 CPU

    100 MHz,每样本周期 125 µs

    当前零超期

    MAX → C3 UART

    51,500 B/s,约 515 kbps

    占 921600 baud 的 55.9%

    C3 → USB

    实测约 51.8 kB/s

    压力很小

    C3 → ST7789

    每秒刷新约 110 kB,40 MHz SPI

    约 2.2% 屏幕总线时间

    BLE

    目前只有控制和状态

    负载可忽略

    UART 是现有链路里最接近上限的环节,但仍有约 44% 理论余量。

    DSP性能

    首先要明确一点,MAX32655 没有独立的专用 DSP 核;它的“DSP 性能”来自 100 MHz Arm Cortex‑M4F 的 DSP 指令扩展和单精度浮点 FPU。
    官方硬件配置:

    项目

    MAX32655

    主 CPU

    Arm Cortex‑M4F

    主频

    100 MHz

    DSP 指令

    单周期 MAC、饱和运算、SIMD 等

    FPU

    FPv4-SP,硬件单精度浮点

    Flash

    512 KB

    SRAM

    128 KB

    指令缓存

    16 KB

    BLE 辅核

    32 位 RISC‑V,负责卸载 BLE 时序任务

    官方资料确认其为 100 MHz Cortex‑M4F,并有一个专门卸载 BLE 时序处理的 RISC‑V 协处理器;理论上每秒约有 1 亿个 CPU 周期;实际处理量取决于数据类型、缓存、访存和中断。
    对于当前 8 kHz ADXL382:
     CPU 时钟              100 MHz
     采样率                 8 kHz
     每个采样周期           125 µs
     每个采样周期 CPU 周期 12,500 cycles
    当前 SPI 每次读取 7 字节、频率 2 MHz,纯线路时间约为:
     7 × 8 ÷ 2 MHz = 28 µs
    因此一个采样周期理论上剩余:
     125 - 28 = 97 µs,约 9,700 CPU cycles/sample
    实际还需要扣除 SPI 驱动、CRC、打包、UART 和中断开销,不过目前实测超过 164 万次采样:
     错过采样周期    0
     SPI 错误       0
     UART 丢样       0
    当前还有处理余量。

    升级到16k SPS 采样率

    需要做的修改是:
     SysTick_Config(SystemCoreClock / 16000U)
    另外SPI 从 2 MHz 提升到 8 MHz,UART2 和 C3 UART1 改成 2 Mbps;改用固定 224 字节协议,加入双缓冲。(以及启用 FIFO)
    字节协议:
     Header     28 bytes
     Payload   192 bytes
     CRC32       4 bytes
     Total     224 bytes
    数据类型:
     0x01 RAW_XYZ
     0x02 STATUS
     0x03 CONFIG
     0x04 FAST_FEATURES
     0x05 SPECTRUM_FEATURES
     0x06 ALARM
     0x07 SPECTRUM_CHUNK
    原始数据固定:
     32 × int16 XYZ = 192 bytes
    ADXL382 配置为 HP 模式、XYZ、±15 g、开启 FIFO 的 Stream 模式,96 words 水位,即 32 组三轴;SPI 读取 193 字节 DMA burst。
    在 20.014 秒 的模式测试中,成功采集 10,016 个有效 V2 帧(320,512 个有效样本),实收速率达 16,014.69 SPS,且 CRC、缺失样本、序号中断、调度遗漏及 UART 错误均为 0,最后一组 XYZ 轴数据正常为 (-205, 488, 1912),期间 MAX32655 内部 SWD 计数显示 10 秒时基为 160,024、FIFO 取样 159,360、UART 完成 4,979 帧。

    内部DSP计算

    建立 4096 点环形采样缓冲,实现快速时域特征(去直流、RMS、峰值、峰峰值、波峰因子、峭度。)

    实现 4096 点 Hann 窗 + RFFT,对应:频率分辨率3.90625 Hz,奈奎斯特频率8 kHz,项目关注频段0–5 kHz

    数据帧里面原始 XYZ 数据仍保留,DSP 特征作为附加包发送;每 4096 个样本计算一次统计与频谱特征,还没有把完整频谱曲线传给电脑,数据路径是:ADXL382 以 16 kSPS 采样 → SPI DMA 每次读取 32 组三轴数据 → 原始数据组成 0x01 帧经 UART2 以 3 Mbaud 发给 C3;同一批样本也送进 DSP 双缓冲。

    4096 点对应 256 ms 的数据,频率分辨率是 16000 / 4096 = 3.90625 Hz/bin


    32655 对 X、Y、Z 各轴分别计算:
    时域部分,先求均值、再去均值后(去直流)的 AC RMS、AC 绝对峰值、峰峰值、峰值因子(峰值/RMS)、峭度;这些数值目前以传感器原始 LSB 为单位,尚未统一转换成 g。
    频域部分是先去均值并乘 Hann 窗,再用 CMSIS-DSP 做 4096 点实数 FFT;只搜索约 0–5 kHz,输出每轴的主峰频率、主峰幅值,以及五个频带的 RMS;主峰频率还使用相邻 bin 做抛物线插值;频域幅值按代码中的 0.0005 g/LSB 换算。
    计算时用两组 4096 × 3 的 int16 缓冲交替填充;每读取一个 32 样本包,只推进一个 DSP 步骤;均值和统计每步处理 512 点,功率谱每步处理 256 个 bin,FFT 则是其中单独的一步。

    使用SWD评估DSP计算性能

    调试的时候以 build/SPI.elf 的符号地址解释 RAM 计数器;使用板载 DAPLink CMSIS-DAP,经 OpenOCD SWD 在 CPU 运行时读取;未暂停、复位或重刷 MCU。
    本轮结果(20.1441 秒):

    指标

    观察值

    采集样本增量

    322,688

    平均采集速率

    16,023.7 SPS

    DSP 块增量

    79

    DSP 完成速率

    3.922 块/s

    理论块速率(16,000 / 4,096)

    3.906 块/s

    特征帧增量

    158(每块 2 帧)

    UART 已完成帧增量

    10,246

    g_missed_deadlines 增量

    0

    g_fifo_not_ready 增量

    41

    g_dsp_max_ticks 终值

    61(开机以来最大;非本窗口最大)

    窗口内 DSP 块数与采样量一致(322,688 / 4,096 ≈ 78.78),没有观察到 DSP 跟不上采集的迹象;特征帧数也恰好是 DSP 块数的两倍。g_fifo_not_ready 是定时轮询时 FIFO 尚未凑够 96 个字的次数,不等同于丢样;SWD 采样本身有轻微扰动,采样间隔和速率均按主机单调时钟计算;约 0.5 秒的块速率会因整数块量化而呈阶梯。

    使用DWT更加精细的评估DSP性能

    给 DSP 各计算阶段加 DWT 周期计数,记录每阶段累计周期、调用次数和最大单次周期,再通过 SWD 读取并用 Python 出图;这样能把 FFT 与均值、时域统计、频带计算分开评估
     Window 20.146s, core 100.0 MHz, counter overhead 16 cycles
     Sampling 16022.1 SPS; 79 DSP blocks; 158 features; missed +0; FIFO wait +41
     setup           237 calls mean     0.57 us max     0.77 us core time   0.00%
     mean           1896 calls mean   53.52 us max   54.05 us core time   0.50%
     moments_hann   1896 calls mean   240.94 us max   244.35 us core time   2.27%
     fft_4096         237 calls mean 3793.50 us max 3793.77 us core time   4.46%
     power_bands     1185 calls mean   121.38 us max   121.69 us core time   0.71%
     finalize         237 calls mean     4.79 us max     4.86 us core time   0.01%
     Total measured DSP core time: 7.95%
    三轴每个 4096 点块的 DSP 总测得约 20.28 ms,其中 FFT 约 11.38 ms;折合测试窗口约 7.95% 的核心时钟时间。采样仍为约 16.02 kSPS、漏期增量 0。

    阶段

    调用次数

    单次平均

    开机以来单次最大

    三轴每块合计

    窗口核心时间占比

    初始化步骤

    237

    0.57 µs

    0.77 µs

    <0.01 ms

    <0.01%

    均值,每轴 8×512 点

    1,896

    53.52 µs

    54.05 µs

    1.28 ms

    0.50%

    中心矩、峰值、Hann 窗,每轴 8×512 点

    1,896

    240.94 µs

    244.35 µs

    5.78 ms

    2.27%

    4096 点实数 FFT,每轴一次

    237

    3,793.50 µs

    3,793.77 µs

    11.38 ms

    4.46%

    功率谱/频带,每轴 5 个分片

    1,185

    121.38 µs

    121.69 µs

    1.82 ms

    0.71%

    峰值插值及频带结果

    237

    4.79 µs

    4.86 µs

    0.01 ms

    0.01%

    合计




    20.28 ms/块

    7.95%

    同一窗口原始数据 322,784 样本,实际采集率 16,022.1 SPS;DSP 完成 79 块,特征帧 158,漏期增量 0,FIFO 暂未凑齐增量 41;三轴 4096 点块的理想周期为 256 ms;平均 DSP 耗时为周期的约 7.9%。

    实时性重点不只是平均负载:单次 FFT 约 3.79 ms,超过 32 XYZ 包对应的 2 ms 时间,所以采集会暂时靠 ADXL382 FIFO 缓冲,之后追赶。

    频谱数据解析

    从 C3 USB 链路抓一帧最新的 0x05 频谱特征,按三轴主峰和五个频带分解:
     crc 59
     block 4227 first_seq 17313792 bin_hz 3.90625
     X peak_hz 268.3132019042969 peak_g 0.0009868545457720757 bands_g_rms (0.0013011862756684422, 0.0018773552728816867, 0.0019023217028006911, 0.0027426122687757015, 0.004297043662518263)
     Y peak_hz 770.075439453125 peak_g 0.001155573409050703 bands_g_rms (0.001327782985754311, 0.0020827981643378735, 0.0023854034952819347, 0.002570707118138671, 0.004741341806948185)
     Z peak_hz 268.40802001953125 peak_g 0.0017939069075509906 bands_g_rms (0.001608308870345354, 0.002986389212310314, 0.0031467906665056944, 0.003475544275715947, 0.005687463562935591)
     0 (4275, 17510400, 3.90625, 4266.13525390625, 3763.20166015625, 252.0399627685547, 0.0012536837020888925, 0.0013949715066701174, 0.0024219087790697813, 0.0010756059782579541, 0.0017819652566686273, 0.002098222030326724, 0.002652243245393038, 0.0045760259963572025, 0.0012079484295099974, 0.0019669109024107456, 0.0024646390229463577, 0.0029253715183585882, 0.004754867870360613, 0.0013006648514419794, 0.0034214667975902557, 0.0032509721349924803, 0.0036392351612448692, 0.006086269393563271)
    这些数据是 MAX32655 从每个 4096 点数据块中提取的“频谱特征摘要”,有效频谱帧里面是:

    DSP 块:4275

    首样本序号:17,510,400

    FFT 点数:4096

    采样率:16 kSPS

    频率分辨率:3.90625 Hz

    分析范围:0–5 kHz

    轴

    主峰频率

    主峰幅值

    X

    4266.14 Hz

    0.001254 g

    Y

    3763.20 Hz

    0.001395 g

    Z

    252.04 Hz

    0.002422 g

    五频带 RMS:

    频带

    X

    Y

    Z

    0–200 Hz

    0.001076 g

    0.001208 g

    0.001301 g

    200–500 Hz

    0.001782 g

    0.001967 g

    0.003421 g

    500–1000 Hz

    0.002098 g

    0.002465 g

    0.003251 g

    1–2 kHz

    0.002652 g

    0.002925 g

    0.003639 g

    2–5 kHz

    0.004576 g

    0.004755 g

    0.006086 g

    每个频谱帧是固定 V2 协议中的 0x05 类型:
     block_sequence
     first_sample_sequence
     bin_width_hz
     peak_frequency_hz[3]
     peak_amplitude_g[3]
     band_rms_g[3][5]
    有效负载 96 bytes,整个线上帧仍是固定 224 bytes;如果想在电脑上画传统的完整频谱曲线,需要把 0–5 kHz 的 1280 个 bin 分片发送。

    为什么不能算出 1 Hz 的分辨率?

    项目的一点核心数学矛盾是 FFT 点数和 1 Hz 分辨率,DFT/FFT 的频率间隔:
    Δf = f_s/N
    现在:
    f_s = 16,000 Hz
    于是:

    FFT 点数 N

    Δf

    数据时间长度

    1024

    15.625 Hz

    64 ms

    2048

    7.8125 Hz

    128 ms

    4096

    3.90625 Hz

    256 ms

    8192

    1.953125 Hz

    512 ms

    16384

    0.9765625 Hz

    1.024 s

    所以:
    N = 4096
    时只能得到:
    Δf = 3.90625 Hz
    达不到 1 Hz,所以在算法里面频域相关的内容以这个为准;emmmm,其实也不是算不出,而是当前 32655 固件只用 4096 点做 FFT;在 16 kSPS 下,频率 bin 间距是 16000 ÷ 4096 = 3.90625 Hz,这一块数据只覆盖 256 ms。
    要达到约 1 Hz 的 bin 间距,需要观察约 1 秒:16,000 点 DFT 的理论间距 1 Hz;16,384 点 FFT 间距约 0.977 Hz,数据时长 1.024 秒。

    当前固件之所以没直接改成 16,384 点,主要是 32655 的 SRAM:现有三轴双缓冲和浮点 FFT 缓冲已占用较多内存,当前固件 SRAM 占用约 80%;把整套缓冲扩大四倍会超过板上 128 KiB SRAM。FFT 后补零可以让曲线更平滑、峰值读数更细,但不能凭空获得 1 Hz 的真实分辨能力。

    现有链路仍持续把 16 kSPS 三轴原始数据传到电脑,所以最直接的做法是:32655 保持当前实时特征计算,电脑端累积至少 16,384 点后做约 1 Hz 频谱,不必改采样驱动或传输协议。

    但是还要区分“bin 间距约 1 Hz”和“可靠分开相距 1 Hz 的两个频率”:后者受 Hann 窗泄漏影响,通常需要更长的观测时间。

    简单来说就是这个采样率下,需要的内存比较多,现在 MCU 满了,但是可以到电脑端处理。

    实测结果

    因为附近没找到工业的场景,在电梯里面做了测试:

    上下楼来回测试,可以看到停顿过程:

    还有骑电动车通过颠簸路段的测试:

    上位机

    image-20260920204156542
    (看视频)

    后记

    感谢硬禾和 ADI 举办的活动,可以体验到一流公司的一流产品,活动中也学到很多(下次还来~),另外也感谢俺帅气B 人的设计师,外壳和 CNC 件均是他设计的,包括宣传视频,这里摇个花手。
附件下载
SPI_副本.zip
主控代码
TFT-32655.zip
ESP32-C3+屏幕
FmiVtnnY5cMQhpVnS-5azHMVk4dU
团队介绍
专业信号链设计师为您服务~
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号