ADXL345调试

ADXL345 是亚德诺半导体(ADI)出品的一款 3 轴数字加速度计(三轴陀螺仪/重力传感器)

它测量的是加速度 (单位通常是 g,即重力加速度,1g ≈ 9.8m/s²)。它可以感知物体的倾斜角度运动状态(静止、走路、震动、跌落)。

测量原理

硅片内部有一个弹簧,另一端悬挂着一块可移动的质量块,在质量块的上方和下方(或两侧),分别固定着两块金属板。质量块和这些金属板构成了电容

  • 当芯片静止时,质量块处于中心位置,两边的电容值相等。

  • 当芯片倾斜(受重力)加速/减速时,由于惯性,质量块会发生位移。

  • 质量块靠近一边的金属板,这一边的电容增大;远离另一边的金属板,那一侧的电容减小。这就形成了差动电容(ΔC)

倾斜板子,弹簧与质量块移动,产生微小的电容变化

质量块产生的电容变化微乎其微(皮法级别),必须经过内部的精密电路处理。

  • 电容-电压转换 :内部有一个高精度的电荷放大器 ,它把差动电容的变化量(ΔC)精确地转换为一个模拟电压信号。这个电压信号的大小,直接对应了加速度的大小。

  • 信号滤波:内部集成了低通滤波器,用于去除高频噪声(比如外部轻微的机械抖动),保证输出的模拟电压平滑且真实。

  • 模数转换(ADC) :滤波后的模拟电压被送入内部的 13 位(或 16 位输出)逐次逼近型 ADC 。它把模拟电压转化为 有符号的二进制补码。此时,真实的"物理加速度"就变成了电脑能处理的"数字信号"。

微小的电压被内部的**"放大器和 ADC"**转成了 16 位的有符号整数。

ADC 转换出来的原始数据,并没有直接送到总线上,而是先经过了内部的"指挥中心"。

  • 数据寄存器 :转换后的数据被存入 6 个 8 位寄存器(0x32 ~ 0x37,分别对应 X/Y/Z 轴的高低字节)。

  • FIFO 缓冲区(数据缓存) :ADXL345 自带一个 32 级 FIFO(先进先出)。这是它最聪明的地方。

    • 假设您的 MCU 很忙,没有及时来读取数据。ADXL345 会把最新测得的数据存入 FIFO 里,最多可以存 32 个。等您的 MCU 闲下来了,可以一次性把 32 个数据读走,保证数据不丢失。这在"高速运动"监控中尤为重要。
  • 中断控制逻辑:内部可以配置阈值(比如"当加速度大于 ±4g 时触发")。一旦发生剧烈震动或自由落体,内部逻辑会立即让外部引脚(INT1 / INT2)产生电平翻转,主动"唤醒"主控去读数据。

数据在内部的**"寄存器"** 或**"FIFO"**里放好

通过 SPI 或 I2C 把 16 位整数拿回去,除以 256,得到真实的加速度 g 值。

具体参数

