SPI通信

从寄存器到加速度值:SPI 驱动 ADXL345 的完整实践

ADXL345 是 ADI 推出的一款低功耗、高分辨率三轴数字加速度传感器,支持 I²C 和 SPI 两种接口。SPI 模式下它的最高时钟可达 5MHz,全双工、时序确定,很适合做实时加速度采集、振动检测、姿态识别等嵌入式项目。

硬件连接

ADXL345 通常使用 3.3V 供电,注意不要直接接 5V。

ADXL345 MCU
VCC 3.3V
GND GND
CS GPIO 片选
SCLK SPI_SCK
SDI / MOSI SPI_MOSI
SDO / MISO SPI_MISO

SPI 模式下 CS 必须由主机控制。通信开始时拉低,结束时拉高。

SPI 时序配置

ADXL345 的 SPI 使用 Mode 3:

  • CPOL = 1:时钟空闲时为高电平
  • CPHA = 1:数据在第二个时钟边沿采样
  • MSB 先发
  • 最高 SPI 时钟 5MHz

如果用 STM32 HAL 配置,大致是:

复制代码
hspi.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi.Init.CLKPhase    = SPI_PHASE_2EDGE;
hspi.Init.Mode        = SPI_MODE_MASTER;
hspi.Init.DataSize    = SPI_DATASIZE_8BIT;
hspi.Init.NSS         = SPI_NSS_SOFT;

SPI 读写协议

ADXL345 的寄存器地址字节有固定规则:

  • bit7 = 1:读操作
  • bit7 = 0:写操作
  • bit6 = 1:多字节连续读写
  • bit6 = 0:单字节操作

所以读寄存器 0x32 时,发送的地址字节是:

复制代码
0x32 | 0x80 = 0xB2

如果要连续读取 X/Y/Z 三轴数据,从 0x32 开始读 6 个字节,地址字节就是:

复制代码
0x32 | 0x80 | 0x40 = 0xF2

读写函数示例

复制代码
void ADXL345_WriteReg(uint8_t reg, uint8_t data)
{
    CS_LOW();
    SPI_Transfer(reg & 0x3F);   // bit7=0 表示写
    SPI_Transfer(data);
    CS_HIGH();
}

uint8_t ADXL345_ReadReg(uint8_t reg)
{
    CS_LOW();
    SPI_Transfer(reg | 0x80);   // bit7=1 表示读
    uint8_t value = SPI_Transfer(0xFF); // 发送 dummy 字节产生时钟
    CS_HIGH();
    return value;
}

SPI 是全双工总线,主机每发送一个字节都会同时收到一个字节。所以读操作时也要发送 dummy 数据来提供时钟。

初始化流程

建议按这个顺序配置:

复制代码
// 1. 验证器件 ID
uint8_t id = ADXL345_ReadReg(0x00); // 应返回 0xE5

// 2. 设置数据格式:±16g,全分辨率,4 线 SPI
ADXL345_WriteReg(0x31, 0x0B);

// 3. 设置输出数据速率:100Hz
ADXL345_WriteReg(0x2C, 0x0A);

// 4. 进入测量模式
ADXL345_WriteReg(0x2D, 0x08);

0x31 寄存器决定量程和对齐方式;0x2C 决定输出数据速率;0x2D 的 bit3 置 1 后才会真正开始测量。

读取三轴加速度

复制代码
void ADXL345_ReadAccel(int16_t *x, int16_t *y, int16_t *z)
{
    uint8_t buf[6];

    CS_LOW();
    SPI_Transfer(0xF2);           // 连续读取 0x32~0x37
    for (int i = 0; i < 6; i++) {
        buf[i] = SPI_Transfer(0xFF);
    }
    CS_HIGH();

    *x = (int16_t)((buf[1] << 8) | buf[0]);
    *y = (int16_t)((buf[3] << 8) | buf[2]);
    *z = (int16_t)((buf[5] << 8) | buf[4]);
}

ADXL345 的三轴数据是小端格式:低字节在前,高字节在后。

数据换算

全分辨率模式下,数据是 16 位有符号补码。±16g 时灵敏度约为:

复制代码
3.9 mg/LSB = 0.0039 g/LSB

所以:

复制代码
float ax_g = x_raw * 0.0039f;
float ay_g = y_raw * 0.0039f;
float az_g = z_raw * 0.0039f;

如果配置为 ±2g,灵敏度会不同,需要根据 DATA_FORMAT 寄存器重新计算。

调试建议

  • 上电后先读 0x00 寄存器,确认返回 0xE5。
  • 如果读出来是 0x00 或 0xFF,优先检查 CS、SCLK、MOSI、MISO 接线。
  • 确认 SPI 模式是 Mode 3,而不是常见的 Mode 0。
  • 确认 CS 在每次传输前后正确拉低、拉高。
  • 确认 POWER_CTL 寄存器已经写入 0x08,否则传感器不会输出测量数据。

小结

SPI 驱动 ADXL345 的关键不在代码量,而在几个细节:Mode 3 时序、读写位和多字节位、CS 控制、小端数据拼接、量程换算。把这些点理顺后,后续接 FIFO、中断、DMA 或者做姿态解算都会更顺手。

相关推荐
创界工坊工作室2 小时前
14-基于stm32的水肥一体化灌溉系统设计
stm32·单片机·嵌入式硬件
子木HAPPY阳VIP2 小时前
Ubuntu 关闭防火墙操作步骤
linux·运维·ubuntu
王振超wzc2 小时前
嵌入式开发环境搭建--WM软件安装,Ubuntu操作系统安装
linux·运维·ubuntu
Logic1012 小时前
C语言/数据结构位运算题解:异或XOR找出书柜中编号只出现一次的那一层——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
斑马1393 小时前
ARM学习笔记(八)——PWM与ADC
单片机·嵌入式硬件
恒锐丰科技林技术员3 小时前
EG2106 半桥 MOS/IGBT 驱动芯片解析与工程应用
经验分享·嵌入式硬件·硬件工程
GeW3 小时前
制造强国底座:Linux与数据库驱动的新型工业基础设施
linux
威嵌神州4 小时前
P2020 性能怎么样?经典 PowerPC 架构的嵌入式控制优势解析
嵌入式硬件
xiaoye-duck4 小时前
《Linux 网络编程》深入理解 IO 多路复用:select 函数详解与 Echo 服务实战
linux·网络