本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:ADXL345是Analog Devices推出的高性能三轴数字加速度传感器,广泛应用于物联网、嵌入式系统和消费电子中的运动、倾斜与冲击检测。本SPI通信程序实现了微控制器与ADXL345之间的高效数据交互,涵盖SPI接口初始化、传感器配置、中断设置、加速度数据读取与解析等关键流程。通过封装模块化函数,提升开发效率,并确保数据采集的准确性与系统稳定性,适用于各类基于加速度感知的智能应用开发。
ADXL345

1. ADXL345加速度传感器功能与应用场景介绍

ADXL345核心功能特性解析

ADXL345是一款由Analog Devices推出的高性能、低功耗三轴数字加速度传感器,采用MEMS(微机电系统)技术实现对X、Y、Z三个方向加速度的高精度检测。其测量范围可配置为±2g、±4g、±8g和±16g,分辨率达13位(在±16g量程下仍保持10位有效分辨率),输出数据通过I²C或SPI接口以数字形式传输,避免了模拟信号易受干扰的问题。

该传感器内置多种工作模式,包括正常测量模式、待机模式以及低功耗监听模式(如运动唤醒、自由落体检测),支持中断驱动机制,能够显著降低主机MCU的轮询负担,特别适用于电池供电设备中的节能设计。

典型行业应用案例分析

在消费电子领域,ADXL345广泛用于智能手机、平板电脑中的屏幕自动旋转与步态识别;在可穿戴设备中,如智能手环,其高灵敏度和低噪声特性为计步算法提供了可靠的数据基础。工业场景中,常被集成于振动监测系统,用于早期故障预警;无人机和机器人控制系统则利用其姿态感知能力实现稳定飞行与平衡调节。此外,在结构健康监测(SHM)系统中,ADXL345可用于捕捉桥梁、建筑等基础设施的微小震动变化,具备良好的环境适应性和长期稳定性。

本章内容为后续SPI通信配置与数据处理奠定应用背景基础。

2. SPI通信协议原理及ADXL345接口配置

在嵌入式系统中,传感器与微控制器之间的高效、可靠通信是实现数据采集和控制功能的关键。ADXL345作为一款广泛应用的三轴数字加速度传感器,支持I²C和SPI两种通信接口。其中, SPI(Serial Peripheral Interface) 因其高速传输能力、全双工特性以及灵活的时钟控制机制,在对实时性要求较高的应用场景中表现出显著优势。本章将深入剖析SPI通信协议的核心原理,并结合ADXL345的具体硬件特性和寄存器结构,详细阐述其SPI模式下的接口配置方法与实际操作流程。

SPI是一种同步串行通信总线标准,最初由Motorola公司提出,广泛用于短距离、板级设备间的主从式通信。它不需要复杂的握手信号或地址编码,通过简单的四线制即可完成高速数据交换。ADXL345支持标准的四线SPI接口,工作于从机模式,能够与各类MCU(如STM32、ESP32、AVR等)无缝对接。理解SPI的工作机制不仅是正确驱动ADXL345的前提,更是构建稳定嵌入式传感系统的基石。

2.1 SPI通信协议基本架构

SPI协议采用主从架构进行通信,通常由一个主设备(Master)和一个或多个从设备(Slave)组成。通信过程中,主设备负责生成时钟信号并发起数据传输,而从设备则根据片选信号被激活后响应数据请求。这种设计使得SPI具备高效率、低延迟的特点,适用于需要快速读写传感器数据的应用场景。

2.1.1 四线制信号定义:MISO、MOSI、SCK、SS详解

SPI通信依赖于四条核心信号线,分别承担不同的功能:

信号线 全称 方向 功能说明
SCK Serial Clock 主 → 从 同步时钟信号,由主设备产生,决定数据采样时刻
MOSI Master Out Slave In 主 → 从 主设备向从设备发送数据的通道
MISO Master In Slave Out 从 → 主 从设备向主设备返回数据的通道
SS/CS Slave Select / Chip Select 主 → 从 片选信号,低电平有效,用于选择特定从设备

这四个引脚构成了SPI通信的基础物理连接。当某个从设备的SS引脚被拉低时,该设备进入“使能”状态,开始监听SCK上的时钟边沿并响应数据传输;其他未被选中的设备保持高阻态,不参与通信。

以ADXL345为例,其SPI接口引脚如下:
- SDO :对应MISO(输出数据)
- SDA :在SPI模式下作为MOSI使用(输入数据)
- SCL :对应SCK(时钟输入)
- CS :片选输入(低电平有效)

⚠️ 注意:ADXL345的SDA引脚是复用引脚,在I²C模式下为双向数据线,在SPI模式下需配置为MOSI输入。因此,在硬件设计阶段必须确保正确设置ALT ADDRESS引脚电平(接地为SPI模式,接VDD_IO为I²C模式)。

数据传输过程示例(8位字节交换)
// 模拟一次SPI字节交换(伪代码)
uint8_t spi_transfer(uint8_t tx_data) {
    uint8_t rx_data = 0;
    for (int i = 7; i >= 0; i--) {
        // 主设备输出当前位到MOSI
        set_mosi((tx_data >> i) & 0x01);
        // 上升沿或下降沿触发时钟(取决于CPHA)
        toggle_sck();  // SCK上升沿
        // 从设备在SCK边沿输出一位到MISO
        uint8_t bit = read_miso();
        // 主设备在下一个边沿采样
        toggle_sck();  // SCK下降沿
        rx_data |= (bit << i);
    }
    return rx_data;
}

逻辑分析:
- 该函数模拟了一个典型的SPI字节传输过程。
- tx_data 是主机要发送的数据,逐位通过MOSI输出。
- rx_data 是从MISO线上接收的来自从机的数据。
- 每个时钟周期完成一位的发送与接收,共8位构成一个字节。
- 时钟极性和相位决定了是在上升沿还是下降沿采样数据。

参数说明:
- tx_data :待发送的8位数据,例如写寄存器命令。
- 返回值 rx_data :从设备返回的响应数据,如寄存器读取结果。
- set_mosi() read_miso() 为底层GPIO操作函数,具体实现依赖于MCU平台。

2.1.2 主从模式工作机制与全双工传输特性

SPI采用严格的主从控制机制,通信始终由主设备发起。主设备通过拉低目标从设备的SS信号来建立通信链路,随后在SCK的驱动下进行数据交换。

主从通信流程图(Mermaid格式)
sequenceDiagram
    participant MCU as MCU (Master)
    participant ADXL345 as ADXL345 (Slave)

    MCU->>ADXL345: CS = LOW (选中设备)
    MCU->>ADXL345: SCK 提供时钟
    loop 每个时钟周期
        MCU->>ADXL345: MOSI 发送1位
        ADXL345-->>MCU: MISO 返回1位
    end
    MCU->>ADXL345: CS = HIGH (结束通信)

此图清晰展示了SPI通信的时序关系: MOSI和MISO同时传输数据 ,体现了SPI的 全双工 特性。也就是说,在发送一个字节的同时,也能接收一个字节。这对于ADXL345这类寄存器型传感器尤为重要——主机发送读命令后,紧接着接收的是寄存器内容。

全双工通信的实际意义

在ADXL345的寄存器读取操作中,典型流程如下:
1. 主机发送读命令字(包含RA_READ标志和寄存器地址);
2. 从机在下一个SCK周期开始返回第一个数据字节;
3. 主机继续发送空时钟脉冲以获取后续数据。

