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严禁接 5VGND接地共地共地接GNDCS片选信号 (Chip Select)由主控控制。必须拉低 (0V) 才能通信必须接高电平 (3.3V)或悬空SPI接GPIO(如 PA4)I2C接3.3VSCL串行时钟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)0x1DSPI接SPI1_MISO(如 PA6)I2C接地 或 接 3.3VINT1中断输出 1可选中断可选中断默认悬空需要时接 GPIOINT2中断输出 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全写11. 定精度0x310x08设为 ±2g 量程全分辨率灵敏度 2562. 定速度0x2C0x0A设为 100Hz 输出速率3. 唤醒0x2D0x08开启测量传感器开始工作驱动#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里拖累了总线效率