☰
ADXL355高精度加速度计例程详解:STM32 SPI驱动与寄存器配置
2026/10/22 5:31:15 网站建设 项目流程

简介:本资源是一套面向嵌入式开发初学者与STM32工程师的ADXL355三轴加速度传感器实战例程,聚焦低功耗物联网、可穿戴设备及运动检测等典型应用场景。资源完整覆盖SPI通信驱动、传感器寄存器配置、原始数据读取与协议解析三大核心环节,帮助开发者快速掌握高精度MEMS传感器在ARM Cortex-M平台上的集成方法。压缩包共5个文件(3个C源码 + 2个头文件),总大小仅5KB,结构精炼:SPI.c实现STM32主控SPI底层时序适配;ADXL355.c封装初始化、量程设置与加速度数据解码逻辑;Communication.c提供CRC校验与数据包解析保障通信可靠性;配套头文件定义寄存器地址、配置结构体及函数接口。已有1764人学习下载,代码注释清晰、模块职责分明,可直接移植至主流STM32系列(如F1/F4/H7),亦可作为传感器驱动开发范式参考,支撑后续滤波算法、姿态解算或边缘触发逻辑的扩展开发。

1. 为什么大家都在找ADXL355的例程和源码

1.1 ADXL355是什么,为什么值得专门写一篇

打开搜索引擎搜ADXL355,出来的结果大多都是英文数据手册,或者某个论坛里零散的代码片段,真正能把“寄存器配置 + 源码讲解 + 实测踩坑”串起来的中文资料其实不多。ADXL355是ADI的一颗三轴数字MEMS加速度计,定位和大部分同学熟悉的MPU6050、ADXL345完全不同。它主打的是超低噪声、超低温度漂移、低功耗,配合20位ADC和数字滤波,适合做地震监测、结构健康监测、倾斜测量、平台稳定这类需要观察微弱低频信号的场景。

我最早接触这颗芯片是在一个桥梁振动测试的项目里,当时测的是0.1Hz到10Hz的微振动,用MPU6050完全压不住本底噪声,换ADXL355之后数据的可读性一下子出来了。从那之后,凡是需要“安静的测量”的活儿,我都优先考虑它。这篇文章就是把我实际用过的例程、寄存器配置和踩过的坑整理出来,给准备用这颗芯片的工程师省点时间。

1.2 它和MPU6050、ADXL345到底差在哪

很多第一次接触ADXL355的人会拿它和MPU6050比,其实这俩根本不是一类东西。MPU6050是16位ADC、自带DMP,主战场是手机姿态、平衡车、四轴飞控,优点是接口简单、算法库成熟,缺点是本底噪声大,站住不动时输出就能跳几十甚至上百个码,给它做低频微振动测量基本是灾难。ADXL345更偏消费级,13位有效分辨率,适合计步和倾角检测,精度和噪声都不够看。

ADXL355则是往“仪器级”方向走的:20位数字输出(实际有效位数大概在18位左右,低4位基本是0)、X/Y轴噪声密度典型值25μg/√Hz、Z轴45μg/√Hz,这个噪声水平比MPU6050低了差不多一个数量级。再加上内部集成了低通滤波器、32级FIFO、温度传感器,一颗芯片就能把数据采集链路的前端都包了。所以如果你正在做的项目需要测“很慢很细”的物理量,直接上ADXL355,别在消费级芯片上浪费时间。

1.3 这篇文章适合谁

如果你符合下面任何一种情况,这篇文章对你是有用的:第一次用ADXL355,想找一个能直接跑的例程;已经能跑例程,但搞不清楚量程、采样率、滤波这些寄存器怎么配;或者正在做倾角、振动、地震监测、大坝桥梁健康监测这类低速高精度测量,想看看这颗芯片实际表现如何。我会以STM32为主讲底层SPI读写和初始化流程,最后补充一下往TMS320F28388D这类DSP移植的思路,例程核心逻辑是平台无关的。

2. 硬件连接与寄存器层面的关键细节

2.1 SPI还是I2C:多数场景我建议用SPI