由于SPI没有内置应答机制(如I²C的ACK),所有数据交换都依赖精确的时序控制和命令构造。

2.1.3 数据帧格式与时序同步机制

SPI本身不定义固定的数据帧格式,数据长度可变(常见为8位或16位),帧边界由SS信号的高低变化界定。一个完整的SPI事务包括以下阶段:

  1. 片选激活 :主设备将目标从设备的SS拉低;
  2. 数据传输 :在SCK驱动下,主从双方逐位交换数据;
  3. 片选释放 :主设备将SS拉高,结束本次通信。
ADXL345 SPI命令帧结构

ADXL345使用特定的命令字格式来区分读/写操作和是否启用地址自动递增:

Bit7 Bit6 Bit5~Bit1 Bit0
R/W A/A Register Address Don’t Care
  • R/W :0表示写操作(Write),1表示读操作(Read)
  • A/A(Auto-Increment) :1表示启用地址自动递增,0表示单地址访问
  • Register Address :指定目标寄存器地址(0x00 ~ 0x3F)

例如,若要连续读取DATAX0(0x32)、DATAX1(0x33)、DATAY0(0x34)……共6字节数据,应构造命令字:

uint8_t cmd = 0xC0 | 0x32;  // 0xC0 = 1100_0000 -> Read + Auto-Increment
时序同步关键点
  • 所有数据在SCK的 上升沿或下降沿 被采样,具体取决于CPOL和CPHA设置;
  • ADXL345要求SCK空闲状态为高电平(CPOL=1),且数据在SCK下降沿采样(CPHA=1),即 SPI Mode 3
  • 每次传输至少包含一个命令字节,之后才是数据字节;
  • 对于多字节读取,主机必须持续提供时钟直到所有数据接收完毕。

2.2 ADXL345的SPI通信接入方式

ADXL345支持标准的四线SPI接口,适用于大多数现代MCU的硬件SPI外设。正确的硬件连接与逻辑配置是确保通信成功的基础。

2.2.1 硬件连接拓扑设计(MCU与传感器引脚对接)

以下是典型的MCU(如STM32)与ADXL345的SPI连接方式:

MCU 引脚 连接到 ADXL345 引脚 功能说明
PA5 (SPI1_SCK) SCL 时钟输入
PA6 (SPI1_MISO) SDO 数据输出(传感器→MCU)
PA7 (SPI1_MOSI) SDA 数据输入(MCU→传感器)
PA4 (GPIO) CS 片选控制(软件管理)

📌 提示:虽然SPI硬件模块可自动处理SCK、MOSI、MISO,但 CS引脚通常由软件控制GPIO实现 ,以便精确控制通信起止时机。

电源与去耦建议
  • VDD 和 VDD_IO 接 3.3V 或 5V(注意电平兼容性);
  • GND 良好接地;
  • 在VDD引脚附近添加0.1μF陶瓷电容,抑制高频噪声;
  • 若走线较长,建议增加1kΩ串联电阻以减少反射。

2.2.2 片选信号(SS)的有效电平与时序要求

ADXL345的CS引脚为低电平有效,即只有当CS ≤ 0.3×VDD_IO时,芯片才进入SPI通信模式。若CS保持高电平,则SDA/SCL引脚处于高阻态,不响应任何操作。

关键时序参数(依据ADI官方数据手册)
参数 符号 最小值 典型值 单位 条件
CS下降沿到第一个SCK上升沿时间 t_CSDS 0 - μs 必须满足建立时间
SCK最后一个上升沿到CS上升沿时间 t_SHSL 0.5 - μs 防止误采样
CS高电平最小宽度(两次通信间隔) t_CSSH 0.1 - μs 建议留出足够恢复时间
实际应用中的CS控制代码(STM32 HAL库)
void adxl345_select() {
    HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);  // CS = LOW
    __NOP(); __NOP();  // 插入微小延时确保稳定
}

void adxl345_deselect() {
    HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);     // CS = HIGH
    osDelay(1);  // 可选:短暂延时保证通信结束
}

逻辑分析:
- adxl345_select() 将CS置低,开启通信窗口;
- 使用 __NOP() 插入两个CPU周期延时,防止因指令流水导致时序错乱;
- adxl345_deselect() 结束通信,释放总线;
- osDelay(1) 在RTOS环境中避免任务抢占干扰。

2.2.3 读写操作命令字构造与地址自动递增机制

ADXL345通过命令字识别主机意图。准确构造命令字是实现寄存器读写的前提。

写操作示例:配置POWER_CTL寄存器启动测量
uint8_t tx_buffer[2];
tx_buffer[0] = 0x00 | 0x2D;           // Write to POWER_CTL (0x2D), no auto-increment
tx_buffer[1] = 0x08;                  // Enable measurement mode (Measure = 1)

adxl345_select();
HAL_SPI_Transmit(&hspi1, tx_buffer, 2, 100);
adxl345_deselect();
读操作示例:读取DEVID寄存器验证器件身份
uint8_t cmd = 0x80 | 0x00;            // Read + Auto-Increment disabled
uint8_t dev_id;

adxl345_select();
HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);      // Send command
HAL_SPI_Receive(&hspi1, &dev_id, 1, 100);     // Receive response
adxl345_deselect();

if (dev_id == 0xE5) {
    printf("ADXL345 detected!\n");
}

参数说明:
- cmd :构造为 0x80 | reg_addr 表示读操作;
- HAL_SPI_Transmit() 发送命令;
- HAL_SPI_Receive() 获取返回数据;
- 超时设为100ms,防止死锁。

✅ 成功读取到 0xE5 表明通信链路正常,为后续初始化奠定基础。

2.3 SPI时钟参数配置理论分析

SPI通信的稳定性高度依赖于时钟配置的准确性。ADXL345对SCK的频率、极性和相位有明确要求,必须与主控MCU的SPI外设严格匹配。

2.3.1 时钟极性(CPOL)与时钟相位(CPHA)组合解析

SPI共有四种工作模式,由CPOL和CPHA决定:

模式 CPOL CPHA 数据采样边沿 适用设备
0 0 0 SCK上升沿 多数Flash
1 0 1 SCK下降沿 少数ADC
2 1 0 SCK下降沿 -
3 1 1 SCK上升沿 ADXL345

ADXL345规定:
- SCK空闲状态为高电平 → CPOL = 1
- 数据在SCK的 下降沿 采样 → CPHA = 1

因此必须配置为主设备运行于 SPI Mode 3

STM32CubeMX配置片段(HAL库)
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;    // CPOL = 1
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;         // CPHA = 1
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

2.3.2 ADXL345支持的SPI模式选择(模式3:CPOL=1, CPHA=1)

ADXL345仅支持SPI Mode 3(CPOL=1, CPHA=1)。若主控配置错误(如Mode 0),会导致数据错位甚至无法通信。

错误配置后果示例

假设主设备使用Mode 0(CPOL=0, CPHA=0):
- SCK空闲为低;
- 数据在上升沿采样;
- 但ADXL345期望在下降沿采样;
- 导致每个比特延迟半个周期,最终读出错误数据。

正确波形特征(示波器观测)
信号 波形特征
SCK 空闲高电平,下降沿采样
MOSI 数据在SCK下降前沿稳定
MISO ADXL345在下降沿改变输出

