从寄存器到加速度值: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 或者做姿态解算都会更顺手。