STM32驱动ADXL345加速度计实现姿态角检测:从硬件连接到算法实现 简介本资源是一套基于STM32G4系列微控制器实现ADXL345三轴加速度传感器姿态解算的完整嵌入式工程面向嵌入式软硬件开发者、飞控与可穿戴设备初学者及高校电子类课程实践者解决静态/准静态场景下俯仰角与横滚角高精度实时计算问题。压缩包含432个文件22.83MB涵盖核心驱动C/H源码共156个、编译中间文件O/SU/CYCLO等约177个、构建配置Makefile、LD、IOC、MXProject等、HAL库适配文件及少量PDF设计说明与README文档结构清晰便于理解底层I2C通信、DMA数据采集、数字滤波与三角函数优化实现。已有183人学习下载。读者可直接部署运行获得含温度补偿、方差异常检测、FPU加速及二阶低通滤波的姿态解算完整链路代码已集成STM32G4xx HAL库关键组件如I2C、TIM、UART、RCC等具备低功耗5mA、高分辨率0.1°与快响应50ms特性适用于无人机飞控、工业倾角监测等实际场景。1. 项目概述从传感器数据到姿态角最近在做一个需要检测物体姿态的小项目核心需求是通过一个三轴加速度传感器来实时计算物体的俯仰角Pitch和横滚角Roll。我选用了ADI的ADXL345这款经典的数字加速度计主控则是大家再熟悉不过的STM32。这听起来像是一个很基础的嵌入式传感器应用但真动手做起来从I2C/SPI通信配置、数据读取滤波到最后的姿态解算公式推导与实现每一步都有不少细节需要注意稍不留神得到的角度数据就会跳得没法看。这个项目非常适合刚接触STM32和传感器驱动的朋友作为练手也适用于需要低成本姿态感应的场景比如平衡小车初期的姿态反馈、一些互动装置的角度检测或者穿戴设备的基本动作识别。它的核心价值在于你不需要昂贵的IMU惯性测量单元或复杂的融合算法仅用一颗几十块钱的加速度计就能获得相对稳定的静态或慢速动态下的姿态角信息。当然你得先搞清楚它的局限性加速度计无法感知绕垂直轴偏航角Yaw的旋转且在存在线性加速度比如突然移动时计算的角度会严重失真。但这并不妨碍它在许多场合大显身手。接下来我会把自己从硬件连接到软件实现的完整过程包括踩过的坑和总结的经验毫无保留地分享出来。我们会用到STM32的HAL库以I2C通信为例一步步驱动ADXL345并最终解算出可用的俯仰角和横滚角。2. 硬件选型与电路连接要点2.1 为什么选择ADXL345市面上三轴加速度计很多从模拟输出的到数字输出的价格和性能差异很大。我选择ADXL345主要基于以下几点考虑数字接口它直接通过I2C或SPI输出数字量省去了STM32外部ADC和模拟信号调理电路的麻烦精度和抗干扰能力也更好。量程可选支持±2g, ±4g, ±8g, ±16g多个量程通过软件配置。对于大多数姿态检测应用±2g或±4g就足够了分辨率更高。集成度高芯片内部集成了ADC和数字滤波器还能支持单拍/双击检测、自由落体检测等实用功能虽然本项目暂不用但为后续扩展留了可能。功耗与封装低功耗模式做得好且常见的LGA-14封装体积小便于集成。相比之下像MPU6050这类IMU虽然集成了陀螺仪功能更强但对于只需要静态角度或者对成本极其敏感的项目ADXL345是更纯粹、更经济的选择。2.2 关键电路连接与注意事项ADXL345与STM32的连接非常简单核心是电源和通信线路。以下是以I2C接口为例的接法STM32引脚ADXL345引脚说明3.3VVCC必须接3.3VADXL345的IO口电平与VCC相关接5V会损坏。GNDGND共地。PB6 (I2C1_SCL)SCLI2C时钟线需接上拉电阻通常4.7kΩ。PB7 (I2C1_SDA)SDAI2C数据线需接上拉电阻通常4.7kΩ。3.3V 或 GNDCS片选。接VCC高电平选择I2C模式接GND选择SPI模式。不接或接GNDSDO/ALT ADDRESSI2C地址选择引脚。接GND时地址为0x53接VCC时地址为0x1D。注意1上拉电阻必不可少。即使STM32的I2C引脚可以配置为内部上拉但驱动能力通常不足尤其是线缆稍长时。务必在SCL和SDA线上各接一个4.7kΩ到10kΩ的外部上拉电阻到3.3V这是通信稳定的基石。注意2电源去耦要到位。在ADXL345的VCC引脚附近一定要并联一个0.1μF的陶瓷电容到GND用于滤除高频噪声。如果电源质量一般还可以再并一个10μF的钽电容。传感器对电源噪声非常敏感糟糕的电源会导致数据跳动剧烈。注意3地址别搞错。根据SDO引脚的电平确定I2C设备地址写程序时对应不上怎么都读不出数据。我一开始就忘了接这个引脚悬空导致地址不确定调试了半天。3. STM32底层驱动开发详解3.1 CubeMX工程配置使用STM32CubeMX可以快速完成基础配置。关键步骤如下选择型号创建工程选择你使用的STM32型号如F103C8T6。配置I2C在Pinout Configuration标签页找到I2C1。将I2C1 Mode设置为I2C。将I2C Speed Mode设置为Standard Mode100kHz或Fast Mode400kHz。ADXL345最高支持400kHz初期调试可用100kHz以求稳定。此时PB6和PB7会自动配置为复用开漏模式Alternate Function Open Drain。务必检查GPIO设置确保上拉Pull-up已启用作为外部上拉电阻的补充。配置调试接口根据你的调试器ST-Link等在System Core-SYS中配置Debug为Serial Wire否则下载一次程序后可能无法再次连接。生成代码设置好工程路径和工具链MDK-ARM/V5生成代码。3.2 ADXL345驱动函数封装生成的工程基于HAL库我们需要自己编写ADXL345的驱动。核心函数包括初始化、写寄存器、读寄存器、读取加速度数据。首先定义设备地址和关键寄存器宏定义// adxl345.h #define ADXL345_I2C_ADDR_WRITE 0xA6 // (0x53 1) | 0 写地址 #define ADXL345_I2C_ADDR_READ 0xA7 // (0x53 1) | 1 读地址 // 重要寄存器地址 #define ADXL345_REG_DEVID 0x00 // 设备ID固定为0xE5 #define ADXL345_REG_POWER_CTL 0x2D // 电源控制 #define ADXL345_REG_DATA_FORMAT 0x31 // 数据格式 #define ADXL345_REG_DATAX0 0x32 // X轴数据低8位 #define ADXL345_REG_DATAX1 0x33 // X轴数据高8位 // ... 类似定义 DATAY0, DATAY1, DATAZ0, DATAZ1 #define ADXL345_REG_BW_RATE 0x2C // 数据速率与带宽然后实现基础的读写函数// adxl345.c #include adxl345.h #include i2c.h // CubeMX生成的I2C头文件 // 写一个寄存器 uint8_t ADXL345_WriteReg(uint8_t reg, uint8_t value) { uint8_t buf[2] {reg, value}; HAL_StatusTypeDef status HAL_I2C_Master_Transmit(hi2c1, ADXL345_I2C_ADDR_WRITE, buf, 2, HAL_MAX_DELAY); return (status HAL_OK) ? 0 : 1; } // 读多个连续寄存器 uint8_t ADXL345_ReadRegs(uint8_t reg, uint8_t *buf, uint16_t len) { // 先发送要读取的寄存器地址 HAL_StatusTypeDef status HAL_I2C_Master_Transmit(hi2c1, ADXL345_I2C_ADDR_WRITE, reg, 1, HAL_MAX_DELAY); if (status ! HAL_OK) return 1; // 然后读取数据 status HAL_I2C_Master_Receive(hi2c1, ADXL345_I2C_ADDR_READ, buf, len, HAL_MAX_DELAY); return (status HAL_OK) ? 0 : 1; }实操心得HAL_I2C的坑。HAL_I2C_Master_Transmit和HAL_I2C_Master_Receive在发送从机地址时HAL库内部会自动帮你左移一位7位地址模式。所以我们的宏定义ADXL345_I2C_ADDR_WRITE已经是左移一位后的值0x531 0xA6。这是新手最容易困惑的地方。另外务必检查hi2c1这个句柄它必须在CubeMX生成的i2c.c中正确初始化。3.3 传感器初始化与配置初始化函数需要配置传感器的工作模式、量程、输出数据速率等。uint8_t ADXL345_Init(void) { uint8_t devid 0; // 1. 读取设备ID验证通信是否正常 if(ADXL345_ReadRegs(ADXL345_REG_DEVID, devid, 1)) return 1; if(devid ! 0xE5) return 2; // ID不对可能接线或地址错误 // 2. 配置数据格式寄存器 (0x31) // Bit3: FULL_RES (1: 全分辨率模式量程由dataformat的D1D0决定分辨率随量程变) // Bit1-0: Range (00: ±2g, 01: ±4g, 10: ±8g, 11: ±16g) // 这里选择全分辨率±4g量程 if(ADXL345_WriteReg(ADXL345_REG_DATA_FORMAT, 0x09)) return 3; // 0b00001001 // 3. 配置带宽速率寄存器 (0x2C) // Bit3-0: Rate (1101: 800Hz, 1011: 200Hz, ... 详见手册) // 这里选择200Hz输出速率 if(ADXL345_WriteReg(ADXL345_REG_BW_RATE, 0x0B)) return 4; // 0b00001011 // 4. 配置电源控制寄存器 (0x2D)启动测量 // Bit3: Measure (1: 进入测量模式) // Bit2: Sleep // Bit1-0: Wakeup frequency if(ADXL345_WriteReg(ADXL345_REG_POWER_CTL, 0x08)) return 5; // 0b00001000 return 0; // 初始化成功 }关键参数解析量程Range±4g意味着传感器能测量的最大加速度为4倍重力加速度约39.2 m/s²。在静态时Z轴理论上会输出1g。选择量程需要权衡量程越大能承受的冲击越大但分辨率越低因为输出的数字量位数固定为13位。对于手势识别或慢速倾斜±2g能提供更高的角度分辨率。输出数据速率ODR这里设为200Hz即传感器每秒输出200组数据。更高的速率能捕捉更快的运动但也会增加数据量和噪声。需要根据应用场景和后续滤波需求选择。如果只是检测缓慢倾斜25Hz或50Hz也足够还能降低功耗。全分辨率模式FULL_RES启用此模式时分辨率每个LSB对应的g值会随量程自动调整以充分利用13位的输出。禁用时无论量程多少分辨率固定为4mg/LSB在10位模式下。通常建议开启。4. 数据读取、处理与滤波4.1 读取原始加速度值ADXL345的X, Y, Z轴数据各占两个寄存器低8位和高8位组成一个13位的补码整数最高位为符号位。typedef struct { int16_t x; int16_t y; int16_t z; } Accel_RawData_t; uint8_t ADXL345_ReadAccel(Accel_RawData_t *accel) { uint8_t buf[6]; if(ADXL345_ReadRegs(ADXL345_REG_DATAX0, buf, 6)) return 1; // 合并高低字节数据为13位左对齐右移3位得到16位有符号数 accel-x ((int16_t)((buf[1] 8) | buf[0])) 3; accel-y ((int16_t)((buf[3] 8) | buf[2])) 3; accel-z ((int16_t)((buf[5] 8) | buf[4])) 3; return 0; }注意数据对齐方式。ADXL345的13位数据在16位寄存器中是左对齐的。这意味着我们需要将读取的16位数右移3位才能得到正确的13位有符号数值。这是数据转换中最关键的一步弄错了会导致计算出的g值差8倍。4.2 将原始值转换为重力加速度g转换公式为加速度(g) (原始值 * 量程因子) / 分辨率对于±4g量程全分辨率模式下分辨率 4g / 2^13 4 / 8192 ≈ 0.000488 g/LSB。 但更常用的方法是直接使用数据手册给出的“比例因子”Scale Factor。对于±4g比例因子通常是 8mg/LSB因为13位左对齐等效于16位下的值。所以// 比例因子单位: g/LSB。对于±4g量程典型值为 0.0078 (8mg) #define ADXL345_SCALE_FACTOR 0.0078f void ConvertToGravity(Accel_RawData_t *raw, float *gx, float *gy, float *gz) { *gx raw-x * ADXL345_SCALE_FACTOR; *gy raw-y * ADXL345_SCALE_FACTOR; *gz raw-z * ADXL345_SCALE_FACTOR; }静态放置时理论上只有Z轴输出接近1g或-1g取决于方向X和Y轴接近0g。你可以通过这个来验证转换是否正确。4.3 必不可少的数字滤波原始加速度数据必然包含噪声直接用于角度计算会抖动严重。必须进行滤波。移动平均滤波是最简单有效的方法之一#define FILTER_WIN_SIZE 10 // 滤波窗口大小 float MovingAverageFilter(float new_sample, float *buffer, uint8_t *index) { static uint8_t count 0; buffer[(*index)] new_sample; if (*index FILTER_WIN_SIZE) *index 0; if (count FILTER_WIN_SIZE) count; float sum 0.0f; for (uint8_t i 0; i count; i) { sum buffer[i]; } return sum / count; } // 使用示例 float gx_buffer[FILTER_WIN_SIZE], gy_buffer[FILTER_WIN_SIZE], gz_buffer[FILTER_WIN_SIZE]; uint8_t gx_idx 0, gy_idx 0, gz_idx 0; float gx_filtered, gy_filtered, gz_filtered; // 在主循环中 Accel_RawData_t raw; float gx, gy, gz; ADXL345_ReadAccel(raw); ConvertToGravity(raw, gx, gy, gz); gx_filtered MovingAverageFilter(gx, gx_buffer, gx_idx); gy_filtered MovingAverageFilter(gy, gy_buffer, gy_idx); gz_filtered MovingAverageFilter(gz, gz_buffer, gz_idx);滤波窗口大小的选择窗口越大滤波效果越平滑但响应延迟也越大。对于200Hz的数据窗口设为10即50ms的窗口长度是一个不错的起点能在平滑度和实时性之间取得平衡。你可以根据实际抖动情况和系统对响应速度的要求进行调整。进阶技巧一阶低通滤波。移动平均滤波对所有历史数据等权重有时不够灵活。一阶低通滤波也称指数平滑滤波更常用公式为filtered a * new (1-a) * filtered_old。其中a是滤波系数0a1a越大对新数据响应越快但平滑度下降。它的优点是计算量小只需保存上一个滤波值。在STM32上实现非常高效。5. 俯仰角与横滚角计算原理与实现这是本项目的核心算法部分。我们假设传感器静止或匀速运动即只有重力加速度那么加速度计测得的矢量就是重力矢量在传感器坐标系下的投影。通过这个投影我们可以反推出传感器坐标系相对于水平面的倾斜角度。5.1 公式推导与理解定义传感器坐标系X轴向右Y轴向前Z轴向上根据数据手册定义。俯仰角Pitchθ绕Y轴旋转的角度。机头向上为正。横滚角Rollφ绕X轴旋转的角度。右翼向下为正。计算公式如下Pitch arctan( X / sqrt(Y^2 Z^2) ) Roll arctan( Y / sqrt(X^2 Z^2) )或者另一种更常见的写法注意分母和分子Pitch arctan( -X / sqrt(Y^2 Z^2) ) // 根据坐标系方向调整正负 Roll arctan( Y / Z )为什么是这两个公式我们可以从几何角度理解。当传感器水平放置时重力矢量完全落在Z轴上0, 0, 1g。当绕Y轴旋转改变Pitch时重力在X轴和Z轴上有分量在Y轴上无分量理想情况。arctan(X/Z)正好反映了这个旋转角度。但直接用arctan(X/Z)在Z接近0时即俯仰角接近90度会发散所以使用arctan(X / sqrt(Y^2Z^2))更鲁棒因为它始终用重力矢量的总投影长度做分母。同理可得Roll角的公式。正负号问题公式中的正负号取决于你定义的坐标系方向和角度正方向。需要根据实际安装方向和期望的角度输出进行调整。我采用的公式是pitch_rad atan2(-gx, sqrt(gy*gy gz*gz)); // 使用atan2函数范围(-π, π] roll_rad atan2(gy, gz); // 使用atan2函数范围(-π, π]atan2(y, x)是C数学库中的函数它根据y和x的符号自动确定角度所在象限返回[-π, π]范围内的弧度值比atan()更安全方便。5.2 代码实现与弧度转角度在STM32上我们需要包含数学库math.h并在工程设置中勾选“Use MicroLIB”或调整链接器设置以支持浮点运算如果使用硬件FPU则性能更佳。#include math.h #define RAD_TO_DEG (57.29577951308232f) // 180 / PI void CalculateAngles(float gx, float gy, float gz, float *pitch, float *roll) { // 安全处理防止sqrt参数为负数理论上不会但浮点误差可能导致极小负数 float denom1 sqrt(gy*gy gz*gz); float denom2 sqrt(gx*gx gz*gz); // 使用atan2计算弧度注意参数顺序和正负号 // 这个符号组合使得水平放置时pitch和roll都为0。 // 抬起X轴机头向上pitch为正。 // 抬起Y轴右侧向下roll为正。 float pitch_rad atan2(-gx, denom1); float roll_rad atan2(gy, gz); // 注意这里用gz不是denom2。这是常用简化在gz0时效果好。 // 转换为角度制 *pitch pitch_rad * RAD_TO_DEG; *roll roll_rad * RAD_TO_DEG; }重要提示atan2的参数顺序。atan2(y, x)计算的是点 (x, y) 与原点连线与正X轴的夹角。务必确认你的分子分母对应的是y和x。我上面的写法atan2(-gx, sqrt(gy*gygz*gz))是经过验证的能得到符合直觉的Pitch角。5.3 角度计算的局限性讨论1. 静态精度与噪声即使传感器静止由于噪声和零偏计算出的角度也会有轻微抖动。滤波能缓解但无法根除。ADXL345的零偏稳定性是重要指标。2. 动态失真这是加速度计用于姿态解算的最大缺陷。当传感器存在线性加速度时例如被拿在手里晃动测得的矢量不再是纯重力而是重力与运动加速度的矢量和。此时计算出的角度会完全错误。因此这种方法仅适用于静态或准静态运动非常缓慢的场景。3. 奇异点问题当俯仰角接近±90度时公式中的分母会很小导致计算出的横滚角误差急剧增大甚至无意义。这是所有基于加速度计的姿态解算的通病。4. 缺少偏航角Yaw加速度计无法感知绕重力方向Z轴的旋转。要获得完整的3D姿态必须结合陀螺仪和磁力计使用传感器融合算法如互补滤波、卡尔曼滤波。尽管有这些局限在四轴飞行器初始化校准、平衡小车角度变化较慢、倾斜开关等应用中仅用加速度计计算的角度已经非常有用。6. 系统集成与主程序逻辑将上述所有模块整合起来形成一个完整的、可周期性输出姿态角的系统。6.1 程序架构设计一个好的程序结构应该清晰、易于维护。我建议采用以下模块main.c系统初始化主循环调度。adxl345.c/.h传感器底层驱动初始化、读写、数据读取。filter.c/.h数字滤波算法实现。angle_calc.c/.h角度计算函数。uart.c/.h可选通过串口打印数据到PC端查看。主程序的逻辑可以基于定时器中断实现固定频率的数据采集、处理和输出。6.2 基于定时器的采样例程使用STM32的定时器产生一个固定中断例如100Hz在中断服务函数中设置一个标志位在主循环中查询该标志位并执行任务。这样能保证采样周期的稳定性。// 在定时器中断回调函数中如HAL_TIM_PeriodElapsedCallback void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2_Instance) { // 你的定时器 data_ready_flag 1; } } // 主循环 int main(void) { // HAL初始化、外设初始化 SystemInit(); ADXL345_Init(); UART_Init(); // 启动定时器配置为100Hz中断 HAL_TIM_Base_Start_IT(htim2); float pitch, roll; Accel_RawData_t raw; float gx, gy, gz; float gxf, gyf, gzf; while (1) { if (data_ready_flag) { data_ready_flag 0; // 1. 读取原始数据 if(ADXL345_ReadAccel(raw) 0) { // 2. 转换为g值 ConvertToGravity(raw, gx, gy, gz); // 3. 滤波 gxf LowPassFilter(gx, gx_prev, 0.1f); // 使用一阶低通滤波示例 gyf LowPassFilter(gy, gy_prev, 0.1f); gzf LowPassFilter(gz, gz_prev, 0.1f); // 4. 计算角度 CalculateAngles(gxf, gyf, gzf, pitch, roll); // 5. 输出例如通过串口 printf(Pitch: %.2f, Roll: %.2f\n, pitch, roll); } } // 其他低优先级任务... } }6.3 数据输出与可视化将角度数据通过串口打印到PC是最直接的调试方式。你可以使用串口助手查看或者使用更高级的工具如MATLAB、Python的matplotlib库甚至一些串口绘图软件如SerialPlot、Vofa来实时绘制角度曲线直观地观察滤波效果和系统响应。7. 调试技巧与常见问题排查在实际操作中你几乎一定会遇到各种问题。下面是我总结的一些常见坑点和排查思路。7.1 通信失败读不到数据或ID不对这是第一步也是最容易出问题的一步。检查硬件连接用万用表确认VCC是3.3VGND连通SCL/SDA线上拉电阻已接好CS引脚电平正确I2C模式接高。检查I2C地址用逻辑分析仪或示波器抓取I2C总线波形看主机发送的地址是否正确。也可以写一个简单的I2C扫描程序遍历所有地址看哪个有应答。检查STM32 I2C配置确认CubeMX中I2C的时钟配置正确不能超过APB1时钟GPIO模式是开漏上拉Alternate Function Open Drain, Pull-up。检查HAL库延时在HAL_I2C_Master_Transmit/Receive中使用HAL_MAX_DELAY可能导致死锁如果怀疑这里可以改用带超时的版本并检查HAL_GetTick()是否正常递增。7.2 数据值异常全零、不变、极大/极小全零或不变可能电源模式未开启。检查是否成功向POWER_CTL寄存器0x2D的Measure位写了1。数据极大/极小如固定为-4096或4095可能是数据对齐错误。务必确认在将两个8位数据组合成16位后进行了右移3位的操作。这是ADXL345 13位数据格式的要求。数据随机跳动剧烈首先检查电源噪声和地线。确保传感器VCC引脚有0.1μF电容紧靠引脚放置。其次检查机械振动传感器本身是否固定牢固。最后启用并调整滤波参数。原始数据噪声大是正常的。7.3 计算出的角度不准或符号反了静态水平时角度不为零这是传感器零偏。可以在水平静止时连续读取多组数据求出X, Y, Z轴的输出平均值这就是零偏值。在后续计算中将每个轴的原始读数减去对应的零偏值再进行转换和计算。零偏校准是提高精度的关键一步。角度符号与预期相反检查传感器安装方向。ADXL345芯片上的坐标系是固定的。如果你的PCB安装方向旋转了90度或180度那么X, Y轴的定义就变了。调整CalculateAngles函数中gx,gy,gz的符号和顺序。例如如果传感器绕Z轴转了180度那么可能需要用-gx和-gy代入公式。角度在某个位置突变或跳变可能是遇到了计算公式的奇异点。尝试使用更鲁棒的计算公式例如同时使用加速度计和陀螺仪进行传感器融合在接近奇异点时更多地依赖陀螺仪。7.4 系统响应慢或卡顿滤波窗口过大减小移动平均的窗口大小或增大低通滤波的系数a。浮点计算负担重在低端STM32如Cortex-M0上进行大量float运算和sqrt、atan2函数调用会比较慢。可以考虑降低采样率。使用定点数运算替代浮点数。例如将g值放大1000倍用int32_t表示。使用查表法近似计算atan2。对于角度分辨率要求不高的场合可以用多项式拟合或查找预计算的arctan表。升级到带FPU的STM32系列如F4, F7。8. 项目优化与进阶思路当基础功能实现后可以考虑以下优化方向让项目更实用、更健壮。8.1 校准与自动零偏补偿上电后让设备在已知水平面上静止几秒钟在这段时间内采集多组数据如100次计算每个轴的平均值保存为零偏偏移量offset。之后所有读数都先减去这个偏移量。这能有效消除传感器个体差异和焊接应力带来的初始误差。typedef struct { int16_t x_offset; int16_t y_offset; int16_t z_offset; } Calib_Data_t; Calib_Data_t calib; void ADXL345_Calibrate(void) { Accel_RawData_t raw; int32_t sum_x 0, sum_y 0, sum_z 0; uint16_t samples 100; for(uint16_t i0; isamples; i) { ADXL345_ReadAccel(raw); sum_x raw.x; sum_y raw.y; sum_z raw.z; HAL_Delay(10); } calib.x_offset sum_x / samples; calib.y_offset sum_y / samples; calib.z_offset sum_z / samples; } // 读取数据时进行补偿 void ADXL345_ReadAccelCalibrated(Accel_RawData_t *accel) { ADXL345_ReadAccel(accel); accel-x - calib.x_offset; accel-y - calib.y_offset; accel-z - calib.z_offset; }8.2 倾斜开关与阈值检测很多应用不需要连续的角度值只需要判断是否超过某个角度阈值。你可以设置一个角度阈值如30度当计算出的俯仰角或横滚角绝对值超过该阈值时触发一个事件如点亮LED、发送信号。这可以避免复杂的浮点运算直接用加速度分量的比值进行比较效率更高。8.3 与陀螺仪融合进阶如前所述纯加速度计方案动态性能差。引入一个陀螺仪如MPU6050就集成了三轴陀螺仪和三轴加速度计通过互补滤波或卡尔曼滤波将加速度计提供的长期稳定角度低频信息与陀螺仪提供的短期精确角速度高频信息融合起来可以得到动态响应好、静态精度高的姿态角。这是制作自平衡机器人、四轴飞行器等产品的必经之路。虽然算法更复杂但网上有大量开源代码和教程可供参考。8.4 低功耗设计如果你的设备是电池供电需要关注功耗。ADXL345支持多种低功耗模式通过POWER_CTL寄存器降低输出数据速率ODR。进入STANDBY模式POWER_CTL的Measure位清0此时电流消耗可降至几十微安。利用其内置的运动/静止检测功能配置相关阈值和延时让传感器在静止时自动进入睡眠被震动唤醒。这样可以极大延长电池寿命。实现这个项目就像搭积木从最底层的通信调通到数据的获取、处理最后到上层算法的实现每一步都踩稳了整个系统就跑起来了。最享受的时刻莫过于看着串口助手屏幕上随着手腕转动而平稳变化的角度数值那一刻你会觉得所有的调试和折腾都是值得的。希望这份超详细的总结能帮你绕过我踩过的那些坑顺利点亮你的ADXL345让它成为你项目里感知世界姿态的“眼睛”。本文还有配套的精品资源点击获取