可通过逻辑分析仪抓取通信过程,验证是否符合预期。

2.3.3 最大传输速率限制与MCU外设匹配策略

ADXL345最大SCK频率为 5 MHz (VDD ≥ 2.5V)。这意味着主控SPI时钟不得超过此上限。

速率计算示例(基于72MHz APB2总线)
分频系数 实际波特率 是否可用
4 18 MHz ❌ 超限
8 9 MHz ❌ 超限
16 4.5 MHz ✅ 安全
32 2.25 MHz ✅ 可靠

推荐使用 16分频 ,获得4.5 MHz通信速率,在速度与稳定性之间取得平衡。

动态调整策略

在低功耗场景中,可进一步降低SPI速率至1 MHz,配合ADXL345的低功耗监听模式,延长电池寿命。

2.4 初始化流程中的关键步骤验证

成功的SPI通信不仅依赖硬件连接,还需通过一系列验证步骤确认器件状态和通信可靠性。

2.4.1 上电复位后状态确认与器件识别(读取DEVID寄存器)

ADXL345上电后会执行内部复位,所有寄存器恢复默认值。第一步应验证能否正确读取设备ID。

uint8_t read_register(uint8_t reg) {
    uint8_t cmd = 0x80 | reg;
    uint8_t data;
    adxl345_select();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi1, &data, 1, 100);
    adxl345_deselect();
    return data;
}

// 测试代码
uint8_t dev_id = read_register(0x00);
if (dev_id == 0xE5) {
    printf("Device ID verified.\n");
} else {
    printf("Invalid device ID: 0x%02X\n", dev_id);
}

扩展建议:
- 若读不到正确ID,检查CS、SCK、MISO连接;
- 使用示波器查看SCK是否有波形;
- 确认ALT ADDRESS引脚接地(SPI模式)。

2.4.2 寄存器默认值检查与通信链路连通性测试

进一步验证通信完整性,可读取多个关键寄存器的默认值:

寄存器 地址 默认值 说明
THRESH_TAP 0x1D 0x00 摇动检测阈值
OFSX/Y/Z 0x1E~0x20 0x00 零偏补偿
BW_RATE 0x2C 0x0A 输出速率=100Hz
POWER_CTL 0x2D 0x08 测量模式关闭
DATA_FORMAT 0x31 0x00 ±2g,10-bit

编写自检函数:

bool adxl345_self_test() {
    if (read_register(0x00) != 0xE5) return false;
    if (read_register(0x2D) != 0x08) return false;
    if (read_register(0x31) != 0x00) return false;
    return true;
}

该函数可用于系统启动时自动诊断硬件故障,提升产品鲁棒性。


综上所述,掌握SPI通信协议及其在ADXL345上的具体应用,是开发高性能传感器系统不可或缺的一环。从信号定义到时序配置,再到初始化验证,每一步都需要严谨对待。下一章将进一步探讨如何通过寄存器配置激活ADXL345的各项核心功能。

3. ADXL345核心功能配置与运行模式设置

ADXL345作为一款高度可编程的三轴数字加速度传感器,其灵活性不仅体现在硬件接口上,更关键的是其丰富的内部寄存器体系所支持的多种运行模式和功能组合。通过合理配置电源管理、数据输出速率、测量范围以及中断系统,开发者可以根据具体应用场景实现性能与功耗之间的最优平衡。尤其在电池供电或对实时性有要求的应用中,如可穿戴设备、远程监控终端和嵌入式运动检测系统,ADXL345的核心功能配置直接决定了系统的响应能力、精度表现与续航时间。本章将深入剖析ADXL345的各项核心功能寄存器及其协同工作机制,并结合典型应用案例,展示如何通过寄存器编程实现高效、智能的传感器控制。

3.1 电源管理与低功耗控制机制

ADXL345内置了完善的电源管理系统,允许用户根据实际需求灵活切换工作状态,从而在保证功能完整性的同时最大限度地降低能耗。这一特性对于物联网边缘节点、无线传感网络及便携式健康监测设备至关重要。电源管理主要依赖于 POWER_CTL 寄存器(地址 0x2D )进行控制,该寄存器集成了测量使能、睡眠模式、自动休眠与唤醒链路等功能位。

3.1.1 POWER_CTL寄存器字段解析(测量启停、睡眠模式切换)

POWER_CTL 寄存器是一个8位寄存器,各比特定义如下表所示:

Bit 名称 功能说明
7 LINK 启用链式模式,用于联动活动/非活动检测与休眠
6 AUTO_SLEEP 允许自动进入睡眠模式
5 MEASURE 启动测量模式(置1开启加速度采样)
4 SLEEP 进入睡眠模式(降低采样频率至0.1Hz或更低)
3:0 RESERVED 保留位,应保持为0

其中最关键的控制位是 MEASURE SLEEP 。只有当 MEASURE = 1 时,传感器才会开始采集加速度数据;若 MEASURE = 0 ,则器件处于完全关闭状态,不进行任何采样操作。而 SLEEP = 1 表示启用低功耗睡眠模式,在此模式下,采样频率由 BW_RATE 寄存器设定的最低档位决定(例如0.1Hz),显著减少电流消耗。

// 示例:通过SPI写入POWER_CTL寄存器以启动测量并禁用睡眠
uint8_t power_ctl_value = (1 << 5); // 设置 MEASURE=1, 其他位默认
spi_write_register(ADXL345_REG_POWER_CTL, &power_ctl_value, 1);

代码逻辑分析
- ADXL345_REG_POWER_CTL 是宏定义常量,值为 0x2D ,指向目标寄存器。
- (1 << 5) 将第5位置为1,即激活 MEASURE 位,其余位清零。
- 使用 spi_write_register() 函数执行SPI写操作,格式为 [SS低电平] + [写命令字] + [寄存器地址] + [数据] + [SS高电平]
- 参数说明:第二个参数是指向数据缓冲区的指针,第三个参数表示要写入的字节数。

该配置适用于需要持续采样的场景,如姿态跟踪或振动分析。若需进入低功耗监听模式,则应进一步设置 SLEEP LINK 位。

3.1.2 自动休眠与唤醒周期设定(Link位与Auto-Sleep功能)

ADXL345支持一种称为“链式模式”(Link Mode)的高级节能策略,通过 LINK AUTO_SLEEP 位配合使用,实现基于事件驱动的自动唤醒机制。在这种模式下,设备可以在检测到特定运动事件(如活动发生)后自动从睡眠状态唤醒,并在静止一段时间后再次进入睡眠。

其工作流程如下图所示(使用Mermaid绘制):

stateDiagram-v2
    [*] --> STANDBY
    STANDBY --> SLEEPING: SLEEP=1 && MEASURE=1
    SLEEPING --> WAKING: 检测到ACTIVITY事件
    WAKING --> MEASURING: 进入全速测量模式
    MEASURING --> INACTIVITY_CHECK: 持续无运动超过TIME_INACT时间
    INACTIVITY_CHECK --> SLEEPING: 自动返回睡眠状态

这种机制特别适合长期部署的环境监测系统。例如,在楼宇结构健康监测中,大多数时间结构处于静态,仅偶尔因外力产生微小振动。利用自动休眠功能,传感器可在大部分时间内维持极低功耗(典型值<10μA),仅在异常事件触发时才启动高精度采样。

配置示例如下:

// 配置自动休眠+链式唤醒
uint8_t power_ctl_config = (1 << 7) | (1 << 6) | (1 << 5); 
// LINK=1, AUTO_SLEEP=1, MEASURE=1
spi_write_register(ADXL345_REG_POWER_CTL, &power_ctl_config, 1);

参数说明
- LINK=1 表示断开活动与非活动检测之间的独立性,使其成为状态机的一部分;
- AUTO_SLEEP=1 允许在非活动期结束后自动转入睡眠;
- 必须先启用 MEASURE 才能激活整个链路。

此外,还需配置 TIME_INACT THRESH_INACT 寄存器来定义“静止”的判断标准(详见3.3节)。

3.1.3 动态功耗优化策略在电池供电系统中的应用

为了最大化电池寿命,必须综合考虑采样频率、测量范围、中断机制与电源模式的协同作用。以下是一个典型的动态功耗优化方案设计思路:

工作阶段 配置策略 功耗水平(典型)
等待状态 SLEEP=1, LINK=1, Auto-Sleep=1 ~5 μA
快速响应阶段 ODR=100Hz, ±2g量程 ~140 μA
高精度记录阶段 ODR=400Hz, ±4g, 高分辨率模式 ~400 μA

通过中断引脚(INT1或INT2)连接MCU的外部中断线,当ADXL345检测到自由落体或剧烈晃动时,会拉低中断信号,唤醒主控芯片进行数据读取与处理。处理完成后,系统可重新进入低功耗监听状态。

该策略已在多个实际项目中验证有效。例如,在某款智能快递箱防盗模块中,采用此机制使平均功耗降至 18μA ,配合2000mAh锂电池可连续运行超过 3年

同时,建议使用动态调节ODR的方式应对不同事件等级:

void adxl345_dynamic_power_control(event_type_t event) {
    switch(event) {
        case NO_EVENT:
            set_sleep_mode();      // 进入自动休眠
            break;
        case LIGHT_ACTIVITY:
            set_odr(50);           // 50Hz采样率
            break;
        case STRONG_IMPACT:
            set_odr(400);          // 提升至400Hz
            enable_full_resolution();
            break;
    }
}

逻辑分析
- 函数接收一个事件类型枚举,动态调整传感器配置;
- set_odr() 修改 BW_RATE 寄存器值;
- enable_full_resolution() 设置 DATA_FORMAT 寄存器中的 FULL_RES 位;
- 实现了“按需采样”的节能思想,避免始终以最高性能运行。

综上所述,ADXL345的电源管理机制并非单一开关控制,而是构建在一个多层级、事件驱动的状态机之上。掌握这些机制,是开发高性能低功耗系统的前提。

3.2 数据输出速率与测量范围配置

ADXL345的数据输出速率(Output Data Rate, ODR)和测量范围(Full Scale Range)是影响系统性能的两个核心参数。它们不仅决定传感器的响应速度和动态范围,还直接影响噪声水平、分辨率和整体功耗。正确配置这两个参数,是确保加速度数据既准确又高效的必要条件。

3.2.1 BW_RATE寄存器编码规则与ODR(输出数据速率)设置

数据输出速率由 BW_RATE 寄存器(地址 0x2C )控制,该寄存器提供4位用于设置带宽率码(Rate Code),对应不同的采样频率。其编码关系如下表所示:

RATE[3:0] 输出数据速率(Hz) 典型抗混叠滤波截止频率(Hz)
0b0000 0.1 0.05
0b0001 0.2 0.1
0b0010 0.39 0.2
0b1011 100 50
0b1100 200 100
0b1101 400 200
0b1111 1600 800

公式:
f_{ODR} = 6.25 \times 2^{RATE}

例如,若希望设置ODR为100Hz,则计算得:
\log_2(100 / 6.25) ≈ \log_2(16) = 4 → RATE = 0b1011

对应的写入操作如下:

uint8_t bw_rate_value = 0x0B; // 对应100Hz
spi_write_register(ADXL345_REG_BW_RATE, &bw_rate_value, 1);

参数说明
- ADXL345_REG_BW_RATE 宏定义为 0x2C
- 写入值为 0x0B 即二进制 1011 ,符合规格书要求;
- 此设置影响所有后续加速度数据的更新频率。

值得注意的是,更高的ODR虽然提升了时间分辨率,但也增加了功耗和噪声。因此,在低速运动检测中(如人体步态识别),通常选择 50~100Hz 足矣。

3.2.2 DATA_FORMAT寄存器中全量程选择(±2g/±4g/±8g/±16g)

测量范围通过 DATA_FORMAT 寄存器(地址 0x31 )中的 RANGE[1:0] 字段设置,支持四种选项:

RANGE[1:0] 量程 分辨率(典型)
00 ±2g 3.9 mg/LSB
01 ±4g 7.8 mg/LSB
10 ±8g 15.6 mg/LSB
11 ±16g 31.2 mg/LSB

例如,在无人机飞行控制系统中,由于加速度变化剧烈,需选用 ±16g 防止溢出;而在智能手表计步应用中,人体运动一般不超过 ±2g ,选择较小量程可以获得更高分辨率。

配置代码示例:

// 设置为±8g量程
uint8_t data_format = (1 << 3) | (1 << 1); 
// FULL_RES=0, JUSTIFY=0, INT_INVERT=0, RANGE=10 (±8g)
spi_write_register(ADXL345_REG_DATA_FORMAT, &data_format, 1);

逻辑分析
- 第3位( RANGE1 )设为1,第2位( RANGE0 )设为0,组合为 10 → ±8g;
- 其他位可根据需要配置,如 FULL_RES 是否启用高分辨率模式;
- 若启用 FULL_RES=1 ,即使量程改变,仍保持13位分辨率。

3.2.3 分辨率与噪声权衡:高精度模式启用条件

ADXL345支持“高分辨率模式”,通过设置 DATA_FORMAT 寄存器中的 FULL_RES 位实现。在此模式下,内部ADC使用完整的13位精度,而非固定10位。这意味着即使在±2g量程下,也能达到约 3.9 mg/LSB 的灵敏度。

然而,高分辨率模式会略微增加噪声(约增加0.5 LSB RMS),且功耗略有上升。因此,是否启用需根据应用权衡:

  • ✅ 推荐使用场景:静态倾斜测量、精密姿态校正、零偏补偿;
  • ❌ 不推荐场景:高速冲击检测、高g值事件捕捉。

启用方式如下:

uint8_t format_reg;
spi_read_register(ADXL345_REG_DATA_FORMAT, &format_reg, 1);
format_reg |= (1 << 3); // 设置 FULL_RES=1
spi_write_register(ADXL345_REG_DATA_FORMAT, &format_reg, 1);

扩展说明
- 必须先读取原值再修改,防止误改其他配置;
- 高分辨率模式下,数据左对齐存储,需注意解析顺序;
- 建议配合低通滤波使用,抑制额外噪声影响。

综上,ODR与量程的选择不是孤立的,而应结合应用目标、噪声容忍度和功耗预算进行系统级优化。

3.3 中断系统配置与事件触发机制

ADXL345内置强大的中断引擎,支持多达六种物理事件的独立检测与上报,极大减轻主控MCU的轮询负担。通过合理配置中断源、阈值与持续时间,可构建高度智能化的事件驱动型传感系统。

