一、前言
本文完整讲解基于 STM32F103C8T6 + ADXL345 MEMS 三轴加速度传感器 + 0.96 寸 I2C OLED 的整套工程原理、硬件接线、寄存器配置、数据换算与现象分析,采用软件模拟 I2C通信,无需硬件 I2C 外设,新手友好,适合单片机课程教学、姿态检测入门实验。

整套工程实现逻辑: 模块姿态 / 振动 / 运动改变 → ADXL345 内部 MEMS 采集加速度 → 软件 I2C 读取三轴原始数据 → 转换为 mg 单位 → OLED 实时打印 X/Y/Z 三轴加速度数值
视频讲解:STM32F103 驱动ADXL345三轴加速度传感器(软件I2C+OLED实时显示)
https://www.bilibili.com/video/BV1wjMd6EEDW/?share_source=copy_web&vd_source=91b967b41a17a5686913521545a8c3fb
二、硬件接线说明
2.1 器件引脚对应表
| SDA | PA0 | 软件 I2C 数据线 |
| SCK(SCL) | PA1 | 软件 I2C 时钟线(SPI 标 SCK,I2C 模式等效 SCL) |
| CS | 3.3V | 拉高切换至 I2C 通信模式 |
| SDO/ALT ADDRESS | GND | I2C 从机地址 0x53 |
| VCC | 3.3V | 传感器供电(严禁直连 5V,无稳压模块仅支持 2~3.6V) |
| GND | GND | 单片机与传感器共地,通信稳定前提 |
| SCL | PB8 | OLED 软件 I2C 时钟 |
| SDA | PB9 | OLED 软件 I2C 数据 |
| VCC | 3.3V | 屏幕供电 |
| GND | GND | 共地 |
2.2 关键引脚补充说明
- 接地:7 位 I2C 地址0x53,写地址0xA6,读地址0xA7(本工程使用)
- 接 3.3V:7 位地址变为0x1D,代码需要同步修改,否则读不到数据
三、ADXL345 核心工作原理
ADXL345 是数字输出 MEMS 三轴加速度传感器,核心是芯片内部微型机械质量块结构。
3.1 信号转换流程
姿态 / 晃动 / 振动 → 惯性使内部质量块产生微小位移 → 两侧差分电容容量发生变化 → 内部电路放大、滤波、ADC 模数转换 → 输出 X/Y/Z 三轴数字加速度值
可检测两种加速度:
重点知识点:传感器只采集加速度,无法直接获取位移、速度;仅单次读数不能推算移动距离。
3.2 静止时数值规律
地球恒定存在 1g 重力加速度(1000mg),设备平放静止时:
- Z 轴垂直地面:Z≈±1000mg;
- X、Y 平行地面:X、Y≈0mg; 翻转传感器后坐标轴正负号反转;任意倾斜状态下三轴矢量合加速度约等于 1000mg。
自由落体特殊现象:完全失重状态下 X/Y/Z 三轴读数全部趋近 0mg。
四、软件 I2C 通信与三轴数据读取逻辑
本工程不使用 STM32 硬件 I2C 外设,将 PA0、PA1 配置为开漏输出,通过翻转 GPIO 电平模拟标准 I2C 时序。
4.1 I2C 基础通信时序
单次寄存器读写完整流程: 起始信号 → 从机地址 + 读写位 → 寄存器地址 → 读写数据 → 停止信号
- 起始:SCL 高电平,SDA 由高拉低;
- 应答 ACK:每传输 8bit 数据,从机拉低 SDA 产生第 9 位应答;
- 停止:SCL 高电平,SDA 由低拉高;
- 寄存器读操作:先发写指令指定寄存器,重复起始信号后切换读模式接收数据。
4.2 连续读取 6 字节数据原因
ADXL345 三轴数据寄存器地址连续排布,从0x32开始依次存储 X 低、X 高、Y 低、Y 高、Z 低、Z 高:
| 0x32 | DATAX0 | X 轴低字节 |
| 0x33 | DATAX1 | X 轴高字节 |
| 0x34 | DATAY0 | Y 轴低字节 |
| 0x35 | DATAY1 | Y 轴高字节 |
| 0x36 | DATAZ0 | Z 轴低字节 |
| 0x37 | DATAZ1 | Z 轴高字节 |
一次性连续读取 6 字节,保证 X/Y/Z 三轴数据来自同一采样周期,分开单轴读取会存在采样时间差,姿态数据失真。
4.3 16 位有符号原始值合成规则
数据存储格式:低字节在前,高字节在后,采用二进制补码存储正负加速度,C 语言处理代码:
s16 x = (s16)((X高字节 << 8) | X低字节);
s16 y = (s16)((Y高字节 << 8) | Y低字节);
s16 z = (s16)((Z高字节 << 8) | Z低字节);
五、寄存器初始化配置与单位换算
5.1 关键寄存器配置表
上电初始化严格按照下表配置,开启测量模式后才能读取有效加速度:
| DEVID | 0x00 | 只读 | 校验传感器 ID,正确值 0xE5,通信故障识别依据 |
| POWER_CTL | 0x2D | 0x00 | 配置参数前进入待机模式 |
| DATA_FORMAT | 0x31 | 0x0B | 开启全分辨率,量程 ±16g |
| BW_RATE | 0x2C | 0x0A | 正常功耗,传感器输出速率 100Hz |
| OFSX/OFSY/OFSZ | 0x1E~0x20 | 0x00 | 三轴硬件偏移寄存器,默认不校准 |
| INT_ENABLE | 0x2E | 0x00 | 关闭所有中断,采用轮询读取数据 |
| FIFO_CTL | 0x38 | 0x00 | 不使用 FIFO 缓存 |
| POWER_CTL | 0x2D | 0x08 | MEASURE 位置 1,传感器启动测量 |
5.2 原始值转 mg 单位计算
工程配置 ±16g 全分辨率模式,分辨率参数:1 LSB = 3.9mg 换算公式:加速度(mg) = 原始采样值 × 3.9
单片机避免浮点运算,使用整数等效计算:
int mg = raw_value * 39 / 10;
单位说明:文中 mg 代表毫重力加速度,和质量单位毫克无关,换算关系 1000mg = 1g ≈ 9.8m/s²
换算示例:
| 0 | 0mg | 0g |
| 128 | 499mg | 0.5g |
| 256 | 998mg | 1g |
| -256 | -998mg | -1g |
5.3 主程序整体运行流程
- ID≠0xE5:OLED 打印 Sensor error,1s 后重新检测;
- ID=0xE5:正常进入采样流程;
补充:传感器内部数据刷新 100Hz(10ms 更新一次),程序 200ms 刷新屏幕,显示速度满足实验观察需求。
main.c代码
/*
* ADXL345 three-axis acceleration teaching demo
* ADXL345 SDA -> PA0, SCK/SCL -> PA1
* OLED SCL -> PB8, SDA -> PB9
*/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "ADXL345.h"
#define ADXL345_REFRESH_MS 200U
static void ADXL345_ShowData(const ADXL345_DataTypeDef *data);
static void ADXL345_ShowError(u8 device_id);
static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 digits, u8 size);
static void OLED_ShowHexByte(u8 x, u8 y, u8 value, u8 size);
int main(void)
{
ADXL345_DataTypeDef sensor_data;
u8 sensor_ok;
delay_init();
NVIC_Configuration();
OLED_Init();
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
OLED_Clear();
OLED_ShowString(36, 0, (u8 *)"ADXL345", 16);
OLED_ShowString(16, 22, (u8 *)"SDA PA0 SCK PA1", 12);
OLED_ShowString(25, 42, (u8 *)"Initializing", 12);
OLED_Refresh();
sensor_ok = ADXL345_Init();
while(1)
{
if((sensor_ok != 0U) && (ADXL345_ReadAcceleration(&sensor_data) != 0U))
{
ADXL345_ShowData(&sensor_data);
delay_ms(ADXL345_REFRESH_MS);
}
else
{
ADXL345_ShowError(ADXL345_ReadDeviceID());
delay_ms(1000);
sensor_ok = ADXL345_Init();
}
}
}
static void ADXL345_ShowData(const ADXL345_DataTypeDef *data)
{
OLED_Clear();
OLED_ShowString(16, 0, (u8 *)"ADXL345 +/-16g", 12);
OLED_ShowString(0, 16, (u8 *)"X:", 12);
OLED_ShowSignedNum(18, 16, ADXL345_RawToMilliG(data->x), 5U, 12);
OLED_ShowString(54, 16, (u8 *)" mg", 12);
OLED_ShowString(0, 32, (u8 *)"Y:", 12);
OLED_ShowSignedNum(18, 32, ADXL345_RawToMilliG(data->y), 5U, 12);
OLED_ShowString(54, 32, (u8 *)" mg", 12);
OLED_ShowString(0, 48, (u8 *)"Z:", 12);
OLED_ShowSignedNum(18, 48, ADXL345_RawToMilliG(data->z), 5U, 12);
OLED_ShowString(54, 48, (u8 *)" mg", 12);
OLED_Refresh();
}
static void ADXL345_ShowError(u8 device_id)
{
OLED_Clear();
OLED_ShowString(36, 0, (u8 *)"ADXL345", 16);
OLED_ShowString(22, 20, (u8 *)"Sensor error", 12);
OLED_ShowString(10, 36, (u8 *)"SDA PA0 SCK PA1", 12);
OLED_ShowString(34, 50, (u8 *)"ID:0x", 12);
OLED_ShowHexByte(64, 50, device_id, 12);
OLED_Refresh();
}
static void OLED_ShowSignedNum(u8 x, u8 y, s32 value, u8 digits, u8 size)
{
u32 magnitude;
if(value < 0)
{
OLED_ShowChar(x, y, '-', size);
magnitude = (u32)(-value);
}
else
{
OLED_ShowChar(x, y, '+', size);
magnitude = (u32)value;
}
OLED_ShowNum((u8)(x + size / 2U), y, magnitude, digits, size);
}
static void OLED_ShowHexByte(u8 x, u8 y, u8 value, u8 size)
{
u8 high;
u8 low;
high = (u8)((value >> 4) & 0x0FU);
low = (u8)(value & 0x0FU);
OLED_ShowChar(x, y, (high < 10U) ? (u8)('0' + high) : (u8)('A' + high – 10U), size);
OLED_ShowChar((u8)(x + size / 2U), y,
(low < 10U) ? (u8)('0' + low) : (u8)('A' + low – 10U), size);
}
ADXL345.c代码
#include "ADXL345.h"
#include "delay.h"
/*
* ADXL345 软件 I2C 驱动
*
* 硬件连接:
* ADXL345 SDA -> STM32F103C8T6 PA0
* ADXL345 SCK/SCL -> STM32F103C8T6 PA1
*
* PA0 和 PA1 配置成开漏输出,通过软件模拟 I2C 时序。
* CS 引脚必须接 3.3V 以选择 I2C 模式;ALT ADDRESS/SDO 接地时,
* ADXL345 的 7 位从机地址为 0x53,对应写地址 0xA6、读地址 0xA7。
*/
/* I2C 从机地址:最低位由读写方向自动补充。 */
#define ADXL345_I2C_ADDRESS 0x53U
#define ADXL345_WRITE_ADDRESS (ADXL345_I2C_ADDRESS << 1)
#define ADXL345_READ_ADDRESS ((ADXL345_I2C_ADDRESS << 1) | 0x01U)
/* 本驱动使用的 ADXL345 寄存器地址。 */
#define ADXL345_REG_DEVID 0x00U /* 器件标识寄存器,固定值为 0xE5 */
#define ADXL345_REG_OFSX 0x1EU /* X 轴零偏校准寄存器 */
#define ADXL345_REG_OFSY 0x1FU /* Y 轴零偏校准寄存器 */
#define ADXL345_REG_OFSZ 0x20U /* Z 轴零偏校准寄存器 */
#define ADXL345_REG_BW_RATE 0x2CU /* 输出数据速率和低功耗控制寄存器 */
#define ADXL345_REG_POWER_CTL 0x2DU /* 电源模式和测量模式控制寄存器 */
#define ADXL345_REG_INT_ENABLE 0x2EU /* 中断使能寄存器 */
#define ADXL345_REG_DATA_FORMAT 0x31U /* 量程、全分辨率和数据格式寄存器 */
#define ADXL345_REG_DATAX0 0x32U /* 三轴数据起始地址:DATAX0~DATAZ1 */
#define ADXL345_REG_FIFO_CTL 0x38U /* FIFO 工作模式控制寄存器 */
/* 软件 I2C 引脚操作:开漏输出写 1 表示释放总线,写 0 表示拉低总线。 */
#define ADXL345_SDA_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_0)
#define ADXL345_SDA_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_0)
#define ADXL345_SCL_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_1)
#define ADXL345_SCL_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_1)
#define ADXL345_READ_SDA() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
/* 软件 I2C 底层函数,只在本文件内部使用。 */
static void ADXL345_I2C_Delay(void);
static void ADXL345_I2C_Start(void);
static void ADXL345_I2C_Stop(void);
static void ADXL345_I2C_SendAck(u8 send_ack);
static u8 ADXL345_I2C_SendByte(u8 data);
static u8 ADXL345_I2C_ReadByte(void);
static void ADXL345_I2C_BusRecovery(void);
/*
* I2C 时序延时。
* 5us 半周期对应的总线速度低于 100kHz,适合教学和杜邦线连接环境。
*/
static void ADXL345_I2C_Delay(void)
{
delay_us(5);
}
/*
* 产生 I2C 起始信号:
* SCL 为高电平时,SDA 由高电平变为低电平。
*/
static void ADXL345_I2C_Start(void)
{
ADXL345_SDA_HIGH();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
ADXL345_SDA_LOW();
ADXL345_I2C_Delay();
ADXL345_SCL_LOW();
ADXL345_I2C_Delay();
}
/*
* 产生 I2C 停止信号:
* SCL 为高电平时,SDA 由低电平变为高电平。
*/
static void ADXL345_I2C_Stop(void)
{
ADXL345_SCL_LOW();
ADXL345_SDA_LOW();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
ADXL345_SDA_HIGH();
ADXL345_I2C_Delay();
}
/*
* 从最高位开始向 I2C 总线发送一个字节,并读取从机应答。
* 返回值:1 表示收到 ACK,0 表示收到 NACK。
*/
static u8 ADXL345_I2C_SendByte(u8 data)
{
u8 i;
u8 acknowledged;
/* 依次发送 D7~D0,每个时钟周期发送一位。 */
for(i = 0U; i < 8U; i++)
{
ADXL345_SCL_LOW();
if((data & 0x80U) != 0U)
ADXL345_SDA_HIGH();
else
ADXL345_SDA_LOW();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
data <<= 1;
}
/*
* 第 9 个时钟用于读取应答:先释放 SDA,再由 ADXL345 拉低 SDA。
* SDA 为低表示 ACK,说明从机已经正确接收当前字节。
*/
ADXL345_SCL_LOW();
ADXL345_SDA_HIGH();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
acknowledged = (ADXL345_READ_SDA() == Bit_RESET) ? 1U : 0U;
ADXL345_SCL_LOW();
ADXL345_I2C_Delay();
return acknowledged;
}
/*
* 从 I2C 总线读取一个字节,按 D7~D0 的顺序接收。
* 本函数只负责读取 8 位数据,读完后的 ACK/NACK 由调用者发送。
*/
static u8 ADXL345_I2C_ReadByte(void)
{
u8 i;
u8 data;
data = 0U;
ADXL345_SDA_HIGH();
for(i = 0U; i < 8U; i++)
{
ADXL345_SCL_LOW();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
data <<= 1;
if(ADXL345_READ_SDA() != Bit_RESET)
data |= 0x01U;
}
ADXL345_SCL_LOW();
ADXL345_I2C_Delay();
return data;
}
/*
* 主机向从机发送应答位。
* send_ack = 1:发送 ACK,表示还要继续读取后续字节;
* send_ack = 0:发送 NACK,表示当前字节是最后一个字节。
*/
static void ADXL345_I2C_SendAck(u8 send_ack)
{
ADXL345_SCL_LOW();
if(send_ack != 0U)
ADXL345_SDA_LOW();
else
ADXL345_SDA_HIGH();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
ADXL345_SCL_LOW();
ADXL345_SDA_HIGH();
ADXL345_I2C_Delay();
}
/*
* I2C 总线恢复:释放 SDA 后输出 9 个 SCL 脉冲,再发送停止信号。
* 如果从机因上次通信被意外中断而一直占用 SDA,该操作可使其退出未完成状态。
*/
static void ADXL345_I2C_BusRecovery(void)
{
u8 i;
ADXL345_SDA_HIGH();
for(i = 0U; i < 9U; i++)
{
ADXL345_SCL_LOW();
ADXL345_I2C_Delay();
ADXL345_SCL_HIGH();
ADXL345_I2C_Delay();
}
ADXL345_I2C_Stop();
}
/*
* 向 ADXL345 的指定寄存器写入一个字节。
* 通信顺序:起始信号 -> 写地址 -> 寄存器地址 -> 数据 -> 停止信号。
* 任意阶段未收到 ACK 都会立即停止通信并返回 0。
*/
u8 ADXL345_WriteRegister(u8 register_address, u8 data)
{
ADXL345_I2C_Start();
if(ADXL345_I2C_SendByte(ADXL345_WRITE_ADDRESS) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
if(ADXL345_I2C_SendByte(register_address) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
if(ADXL345_I2C_SendByte(data) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
ADXL345_I2C_Stop();
return 1U;
}
/*
* 从指定起始寄存器连续读取多个字节。
*
* 先以写方向发送寄存器地址,再发送重复起始信号并切换到读方向;
* 除最后一个字节外,每读完一个字节都发送 ACK,最后一个字节发送 NACK。
*/
u8 ADXL345_ReadRegisters(u8 start_address, u8 *data, u8 length)
{
u8 i;
if((data == 0) || (length == 0U))
return 0U;
ADXL345_I2C_Start();
if(ADXL345_I2C_SendByte(ADXL345_WRITE_ADDRESS) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
if(ADXL345_I2C_SendByte(start_address) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
ADXL345_I2C_Start();
if(ADXL345_I2C_SendByte(ADXL345_READ_ADDRESS) == 0U)
{
ADXL345_I2C_Stop();
return 0U;
}
for(i = 0U; i < length; i++)
{
data[i] = ADXL345_I2C_ReadByte();
ADXL345_I2C_SendAck((i < (u8)(length – 1U)) ? 1U : 0U);
}
ADXL345_I2C_Stop();
return 1U;
}
/* 从指定寄存器读取一个字节,是连续读取接口的单字节封装。 */
u8 ADXL345_ReadRegister(u8 register_address, u8 *data)
{
return ADXL345_ReadRegisters(register_address, data, 1U);
}
/*
* 读取器件标识寄存器。
* 正常连接的 ADXL345 应返回 0xE5;通信失败时返回初始值 0x00。
*/
u8 ADXL345_ReadDeviceID(void)
{
u8 device_id;
device_id = 0U;
(void)ADXL345_ReadRegister(ADXL345_REG_DEVID, &device_id);
return device_id;
}
/*
* 初始化软件 I2C 引脚和 ADXL345 工作参数。
*
* 配置过程:
* 1. 将 PA0、PA1 配置为 50MHz 开漏输出,并恢复 I2C 总线;
* 2. 读取 DEVID,确认连接的器件确实是 ADXL345;
* 3. 进入待机模式后配置全分辨率、±16g 和 100Hz 数据速率;
* 4. 清零三轴偏移量,关闭本示例未使用的中断和 FIFO;
* 5. 设置 MEASURE 位,开始连续测量。
*
* 返回值:1 表示初始化成功,0 表示器件标识错误或寄存器写入失败。
*/
u8 ADXL345_Init(void)
{
GPIO_InitTypeDef gpio_init_structure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
gpio_init_structure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_init_structure.GPIO_Speed = GPIO_Speed_50MHz;
gpio_init_structure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA, &gpio_init_structure);
ADXL345_SDA_HIGH();
ADXL345_SCL_HIGH();
delay_ms(5);
ADXL345_I2C_BusRecovery();
if(ADXL345_ReadDeviceID() != ADXL345_DEVICE_ID)
return 0U;
/* 修改测量参数前先进入待机模式。 */
if(ADXL345_WriteRegister(ADXL345_REG_POWER_CTL, 0x00U) == 0U)
return 0U;
/* FULL_RES = 1、Range = 11,选择全分辨率和 ±16g 量程。 */
if(ADXL345_WriteRegister(ADXL345_REG_DATA_FORMAT, 0x0BU) == 0U)
return 0U;
/* LOW_POWER = 0、Rate = 0x0A,选择正常功耗和 100Hz 输出速率。 */
if(ADXL345_WriteRegister(ADXL345_REG_BW_RATE, 0x0AU) == 0U)
return 0U;
/* 教学演示暂不进行零偏校准,三个偏移寄存器均写入 0。 */
if(ADXL345_WriteRegister(ADXL345_REG_OFSX, 0x00U) == 0U)
return 0U;
if(ADXL345_WriteRegister(ADXL345_REG_OFSY, 0x00U) == 0U)
return 0U;
if(ADXL345_WriteRegister(ADXL345_REG_OFSZ, 0x00U) == 0U)
return 0U;
/* 本教学演示采用轮询读取,因此关闭中断和 FIFO 功能。 */
if(ADXL345_WriteRegister(ADXL345_REG_INT_ENABLE, 0x00U) == 0U)
return 0U;
if(ADXL345_WriteRegister(ADXL345_REG_FIFO_CTL, 0x00U) == 0U)
return 0U;
/* POWER_CTL 的 MEASURE 位置 1,退出待机并开始连续转换。 */
if(ADXL345_WriteRegister(ADXL345_REG_POWER_CTL, 0x08U) == 0U)
return 0U;
delay_ms(10);
return 1U;
}
/*
* 一次连续读取 DATAX0~DATAZ1 共 6 个寄存器。
* ADXL345 按低字节在前、高字节在后的顺序输出数据,合并后转换为 s16,
* 从而保留负加速度的二进制补码符号。
*/
u8 ADXL345_ReadAcceleration(ADXL345_DataTypeDef *data)
{
u8 buffer[6];
if(data == 0)
return 0U;
if(ADXL345_ReadRegisters(ADXL345_REG_DATAX0, buffer, 6U) == 0U)
return 0U;
/* 每个轴由相邻的低、高两个字节组成。 */
data->x = (s16)(((u16)buffer[1] << 8) | buffer[0]);
data->y = (s16)(((u16)buffer[3] << 8) | buffer[2]);
data->z = (s16)(((u16)buffer[5] << 8) | buffer[4]);
return 1U;
}
/*
* 将某一轴的原始数据换算成 mg(毫重力加速度)。
*
* 当前为全分辨率模式,典型灵敏度为 3.9mg/LSB:
* 加速度(mg) = 原始数据 × 3.9
* 使用“先乘 39、再除 10”的整数运算,避免引入浮点运算代码。
*/
s32 ADXL345_RawToMilliG(s16 raw_value)
{
return ((s32)raw_value * 39L) / 10L;
}
ADXL345.h代码
#ifndef __ADXL345_H
#define __ADXL345_H
#include "sys.h"
/*
* ADXL345 三轴加速度传感器驱动头文件
*
* 硬件连接:
* ADXL345 SDA -> STM32F103C8T6 PA0
* ADXL345 SCK/SCL -> STM32F103C8T6 PA1
*
* 注意:ADXL345 的 CS 引脚需要接 3.3V,才能进入 I2C 通信模式;
* ALT ADDRESS/SDO 接地时,器件的 7 位 I2C 地址为 0x53。
*/
/* ADXL345 的固定器件标识,读取 DEVID 寄存器应得到 0xE5。 */
#define ADXL345_DEVICE_ID 0xE5U
/* 保存一次采样得到的 X、Y、Z 三轴原始有符号数据。 */
typedef struct
{
s16 x; /* X 轴原始数据 */
s16 y; /* Y 轴原始数据 */
s16 z; /* Z 轴原始数据 */
} ADXL345_DataTypeDef;
/*
* 初始化 GPIO 和 ADXL345。
* 配置为全分辨率、±16g、100Hz 输出数据速率并进入测量模式。
* 返回值:1 表示初始化成功,0 表示通信或器件检测失败。
*/
u8 ADXL345_Init(void);
/* 读取 DEVID 寄存器,正常模块应返回 ADXL345_DEVICE_ID(0xE5)。 */
u8 ADXL345_ReadDeviceID(void);
/* 向指定寄存器写入一个字节,成功返回 1,失败返回 0。 */
u8 ADXL345_WriteRegister(u8 register_address, u8 data);
/* 从指定寄存器读取一个字节,成功返回 1,失败返回 0。 */
u8 ADXL345_ReadRegister(u8 register_address, u8 *data);
/* 从 start_address 开始连续读取 length 个字节。 */
u8 ADXL345_ReadRegisters(u8 start_address, u8 *data, u8 length);
/* 连续读取 X、Y、Z 三轴加速度原始数据。 */
u8 ADXL345_ReadAcceleration(ADXL345_DataTypeDef *data);
/*
* 将原始数据换算为 mg(毫重力加速度)。
* 全分辨率模式下的典型比例系数为 3.9mg/LSB,1000mg 约等于 1g。
*/
s32 ADXL345_RawToMilliG(s16 raw_value);
#endif
六、OLED 屏幕显示效果解析
6.1 正常运行界面
ADXL345 +/-16g
X:+00000 mg
Y:+00000 mg
Z:+00998 mg

字段释义:
- +/-16g:当前传感器测量量程;
- X/Y/Z:三轴加速度,正负号代表受力方向;
- mg:毫重力加速度单位。
6.2 通信故障报错界面
ADXL345
Sensor error
SDA PA0 SCK PA1
ID:0x00
出现报错排查顺序:供电、共地、CS 引脚、SDO 地址引脚、SDA/SCL 接线、I2C 上拉电阻。
七、量程、分辨率与实验现象分析
ADXL345 支持 4 档量程:±2g、±4g、±8g、±16g;本工程选用 ±16g,可捕捉大幅度晃动、冲击,全分辨率模式下所有量程统一 3.9mg/LSB,±16g 可达 13 位高精度。

八、传感器误差来源与简易校准方案
实际采样数值和理论值存在偏差,主要误差来源:
简易校准步骤
拓展优化:多次采样滑动平均滤波、一阶低通滤波,消除数值抖动。
网硕互联帮助中心




评论前必须登录!
注册