参数项 具体规格
通信接口 I2C (最大 400kHz) 或 SPI (最大 5MHz)
测量范围 可编程选择:±2g, ±4g, ±8g, ±16g
分辨率 13 位(即原始数据范围是 -2048 ~ +2047)
供电电压 2.0V ~ 3.6V (可直接接 STM32 的 3.3V,切记不能接 5V
超低功耗 仅需 23 µA(非常适合电池供电设备)
内置功能 自带 32 级 FIFO 缓存(可以一次性存 32 个采样数据,减轻 MCU 的负担)

引脚说明

|----------|--------------------|--------------------------|--------------------------------------|--------------------------------------------------------|
| VCC | 电源正极 | 供电 | 供电 | 接 3.3V (严禁接 5V) |
| GND | 接地 | 共地 | 共地 | 接 GND |
| CS | 片选信号 (Chip Select) | 由主控控制。必须拉低 (0V) 才能通信 | 必须接高电平 (3.3V)(或悬空) | SPI:接 GPIO (如 PA4) I2C:接 3.3V |
| SCL | 串行时钟 | SPI 时钟 (SCLK) | I2C 时钟 (SCL) | SPI:接 SPI1_SCK (如 PA5) I2C:接 I2C1_SCL (如 PB6) |
| SDA | 串行数据 | SPI 主机输出 (MOSI) | I2C 双向数据线 | SPI:接 SPI1_MOSI (如 PA7) I2C:接 I2C1_SDA (如 PB7) |
| SDO | 串行数据输出 / 地址选择 | SPI 从机输出 (MISO) | I2C 地址选择 : 接地(0)=0x53,接高(1)=0x1D | SPI:接 SPI1_MISO (如 PA6) I2C:接地 或 接 3.3V |
| INT1 | 中断输出 1 | 可选中断 | 可选中断 | 默认悬空(需要时接 GPIO) |
| INT2 | 中断输出 2 | 可选中断 | 可选中断 | 默认悬空(需要时接 GPIO) |

对比维度 SPI 模式(您决定用的) I2C 模式
通信线数量 4 根线 (CS, SCLK, MOSI, MISO) 2 根线 (SCL, SDA)
通信速度 (最高 5MHz) 慢 (通常 100kHz~400kHz)
CS 引脚状态 必须接主控 GPIO,由代码手动拉低/拉高 接 3.3V(高电平)
SDO 引脚状态 必须接 SPI1_MISO 接地(地址 0x53)或接高(地址 0x1D)
代码复杂程度 需要配置 CS 引脚和 SPI 寄存器 只需要配置 I2C,发送地址和数据
适合场景 需要高频率、大量、连续读取数据(如姿态解算) 接线简单,低速监测(如温和的倾角监测)

关于姿态检测的场景可能需要高采样低延迟的条件,建议使用spi

时序图

寄存器相关

小端存储 ,且输出的是补码,因为加速度既有大小又有方向,物理世界存在负数。补码是计算机内部表示有符号数的最标准方式,它让 CPU 做加减法时无需额外逻辑。

连接方式

设备树

时钟相位写了就是1,不写默认为0,这里要选模式3,全写1

|------------|--------|--------|-------------------------|
| 1. 定精度 | 0x31 | 0x08 | 设为 ±2g 量程,全分辨率(灵敏度 256) |
| 2. 定速度 | 0x2C | 0x0A | 设为 100Hz 输出速率 |
| 3. 唤醒 | 0x2D | 0x08 | 开启测量,传感器开始工作 |

驱动

复制代码
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include "asm/uaccess.h"
#include "linux/export.h"

#define DEV_NAME "ptadxl345"

ssize_t myread(struct file *, char __user *, size_t, loff_t *);
int myopen(struct inode *, struct file *);
int myrelease(struct inode *, struct file *);
int myprobe(struct spi_device *spi);
int myremove(struct spi_device *spi);

struct spi_device *pspi;

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = myopen,
    .release = myrelease,
    .read = myread,

};
struct of_device_id of_table[] = {{
                                      .compatible = "pute,puteadxl345",
                                  },
                                  {}};
struct spi_device_id id_table[] = {{
                                       .name = "puteadxl345",
                                   },
                                   {}};

static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEV_NAME,
    .fops = &fops,
};
ssize_t myread(struct file *f, char __user *userdata, size_t size, loff_t *off)
{
  int ret = 0;
  // 1000 0000 read
  // 0100 0000  multi byte
  char reg_add = 0x32 | 0x80 | 0x40;
  char rxbuf[6] = {0};
  spi_write_then_read(pspi, &reg_add, 1, rxbuf, 6);
  ret = copy_to_user(userdata, rxbuf, sizeof(rxbuf));
  if (ret)
  {
    pr_err("myread error,%d\n", ret);
    return ret;
  }

  return sizeof(rxbuf);
}
int myopen(struct inode *node, struct file *f)
{
  // adxl345 init
  char txbuf[2] = {0x31, 0x08};
  char rxbuf[2] = {0};
  // 全分辨率 ±2g
  spi_write_then_read(pspi, txbuf, 2, rxbuf, 0);
  // 输出速率 100 Hz
  txbuf[0] = 0x2c;
  txbuf[1] = 0x0a;
  spi_write_then_read(pspi, txbuf, 2, rxbuf, 0);

  // 开始测量
  txbuf[0] = 0x2d;
  txbuf[1] = 0x08;
  spi_write_then_read(pspi, txbuf, 2, rxbuf, 0);
  pr_info("adxl 345 init ok\n");

  return 0;
}
int myrelease(struct inode *node, struct file *f)
{
  pr_info("close called\n");
  return 0;
}
int myprobe(struct spi_device *spi)
{
  int ret = 0;
  pspi = spi;
  ret = misc_register(&misc_dev);
  if (ret < 0)
  {
    pr_err("myprobe misc_register\n");
    return ret;
  }
  pr_info("adxl 345 misc_register probe ok\n");
  return 0;
}
int myremove(struct spi_device *spi)
{
  misc_deregister(&misc_dev);
  return 0;
}