3.3.1 中断源使能(FREE_FALL、INACTIVITY、ACTIVITY等)

中断源由 INT_ENABLE (地址 0x2E )和 INT_MAP (地址 0x2F )两个寄存器共同控制。前者决定哪些事件可以触发中断,后者指定该事件映射到哪个物理引脚(INT1 或 INT2)。

支持的主要中断源包括:

中断源 触发条件 相关寄存器
DATA_READY 新数据可用 无需额外设置
SINGLE_TAP 单次敲击 THRESH_TAP, DUR, Latent
DOUBLE_TAP 双击 同上
ACTIVITY 加速度超过设定阈值 THRESH_ACT, TIME_ACT
INACTIVITY 连续时间内低于阈值 THRESH_INACT, TIME_INACT
FREE_FALL 自由落体检测(三轴加速度接近0g) THRESH_FF, TIME_FF

启用自由落体中断示例:

uint8_t int_enable = (1 << 5); // FREE_FALL interrupt enable
spi_write_register(ADXL345_REG_INT_ENABLE, &int_enable, 1);

// 映射到INT1引脚
uint8_t int_map = 0x00; // 所有中断映射到INT1
spi_write_register(ADXL345_REG_INT_MAP, &int_map, 1);

参数说明
- INT_ENABLE[5] 对应 FREE_FALL
- INT_MAP 若某位为1,则对应中断送往INT2,否则送INT1;
- 此处全部设为0,即所有中断均输出至INT1。

3.3.2 阈值设置与持续时间判定(THRESH_ 和 TIME_ 寄存器)

每个中断都有对应的阈值(Threshold)和时间窗口(Time)寄存器,单位如下:

  • THRESH_XXX :每LSB代表 62.5 mg (即 0.0625 g)
  • TIME_XXX :每LSB代表 1个ODR周期

例如,设置自由落体检测:

// 设置自由落体阈值为300mg → 300 / 62.5 = 4.8 → 取5
uint8_t thresh_ff = 5;
spi_write_register(ADXL345_REG_THRESH_FF, &thresh_ff, 1);

// 持续时间至少2个ODR周期(假设ODR=50Hz → 40ms)
uint8_t time_ff = 2;
spi_write_register(ADXL345_REG_TIME_FF, &time_ff, 1);

逻辑分析
- 当三轴加速度均小于300mg且持续超过40ms时,触发自由落体中断;
- 此配置可有效区分跌落与短暂失重;
- 需结合实际安装方向调整坐标轴权重。

3.3.3 中断引脚输出极性与锁存行为控制

INT_OUTPUT 寄存器(地址 0x2E 的别名?实际为 OFCAP 控制)可通过 DATA_FORMAT INT_INVERT 位设置中断极性。默认为高电平有效,也可设为低电平有效以兼容不同MCU。

此外, FIFO_CTL 中的 LATCH_INT 位可用于锁存中断信号,直到软件明确清除(通过读取 INT_SOURCE 寄存器)。这防止中断脉冲过短导致丢失。

// 锁定中断直到被读取
uint8_t fifo_ctl;
spi_read_register(ADXL345_REG_FIFO_CTL, &fifo_ctl, 1);
fifo_ctl |= (1 << 7); // LATCH_INT = 1
spi_write_register(ADXL345_REG_FIFO_CTL, &fifo_ctl, 1);

应用意义
- 在多任务系统中,确保中断不会被忽略;
- 支持边沿触发中断输入;
- 提高系统可靠性。

3.4 多模式协同工作的配置实例

3.4.1 运动检测+低功耗循环监控的综合配置方案

设想一个资产追踪设备,要求在静止时极低功耗,移动时自动唤醒并记录轨迹。

配置流程如下:

  1. 设置ODR=50Hz,量程±2g;
  2. 启用INACTIVITY检测,阈值=100mg,持续时间=5s;
  3. 启用ACTIVITY检测,阈值=150mg;
  4. 开启LINK+Auto-Sleep;
  5. 所有中断映射至INT1,连接MCU唤醒引脚。
adxl345_init_low_power_tracking() {
    set_data_rate(50);
    set_range(2); // ±2g
    write_reg(THRESH_INACT, 2);   // 100mg
    write_reg(TIME_INACT, 250);   // 5s @ 50Hz
    write_reg(THRESH_ACT, 3);     // 150mg
    enable_interrupt(FREE_FALL | ACTIVITY | INACTIVITY);
    set_power_mode(LINK | AUTO_SLEEP | MEASURE);
}

系统行为
- 设备静止>5秒 → 触发INACTIVITY → 进入Sleep;
- 再次移动 → ACTIVITY中断 → 唤醒MCU → 开始记录;
- 实测平均功耗 < 25μA。

3.4.2 基于中断唤醒的节能型数据采集系统设计思路

构建完整系统框图如下:

graph TD
    A[ADXL345] -- INT1 --> B(MCU EXTI)
    B --> C{是否活动?}
    C -->|是| D[启动SPI读取数据]
    D --> E[存储/上传]
    C -->|否| F[继续休眠]
    G[BW_RATE] --> A
    H[POWER_CTL] --> A

该架构实现了真正的“事件驱动”传感,大幅降低CPU占用率与整体能耗,适用于LPWAN、NB-IoT等远程监控场景。

4. 加速度数据获取、解析与误差处理

在嵌入式系统中,ADXL345作为高精度三轴加速度传感器的核心价值不仅体现在其硬件性能上,更依赖于对原始数据的准确获取、科学解析以及有效的误差补偿机制。尽管传感器本身具备数字输出能力,但未经处理的寄存器数据仅为二进制编码值,无法直接用于姿态解算、运动识别或结构健康监测等高级应用。因此,从SPI通信链路读取原始数据开始,到最终转换为物理单位(g),再到消除系统性偏差和环境干扰影响,整个流程构成了一个完整且严谨的数据处理闭环。本章将深入剖析这一过程的关键技术环节,涵盖多字节连续读取时序控制、补码解析算法、灵敏度校准策略、零偏补偿方法以及数据异常检测与滤波优化机制,确保输出结果具备高可靠性与长期稳定性。

4.1 加速度寄存器读取时序与操作流程

ADXL345通过I²C或SPI接口提供对内部寄存器的访问能力,其中加速度数据存储于三个16位寄存器对中:DATAX0/DATAX1(X轴)、DATAY0/DATAY1(Y轴)、DATAZ0/DATAZ1(Z轴)。每个轴由两个8位寄存器组成,分别表示低字节(LSB)和高字节(MSB)。由于采用SPI协议进行通信,必须严格遵循其命令格式与时序要求,才能确保数据正确读取。

4.1.1 多字节连续读取命令格式(RA_READ | AUTO_INCREMENT)

为了高效获取三轴加速度数据,ADXL345支持 自动地址递增模式 (Auto-Increment),允许一次SPI事务中连续读取多个寄存器内容。该功能通过设置读取地址的最高位为1来激活,即使用 RA_READ = 0x80 标志位,并结合起始寄存器地址构成完整的读命令字。

例如,若要从X轴低字节寄存器 DATAX0 (地址0x32)开始连续读取6个字节(X/Y/Z各两字节),应发送如下命令字:

uint8_t cmd = 0x80 | 0x32; // RA_READ | DATAX0 address

随后启动SPI传输,主设备发送该命令后即可接收6个连续字节的数据流。