ADXL355支持SPI和I2C两种接口,I2C模式下地址由ADDR/SDO引脚决定,接地是0x1D,接高是0x53。I2C的好处是省引脚,如果你主控的I2C资源紧,可以这么接。但我在实践中更推荐SPI,原因有三:SPI速率上限比I2C高很多,ADXL355的SPI最高可以跑到10MHz(实际测试中不少人跑到更高也能工作,但按手册来最稳)。如果你的ODR设到500Hz以上,I2C的400kHz标准模式虽然也够用,但SPI留给你的余量要大得多。第二个原因是SPI读多字节时是一次性连续读出来的,时序干净,不容易被I2C的地址翻转搞乱。第三个原因是调试方便,逻辑分析仪上看SPI的波形比I2C直观太多了,出问题好定位。

接线上,SPI四根线:CS、SCLK、MOSI、MISO。注意ADXL355的MISO和ADDR/SDO是同一个引脚,接SPI时它自动做数据输出,不需要额外处理。另外这颗芯片的VDD和VDDIO可以分开供电,I/O电平跟着VDDIO走,如果你的MCU是3.3V,VDDIO接3.3V,如果MCU是1.8V,VDDIO接1.8V就行,比较灵活。

2.2 量程、采样率、滤波:开局先把这三个想明白

ADXL355的量程寄存器是0x1B,可选±2.048g、±4.096g、±8.192g三档,默认是±2.048g。虽然它有±8g的档位,但如果你做倾角或振动测量,我建议固定用±2.048g,因为小量程下分辨率最高,噪声表现最好。只有当你确定测量物体会有超过2g的冲击时,才需要去切换大档。量程的切换会直接影响后面g值换算系数,这个一定要注意。

采样率寄存器是0x1C,ADXL355的ODR上限是4000Hz,往下按2的幂次递减。实际上我们的低频测量很少需要很高ODR,桥梁振动常见的频率范围也就是0.1到20Hz,把ODR配到62.5Hz或125Hz已经非常富裕。这里给一个对应关系,SAMPLING寄存器写0x06大约对应62.5Hz,写0x05大约对应125Hz,具体还是要对照数据手册的表格确认。

滤波寄存器是0x1D,内部低通滤波器的配置就在这。它的作用是在数字域上进一步限制带宽,帮助抑制带外噪声。用的时候记住一个原则:ODR越低,LPF可选的截止频率档位越少;LPF截止频率不要设得太高,否则高频噪声会通过混叠折回到你关心的低频段。我通常的做法是先把ODR定下来,再选一个截止频率在ODR/8到ODR/4附近的LPF档位。

2.3 上电后的第一步:把ID读出来

不管写什么外设驱动,我习惯先把器件ID读出来。ADXL355的寄存器0x00是DEVID_AD,读出来应该是0xAD,寄存器0x01是DEVID_MST,应该读0x1D,寄存器0x02是PARTID,应该读0xED。如果这三项都对,说明SPI/I2C链路通、芯片没虚焊、供电正常,接下来调数据才是有意义的。

我遇到过不止一次“数据全为0”的情况,最后查到是芯片压根没跑起来,因为代码里没做ID校验,程序也没报错,就在那空转。第一步先读ID,能帮你把“通信问题”和“业务逻辑问题”快速切开。

3. ADXL355例程源码逐段拆解

3.1 底层SPI读写函数怎么写

下面这段是STM32 HAL库环境下最基础的SPI读写封装。ADXL355的SPI读操作是:先发一个命令字节,最高位是1表示读,低7位是寄存器地址,然后继续发SCLK把数据读回来。写操作是命令字节最高位为0,后面跟一个数据字节。

#define ADXL355_CS_LOW() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET) #define ADXL355_CS_HIGH() HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET) static void spi_tx_byte(uint8_t byte) { HAL_SPI_Transmit(&hspi1, &byte, 1, 10); } static uint8_t spi_rx_byte(void) { uint8_t rx; HAL_SPI_Receive(&hspi1, &rx, 1, 10); return rx; } uint8_t adxl355_read_reg(uint8_t reg) { uint8_t val; ADXL355_CS_LOW(); spi_tx_byte(0x80 | (reg & 0x7F)); val = spi_rx_byte(); ADXL355_CS_HIGH(); return val; } void adxl355_write_reg(uint8_t reg, uint8_t value) { ADXL355_CS_LOW(); spi_tx_byte(reg & 0x7F); spi_tx_byte(value); ADXL355_CS_HIGH(); }