struct spi_driver spi_drv = {
    .probe = myprobe,
    .remove = myremove,
    .driver =
        {

            .name = "puteadxl345",
            .owner = THIS_MODULE,
            .of_match_table = of_table,
        },
    .id_table = id_table,

};

module_spi_driver(spi_drv);
MODULE_LICENSE("GPL");

应用层处理

复制代码
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
  int fd = open("/dev/ptadxl345", O_RDWR);
  if (fd < 0)
  {
    perror("open");
    return 1;
  }
  char buf[10] = {0};
  short adxl_x = 0;

  short adxl_y = 0;
  short adxl_z = 0;
  while (1)
  {
    read(fd, buf, 6);
    adxl_x = buf[1] << 8 | buf[0];
    adxl_y = buf[3] << 8 | buf[2];
    adxl_z = buf[5] << 8 | buf[4];

    // printf("x:%d y:%d z:%d\n", adxl_x, adxl_y, adxl_z);

    float gx, gy, gz;

    gx = adxl_x / 256.0f;
    gy = adxl_y / 256.0f;
    gz = adxl_z / 256.0f;

    printf("x: %.2f g, y: %.2f g, z: %.2f g\n", gx, gy, gz);
    sleep(1);
  }

  close(fd);

  // system("pause");
  return 0;
}

结果:

遇到的问题:

1.最后数据处理时,直接除以256,导致小数部分遗弃

因为C 语言规则 :两个整数相除,结果还是整数(向下取整,直接丢掉小数部分)。

解决办法很简单,写成256.0f或者

复制代码
gx = (float)adxl_x / 256;

当整数与浮点数做除法时,整数会自动被提升(隐式转换)为浮点数,然后进行浮点数除法。

2.收集到的数据adxl_x必须是short类型

3.把初始化放到了read里,拖累了总线效率

相关推荐
LCG元1 小时前
STM32 软件模拟 I2C 驱动 AT24C02 EEPROM:从时序原理到实测读写全记录
stm32·单片机·嵌入式硬件
振南的单片机世界2 小时前
终端电阻硬核拆解:不接120Ω,RS485信号“撞墙反弹”乱码
arm开发·stm32·单片机·嵌入式硬件
恒锐丰科技韩生2 小时前
SS6625E|3.0‑20V 单通道 16A 峰值 H 桥直流电机驱动 SOP8 门锁玩具机器人专用
嵌入式硬件·硬件工程
国产HT1621B2 小时前
外挂 YL1621 SSOP48 芯片 vs MCU 内置 LCD 控制器深度对比
科技·单片机·嵌入式硬件·mcu·lcd驱动
zlinear数据采集卡2 小时前
数据采集卡从入门到精通(42):项目实战三——设备预测性维护系统,从布点到预警
开发语言·单片机·嵌入式硬件·安全·fpga开发
嵌入式阿蔡13 小时前
传感器_03:电机驱动——让 GPIO 当指挥官,别让 GPIO 当电源
网络·stm32·单片机·嵌入式硬件·嵌入式实时数据库
国科安芯16 小时前
ASL706S:让 MCU 不再“失忆跑飞“的守护芯片
分布式·单片机·嵌入式硬件·安全·fpga开发·架构
树的枝18 小时前
【STM32】03.中断和EXTI外设
笔记·stm32·单片机·嵌入式硬件
单片机杂货铺19 小时前
【单片机毕业设计选题】基于stm32单片机儿童公路车速度检测报警
stm32·单片机·嵌入式硬件·51单片机·硬件工程·课程设计