字节序号 寄存器 内容说明
1 DATAX0 X轴低字节
2 DATAX1 X轴高字节
3 DATAY0 Y轴低字节
4 DATAY1 Y轴高字节
5 DATAZ0 Z轴低字节
6 DATAZ1 Z轴高字节

注意 :ADXL345默认采用 右对齐 (Right-Justified)模式,10/12/14位有效数据位于高比特位;当启用全分辨率模式时,数据为13位(±16g量程下可达10-bit有效分辨率)。

sequenceDiagram
    participant MCU
    participant ADXL345
    MCU->>ADXL345: 拉低SS (片选)
    MCU->>ADXL345: 发送命令字 0xB2 (0x80|0x32)
    ADXL345-->>MCU: 返回 DATAX0 (低字节)
    ADXL345-->>MCU: 返回 DATAX1 (高字节)
    ADXL345-->>MCU: 返回 DATAY0 (低字节)
    ADXL345-->>MCU: 返回 DATAY1 (高字节)
    ADXL345-->>MCU: 返回 DATAZ0 (低字节)
    ADXL345-->>MCU: 返回 DATAZ1 (高字节)
    MCU->>ADXL345: 拉高SS (结束传输)

上述流程图展示了典型的六字节连续读取过程,体现了SPI主从设备之间的交互逻辑。

代码实现示例(基于STM32 HAL库):
/**
 * @brief 连续读取ADXL345三轴加速度寄存器
 * @param reg_start 起始寄存器地址(如ADXL32_REG_DATAX0)
 * @param data 缓冲区指针,用于存储读回数据
 * @param len 数据长度(通常为6)
 * @return 操作状态
 */
HAL_StatusTypeDef adxl345_read_bytes(uint8_t reg_start, uint8_t *data, uint8_t len) {
    uint8_t cmd = 0x80 | reg_start;  // 启用读操作 + 自动递增
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  // SS低电平选中
    HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);                // 发送命令
    HAL_SPI_Receive(&hspi1, data, len, 100);               // 接收数据
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    // SS高电平释放
    return HAL_OK;
}

逐行分析

  • uint8_t cmd = 0x80 | reg_start; :构造读命令,0x80表示读操作并启用地址自动递增。
  • HAL_GPIO_WritePin(... RESET) :片选信号拉低,启动SPI事务。
  • HAL_SPI_Transmit() :发送命令字,通知ADXL345准备输出指定寄存器数据。
  • HAL_SPI_Receive() :接收后续len个字节数据,利用自动递增特性无需重复发送地址。
  • 最后拉高SS,结束通信,防止总线冲突。

此方式极大提升了数据吞吐效率,避免了多次片选切换带来的延迟。

4.1.2 SPI读操作时序要求与延时控制要点

ADXL345支持最高3.2 MHz的SPI时钟频率(SCK),但在实际应用中需考虑MCU外设驱动能力和PCB布线质量。关键时序参数包括:
- 命令发送后至首个数据位输出的延迟(t CS-DO )典型值为4 μs;
- SCK上升沿采样MISO数据,下降沿驱动输出;
- 片选信号(SS)必须在整个传输期间保持低电平。

若MCU软件模拟SPI或使用非DMA方式,可能引入额外延时。建议插入微秒级等待以确保稳定读取:

usDelay(5); // 在发送命令后插入5μs延时,保障器件响应

此外,在高速采集场景下推荐启用DMA传输,减少CPU干预,提高实时性。

4.1.3 数据对齐方式与LSB/MSB存储顺序说明

ADXL345的加速度数据默认以 16位有符号整数 形式存储,分为高低字节。组合时需注意字节顺序:先低后高,再按补码规则解析。

例如,某轴读得 LSB=0xF0 , MSB=0xFF ,则合成16位值为:

int16_t raw = (int16_t)((MSB << 8) | LSB);

但由于ADXL345是 10~13位有效分辨率 ,其余位为空闲或扩展符号位,故实际有效数据需根据当前量程判断是否需要右移对齐。

量程设置 有效位数 存储方式 是否需右移
±2g 10-bit 右对齐 是(移6位)
±4g 11-bit 右对齐 是(移5位)
±8g 12-bit 右对齐 是(移4位)
±16g 13-bit 右对齐 是(移3位)

可通过配置 DATA_FORMAT 寄存器中的 FULL_RES 位切换为左对齐模式,此时高有效位置于MSB左侧,便于快速截取高位数据,适用于FIFO批量处理场景。

4.2 二进制补码数据解析方法

ADXL345输出的加速度数据为有符号整数,采用 二进制补码 (Two’s Complement)表示法,能够正确反映正负方向的加速度变化。正确解析这些数据是获得真实物理量的前提。

4.2.1 10位/12位/14位有效数据提取(依量程而定)

不同量程下,ADXL345使用的ADC分辨率不同,导致有效数据位宽变化。例如:

  • ±2g:10位分辨率 → 精度约3.9 mg/LSB
  • ±16g:13位分辨率(FULL_RES=1时)→ 精度约31.2 mg/LSB

即使寄存器返回16位数据,也仅部分比特承载有效信息。假设当前量程为±2g,且未启用全分辨率模式,则实际有效数据占据高10位,低6位为填充零。

因此,需进行右移操作以对齐数值:

int16_t align_data(int16_t raw, uint8_t range) {
    switch(range) {
        case RANGE_2G:  return raw >> 6;  // 保留高10位
        case RANGE_4G:  return raw >> 5;  // 保留高11位
        case RANGE_8G:  return raw >> 4;  // 保留高12位
        case RANGE_16G: return raw >> 3;  // 保留高13位
        default:        return raw;
    }
}

此函数实现了根据不同量程动态调整数据对齐的能力,提升通用性。

4.2.2 补码转十进制算法实现与溢出边界判断

补码表示的最大优点是统一了加减运算逻辑,但解析时需注意符号扩展问题。C语言中 int16_t 类型天然支持补码运算,但仍需验证极端情况:

if (raw == 0x8000) {
    // 特殊情况:最小负值,可能发生溢出误判
    printf("Warning: Possible overflow detected\n");
}

对于±2g量程,理论范围为[-512, +511](10位补码),对应实际加速度[-2g, +1.996g]。超出此范围可能意味着配置错误或外部冲击过大。

4.2.3 实际加速度值计算公式(g = code × sensitivity)

将对齐后的数字码转换为物理加速度值,需乘以 灵敏度因子 (Sensitivity),单位为mg/LSB或g/LSB。

量程 默认灵敏度(典型值)
±2g 3.9 mg/LSB
±4g 7.8 mg/LSB
±8g 15.6 mg/LSB
±16g 31.2 mg/LSB

计算公式如下:

a(g) = \frac{code \times sensitivity}{1000}

其中 sensitivity 单位为mg/LSB,除以1000转换为g。

代码实现:
float convert_to_g(int16_t code, uint8_t range) {
    float sensitivity;
    switch(range) {
        case RANGE_2G:  sensitivity = 3.9f;  break;
        case RANGE_4G:  sensitivity = 7.8f;  break;
        case RANGE_8G:  sensitivity = 15.6f; break;
        case RANGE_16G: sensitivity = 31.2f; break;
        default:        sensitivity = 3.9f;
    }
    return (code * sensitivity) / 1000.0f;
}

示例:若读得code = 256,量程±2g,则
$ a = 256 × 3.9 / 1000 = 0.9984g $