这里有个小细节:读操作时,发送完命令字节后,接收函数会提供SCLK时钟把数据移出来,所以逻辑上是“先写命令,再读数据”,中间不需要额外的延时。如果你的MCU平台比较特殊,比如用了软件模拟SPI,注意在命令发完之后让时钟继续跑几个周期,数据才会出现在MISO上。

3.2 初始化流程:重复做三件事,省一小时调试时间

初始化代码不长,但顺序很重要。第一步读ID校验,第二步设置量程,第三步设置采样率,第四步确认芯片是否进入测量模式。ADXL355上电后默认就处于测量模式,所以POWER_CTL寄存器(0x28)一般不用动,但为了稳妥,我还是会把0x28写成0x00,确保芯片是在正常测量状态而不是待机。

uint8_t adxl355_init(void) { uint8_t id_ad, id_mst, part_id; id_ad = adxl355_read_reg(0x00); id_mst = adxl355_read_reg(0x01); part_id = adxl355_read_reg(0x02); if (id_ad != 0xAD || id_mst != 0x1D || part_id != 0xED) { return 1; // 读取失败 } // 量程:±2.048g adxl355_write_reg(0x1B, 0x01); // 采样率:约62.5Hz adxl355_write_reg(0x1C, 0x06); // 滤波:用一档合适的低通带宽,具体看数据手册表格 adxl355_write_reg(0x1D, 0x01); return 0; }

实际项目里,量程和采样率往往需要做成可配置的,而不是写死。比如做倾角测量,ODR用31.25Hz足够;做地震波监测,可能要用到250Hz甚至更高。我的建议是先在头文件里定义三个宏,把量程、采样率、滤波档位都定义出来,后面需要改参数时只动一个地方。

3.3 读取三轴加速度并换算成g值

读取三轴数据时,X轴起始寄存器是0x08,寄存器排列是XDATA3、XDATA2、XDATA1,接着是YDATA3、YDATA2、YDATA1,最后是ZDATA3、ZDATA2、ZDATA1。所以一次性连续读9个字节,就能把三轴原始数据全拿回来。

void adxl355_read_accel(float *x, float *y, float *z) { uint8_t buf[9]; int32_t raw_x, raw_y, raw_z; // 从寄存器0x08连续读9字节:X/Y/Z各3字节 ADXL355_CS_LOW(); spi_tx_byte(0x80 | 0x08); for (int i = 0; i < 9; i++) { buf[i] = spi_rx_byte(); } ADXL355_CS_HIGH(); // 把3字节合并成24位整数,再右移4位得到20位有效数据 raw_x = ((int32_t)buf[0] << 16) | ((int32_t)buf[1] << 8) | buf[2]; raw_y = ((int32_t)buf[3] << 16) | ((int32_t)buf[4] << 8) | buf[5]; raw_z = ((int32_t)buf[6] << 16) | ((int32_t)buf[7] << 8) | buf[8]; raw_x >>= 4; raw_y >>= 4; raw_z >>= 4; // 20位补码符号扩展 if (raw_x & 0x80000) raw_x |= 0xFFF00000; if (raw_y & 0x80000) raw_y |= 0xFFF00000; if (raw_z & 0x80000) raw_z |= 0xFFF00000; // ±2.048g量程下,1g对应约256000个LSB(20位) *x = (float)raw_x / 256000.0f; *y = (float)raw_y / 256000.0f; *z = (float)raw_z / 256000.0f; }

这段代码里最容易翻车的地方是符号扩展。20位补码的数据范围是-524288到+524287,但直接放在int32_t里时,最高位并不是32位的符号位,所以必须手动扩展。如果忘了这一步,负方向的加速度会变成很大的正数,输出的g值完全没法看。另外,除数是256000,如果你量程切到了±4.096g或±8.192g,这个系数要跟着变,通常是128000和64000,具体以数据手册为准。

3.4 用FIFO和中断,避免在高ODR下丢数据

有些应用要把ODR开到1000Hz甚至4000Hz,比如做冲击信号采集。这个时候如果还用主循环轮询寄存器,稍微被其他任务打断一下,就可能丢样本。ADXL355内置了32级FIFO,可以把测量数据缓存起来,MCU抽空一次性读走,这是我在高频采集场景下的首选方案。

FIFO的配置也很简单:先把FIFO_CTL寄存器(0x2C)写成0x01使能FIFO,再通过INT_EN寄存器(0x1F)使能FIFO中断,配置INT1引脚输出。之后每来一个采样点,数据就会自动压进FIFO,FIFO里攒了多少个样本可以从FIFO_ENTRIES_L(0x05)和FIFO_ENTRIES_H(0x06)读出来。读FIFO数据时,连续读寄存器0x11,每读9个字节就是一组X/Y/Z三轴数据。

使用FIFO的一个好处是MCU可以不关心每个采样点的精确时刻,只需要在FIFO半满或者溢出之前把数据取走。FIFO溢出时STATUS寄存器里会有一个标志位,如果你的系统对数据完整性要求很高,可以在中断处理里把这个标志位记录下来,事后知道哪一段数据丢了。

4. 实测效果与避坑经验

4.1 我实测到的噪声和稳定性

把芯片固定在光学平台上,用±2.048g量程、ODR 62.5Hz、低通带宽大约15Hz左右去测静止状态下的输出,X/Y轴输出的峰峰值大概在0.2到0.4mg之间,Z轴稍差一些,大约0.5到0.8mg。换算成噪声密度,和我看手册标称的X/Y轴25μg/√Hz基本对得上。这个数据在MEMS加速度计里已经算非常漂亮了,用来测建筑振动、桥梁摆幅,完全够用。

温度漂移方面,ADXL355比普通加速度计好很多。我做过一次从25°C升温到60°C的简单测试,零偏变化大概在几毫g级别,具体数值和板子焊接应力关系很大。这里有一个很重要的心得:LFCSP封装焊接完以后,机械应力会让零偏发生明显偏移,所以量产板最好做一次零点校准,把焊接应力带来的固定偏差消掉。

4.2 常见问题排查速查表

现象可能原因检查方法
ID读出来是0x00或0xFFSPI接线错误、芯片未上电检查CS/MOSI/MISO/SCLK是否接反,用万用表量VDD
ID正确但三轴数据全为0POWER_CTL被写成待机、SPI时序问题确认0x28寄存器值是0x00,用逻辑分析仪抓波形
X/Y/Z某个轴数据明显不对焊接不良、PCB应力、量程配置不一致补焊后重新校准,检查换算系数
数据跳动大,噪声异常电源纹波、SPI速率过高、滤波档位过宽给VDD加100nF去耦电容,检查LPF配置
Z轴噪声明显大于X/Y正常现象,Z轴MEMS结构决定接受即可,无需修复

排查时我一般遵循一个顺序:先确认电源电压,再查SPI波形,最后查寄存器值。千万别一上来就去怀疑算法,因为ADXL355的SPI时序问题占了通信故障的一半以上。

4.3 从STM32移植到TMS320F28388D或其他平台

很多人搜ADXL355例程时会看到TMS320F28388D、ADUC7061、ESP32这些关键词,说明大家都在往不同平台移植。移植的核心思路很简单:底层的SPI收发函数换掉,上层初始化和数据处理逻辑基本不用动。以TMS320F28388D为例,它的MCU子系统里自带SPI模块,你只需要把spi_tx_byte和spi_rx_byte替换成基于SpiaRegs的发送接收函数就行。

C2000的SPI发送一般是这样:

void spia_tx_byte(uint16_t data) { while (SpiaRegs.SPISTS.bit.INT_FLAG == 0); // 等待发送完成 SpiaRegs.SPITXBUF = data; } uint16_t spia_rx_byte(void) { while (SpiaRegs.SPISTS.bit.INT_FLAG == 0); // 等待接收完成 return SpiaRegs.SPIRXBUF; }

注意C2000的SPI寄存器是16位宽的,命令字节和数据字节放在低8位即可,高

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询