该结果接近重力加速度的一倍,符合预期。

4.3 灵敏度校准与零偏补偿技术

出厂标称灵敏度虽具参考价值,但在精密测量场合仍需现场标定。同时,静态零点偏移(Offset)会显著影响测量准确性。

4.3.1 出厂默认灵敏度与实际标定差异分析

受制造工艺限制,每颗ADXL345的实际灵敏度存在±10%左右偏差。例如标称3.9 mg/LSB的±2g模式,实测可能为3.7或4.1 mg/LSB。

解决办法是执行 多点标定法 :将传感器分别静置于+1g和-1g方向(如翻转电路板),记录对应输出码值:

sensitivity = \frac{code_{+1g} - code_{-1g}}{2}

该差值即为1g对应的LSB数,倒数即为实际灵敏度(mg/LSB)。

4.3.2 静态零点偏移测量与软件补偿算法

即使无外力作用,三轴输出未必为零。常见Z轴在水平放置时输出约255 LSB(对应+1g),但X/Y轴理想应为0。实测中往往存在±50 LSB漂移。

零偏补偿步骤如下:

  1. 将设备平稳放置于水平台面;
  2. 连续采集N组数据(建议N≥100);
  3. 计算各轴均值作为初始偏移量offset_x, offset_y, offset_z;
  4. 后续每次读数减去对应偏移。
typedef struct {
    float offset_x;
    float offset_y;
    float offset_z;
} adxl345_calib_t;

void calibrate_offset(adxl345_calib_t *calib) {
    int32_t sum_x = 0, sum_y = 0, sum_z = 0;
    for(int i = 0; i < 100; i++) {
        int16_t ax, ay, az;
        read_accel_raw(&ax, &ay, &az);
        sum_x += ax; sum_y += ay; sum_z += az;
        HAL_Delay(10);
    }
    calib->offset_x = (float)sum_x / 100.0f;
    calib->offset_y = (float)sum_y / 100.0f;
    calib->offset_z = (float)sum_z / 100.0f;
}

补偿后数据: ax_corrected = ax_raw - calib->offset_x

4.3.3 温度漂移影响评估与长期稳定性对策

ADXL345具有较好的温度稳定性(零偏温漂约±1 mg/°C),但在工业级应用中仍不可忽视。可在固件中集成温度传感器(如MCU内置TS),建立查表法或线性补偿模型:

offset(T) = offset_{25°C} + k \cdot (T - 25)

其中k为实测温漂系数,可通过恒温箱实验获取。

4.4 数据有效性验证与异常处理机制

在复杂电磁环境或电源不稳定条件下,SPI通信可能出现丢包、错位等问题,需构建健壮的数据验证体系。

4.4.1 CRC校验缺失下的替代检测手段(超限值、突变检测)

ADXL345不支持硬件CRC,可采用以下软件手段:

  • 范围检查 :±2g量程下,任何轴超过±2.5g视为异常;
  • 变化率检测 :相邻两次读数差值超过阈值(如5g/ms)则标记可疑;
  • 一致性校验 :三轴平方和应接近1g²(静态时):
float norm = sqrt(ax*ax + ay*ay + az*az);
if (fabs(norm - 1.0f) > 0.3f) {
    // 加速度模长偏离严重,可能是噪声或故障
}

4.4.2 通信失败重试机制与超时判定逻辑

封装带重试机制的读取函数:

HAL_StatusTypeDef safe_read_accel(float *ax, float *ay, float *az) {
    for(int i = 0; i < 3; i++) {
        if(HAL_OK == adxl345_read_accel(ax, ay, az)) {
            if(is_valid(*ax, *ay, *az)) 
                return HAL_OK;
        }
        HAL_Delay(10);
    }
    return HAL_ERROR; // 三次失败
}

配合看门狗定时器防止死锁。

4.4.3 数据平滑滤波(移动平均、低通滤波)提升可靠性

原始数据常含高频噪声,适合采用一阶IIR低通滤波器:

// alpha ∈ [0.1, 0.3],越小越平滑
filtered = alpha * current + (1 - alpha) * filtered;

或使用滑动窗口平均法:

滤波方式 延迟 抑噪能力 适用场景
移动平均 静态测量
一阶IIR 实时动态响应
卡尔曼滤波 极强 复杂运动建模
graph LR
    A[Raw Data] --> B{Filter Type?}
    B -->|Simple| C[Moving Average]
    B -->|Responsive| D[First-order IIR]
    B -->|Advanced| E[Kalman Filter]
    C --> F[Stable Output]
    D --> F
    E --> F

合理选择滤波策略可在响应速度与稳定性之间取得平衡。

5. 模块化SPI驱动程序设计与完整实战实现

5.1 驱动程序分层架构设计思想

在嵌入式系统中,良好的软件架构是确保传感器驱动可维护性、可移植性和可扩展性的关键。针对ADXL345的SPI通信特性,采用 分层设计模式 将硬件依赖与功能逻辑解耦,提升代码复用率。

5.1.1 硬件抽象层(HAL)与传感器驱动层分离原则

驱动结构分为两层:

  • 硬件抽象层(HAL) :封装MCU特定的SPI外设操作,如 spi_transmit_receive() gpio_set() 等,屏蔽底层差异。
  • ADXL345驱动层 :专注于寄存器访问、数据解析和模式配置,不直接调用MCU外设API,仅通过HAL接口交互。
// 示例:硬件抽象层接口定义(hal_spi.h)
typedef struct {
    void (*init)(void);
    int (*transfer)(uint8_t *tx, uint8_t *rx, size_t len);
    void (*cs_select)(void);
    void (*cs_deselect)(void);
} spi_hal_t;

该设计允许同一份ADXL345驱动代码运行于STM32、ESP32或nRF系列平台,只需替换HAL实现。

5.1.2 接口函数命名规范与参数传递设计

统一采用 adxl345_<verb>_<noun>() 命名风格,增强语义清晰度。所有配置函数返回 int 类型状态码(0表示成功),便于错误追踪。

// 函数原型示例
int adxl345_init(spi_hal_t *spi_if);                    // 初始化
int adxl345_read_accel(float *x, float *y, float *z);   // 获取加速度(单位:g)
int adxl345_set_range(uint8_t range_g);                // 设置量程

参数设计遵循“输入常量、输出指针”原则,避免全局变量污染,支持多实例管理。

5.2 核心功能函数实现清单

5.2.1 adxl345_init() —— 初始化配置序列封装

初始化流程严格按照ADXL345数据手册要求执行上电时序与寄存器配置。

int adxl345_init(spi_hal_t *spi_if) {
    uint8_t dev_id;

    // 保存接口句柄
    g_spi = spi_if;

    // 片选拉低前延时(等待电源稳定)
    delay_ms(10);

    // 读取设备ID验证通信
    if (adxl345_reg_read(ADXL345_REG_DEVID, &dev_id) != 0) {
        return -1; // SPI失败
    }
    if (dev_id != ADXL345_DEVICE_ID_VAL) {
        return -2; // 设备未识别
    }

    // 配置测量参数
    adxl345_reg_write(ADXL345_REG_BW_RATE,     0x0A); // ODR=100Hz
    adxl345_reg_write(ADXL345_REG_DATA_FORMAT, 0x01); // ±4g范围
    adxl345_reg_write(ADXL345_REG_POWER_CTL,   0x08); // 启动测量模式

    return 0;
}
寄存器地址 名称 写入值 功能说明
0x00 DEVID 0xE5 器件ID(只读)
0x2C BW_RATE 0x0A 输出数据速率=100Hz
0x31 DATA_FORMAT 0x01 ±4g量程,右对齐
0x2D POWER_CTL 0x08 测量模式启用
0x1E~0x20 OFSX/Y/Z 0x00 零偏校准(可后续动态调整)

5.2.2 adxl345_set_range() 与 adxl345_set_datarate()

动态调整量程与采样率,适应不同应用场景。

int adxl345_set_range(uint8_t range_g) {
    uint8_t val;
    switch(range_g) {
        case 2:  val = 0x00; g_scale = 0.0078; break;
        case 4:  val = 0x01; g_scale = 0.0156; break;
        case 8:  val = 0x02; g_scale = 0.0312; break;
        case 16: val = 0x03; g_scale = 0.0625; break;
        default: return -1;
    }
    return adxl345_reg_write(ADXL345_REG_DATA_FORMAT, val);
}

int adxl345_set_datarate(uint8_t odr_hz) {
    uint8_t rate_code;
    if(odr_hz > 1600) return -1;
    // 查表映射ODR到BW_RATE编码
    static const struct { uint16_t hz; uint8_t code; } map[] = {
        {10, 0x05}, {25, 0x08}, {50, 0x09}, {100, 0x0A},
        {200, 0x0B}, {400, 0x0C}, {800, 0x0D}, {1600, 0x0F}
    };
    for(int i=0; i<8; i++) {
        if(map[i].hz == odr_hz) {
            rate_code = map[i].code;
            return adxl345_reg_write(ADXL345_REG_BW_RATE, rate_code);
        }
    }
    return -1;
}

5.2.3 adxl345_read_accel() —— 三轴数据批量读取与解析

利用自动地址递增特性一次性读取6字节原始数据。

int adxl345_read_accel(float *x, float *y, float *z) {
    uint8_t buffer[6];
    int16_t raw_x, raw_y, raw_z;

    // 发起多字节读操作(RA_READ | AUTO_INCREMENT)
    uint8_t cmd = ADXL345_REG_DATAX0 | 0x80 | 0x40;
    g_spi->cs_select();
    g_spi->transfer(&cmd, NULL, 1);
    g_spi->transfer(NULL, buffer, 6);
    g_spi->cs_deselect();

    // 组合16位补码(小端格式)
    raw_x = (int16_t)((buffer[1] << 8) | buffer[0]);
    raw_y = (int16_t)((buffer[3] << 8) | buffer[2]);
    raw_z = (int16_t)((buffer[5] << 8) | buffer[4]);

    // 转换为g单位(假设已设置±4g量程)
    *x = (float)raw_x * g_scale;
    *y = (float)raw_y * g_scale;
    *z = (float)raw_z * g_scale;

    return 0;
}

5.2.4 adxl345_enable_interrupt() —— 中断功能配置封装

使能自由落体中断并配置阈值。

int adxl345_enable_freefall_interrupt(uint8_t thresh_mg, uint8_t duration_ms) {
    uint8_t thresh = (uint8_t)(thresh_mg / 62.5); // 62.5mg/LSB
    uint8_t time_cycles = (uint8_t)(duration_ms / 5); // 5ms/cycle @100Hz

    adxl345_reg_write(ADXL345_REG_THRESH_FF,  thresh);
    adxl345_reg_write(ADXL345_REG_TIME_FF,    time_cycles);
    adxl345_reg_write(ADXL345_REG_INT_ENABLE, 0x04); // 使能FREE_FALL中断
    return 0;
}

5.3 抗干扰与容错机制集成

5.3.1 SPI通信超时检测与重发机制嵌入

引入最大重试次数防止死循环。

#define MAX_RETRY 3
static int spi_write_with_retry(uint8_t reg, uint8_t data) {
    int i;
    for(i=0; i<MAX_RETRY; i++) {
        if(adxl345_reg_write(reg, data) == 0) {
            return 0;
        }
        delay_ms(1);
    }
    return -1;
}

5.3.2 寄存器回读验证确保配置生效

写后立即读取确认值一致。

int adxl345_safe_write(uint8_t reg, uint8_t val) {
    adxl345_reg_write(reg, val);
    uint8_t readback;
    adxl345_reg_read(reg, &readback);
    return (readback == val) ? 0 : -1;
}

5.3.3 看门狗配合下的死锁预防策略

在长时间阻塞操作前后喂狗。

wdt_reset(); // 喂狗
if(adxl345_read_accel(&x,&y,&z) != 0) {
    error_handler();
}
wdt_reset(); // 喂狗

5.4 完整工程实例演示与调试验证

5.4.1 基于STM32平台的SPI驱动移植案例

使用STM32CubeMX配置SPI2工作在Mode 3(CPOL=1, CPHA=1),NSS软件控制。

spi_hal_t stm32_spi = {
    .init = MX_SPI2_Init,
    .transfer = HAL_SPI_TransmitReceive,
    .cs_select = () -> HAL_GPIO_WritePin(CS_GPIO, CS_PIN, GPIO_PIN_RESET),
    .cs_deselect = () -> HAL_GPIO_WritePin(CS_GPIO, CS_PIN, GPIO_PIN_SET)
};

5.4.2 串口打印加速度数据实现实时监控

while(1) {
    float x,y,z;
    if(adxl345_read_accel(&x,&y,&z)==0) {
        printf("ACC: %.3fg, %.3fg, %.3fg\r\n", x, y, z);
    }
    delay_ms(100);
}

输出示例:

ACC: 0.012g, -0.003g, 1.018g
ACC: 0.015g, 0.001g, 1.016g
ACC: 0.452g, 0.321g, 0.887g  // 晃动中

5.4.3 示波器抓取SCK/MISO信号验证通信正确性

使用逻辑分析仪捕获SPI时序,确认:

  • SCK空闲高电平(CPOL=1)
  • 数据在SCK上升沿采样(CPHA=1)
  • MISO延迟符合建立时间要求
sequenceDiagram
    participant MCU
    participant ADXL345
    MCU->>ADXL345: CS=LOW
    MCU->>ADXL345: SCK周期性脉冲(1MHz)
    MCU->>ADXL345: MOSI: [0xC0, 0x32] (读REG=0x32)
    ADXL345-->>MCU: MISO: 0x0A (返回值)
    MCU->>ADXL345: CS=HIGH

5.4.4 实测自由落体与晃动事件的中断响应表现

当检测到自由落体(阈值500mg,持续20ms)时,INT1引脚拉低,触发外部中断:

void EXTI1_IRQHandler(void) {
    if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_1)) {
        HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_1);
        handle_freefall_event(); // 记录日志或报警
    }
}

测试结果显示中断响应延迟小于5ms,满足实时性需求。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:ADXL345是Analog Devices推出的高性能三轴数字加速度传感器,广泛应用于物联网、嵌入式系统和消费电子中的运动、倾斜与冲击检测。本SPI通信程序实现了微控制器与ADXL345之间的高效数据交互,涵盖SPI接口初始化、传感器配置、中断设置、加速度数据读取与解析等关键流程。通过封装模块化函数,提升开发效率,并确保数据采集的准确性与系统稳定性,适用于各类基于加速度感知的智能应用开发。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