资讯动态

LIS3DH三轴加速度计驱动实战:I2C与SPI双接口配置及避坑指南

发布时间:2026/10/26 16:46:20 来源:尧图企业网站定制
简介这份资源面向嵌入式开发初学者与需要快速集成三轴加速度计的工程师围绕LIS3DH传感器提供完整的软硬件驱动方案重点解决I2C与SPI两种总线接口下的初始化、读写与数据采集问题。压缩包共139个文件约6.05MB以35个h头文件与35个c源文件为核心辅以o、cout、pbi等编译中间文件以及pdf、txt技术文档和hex固件覆盖驱动源码、工程配置与说明资料。资源中已包含IIC2_Init、IIC2_Start、IIC2_Send_Byte、IIC2_Read_Byte、IIC2_Wait_Ack等完整I2C操作函数并配套lis3dh_driver.c与lis3dh_driver_spi.c等SPI驱动实现便于读者直接移植到STM8等平台。目前已有506人学习下载适合需要快速验证传感器通信、理解总线时序与寄存器配置的开发者参考。1. 从一颗 LIS3DH 说起为什么 I2C 和 SPI 两套驱动都得攥在手里前阵子帮朋友调一块三轴加速度计板子现象很邪门I2C 能读出 WHO_AM_I 是 0x33可一读加速度数据就全是 0换成 SPI 接法立马正常。折腾半天才发现是 I2C 上拉电阻选得不对波形上升沿塌成了台阶。这件事让我重新翻出硬盘里那份 LIS3DH 驱动资料包——它同时给了 I2C 和 SPI 两套源码还有 STM8S 的定时器文件正好能把这类玄学问题一次讲透。LIS3DH 是 ST 家的经典三轴加速度传感器量程可配 ±2g 到 ±16g支持 I2C 和 SPI 两种数字接口低功耗模式下电流只有微安级在计步、姿态检测、振动监测里出镜率极高。这份资源包的价值不在于代码多长而在于它把两种接口的底层时序都摊开给你看还附带了 STM8S 平台的工程文件适合刚接触嵌入式传感器驱动的新手照着复现也适合老手拿来对照排查时序问题。下面我就按「这是什么 → 怎么用 → 坑在哪」的顺序把这份资料拆开讲。2. LIS3DH 驱动包的骨架文件构成与接口选型逻辑2.1 压缩包里到底有什么先把包里的东西理清楚不然拿到一堆 .c 和 .h 容易懵。从项目正文看核心文件分三类LIS3DH 驱动层lis3dh_driver.c、lis3dh_driver_spi.c、STM8S 外设层stm8s_tim1.c、stm8s_tim5.c、以及工程调试残留文件SPI_Flash.Debug.cspy.bat、SPI_Flash.pbd.browse 这类。驱动层是重点它把寄存器操作和接口时序分开了lis3dh_driver.c 负责芯片本身的初始化、量程配置、数据读取lis3dh_driver_spi.c 则专门处理 SPI 的字节收发。这种分层写法很实用换平台时只改接口文件寄存器逻辑不用动。文件作用是否需移植lis3dh_driver.c寄存器读写、量程/速率配置、数据解析否逻辑通用lis3dh_driver_spi.cSPI 字节收发、片选控制是依赖 MCUstm8s_tim1.c / tim5.c定时器用于周期采样是平台相关SPI_Flash.*.bat / .browse调试工程残留可删注意那些 .bat 和 .browse 是 IDE 生成的临时文件不影响驱动逻辑移植时直接忽略。真正要盯的是驱动层里对 WHO_AM_I0x0F的读取这是判断通信是否打通的第一道关卡。2.2 I2C 还是 SPI先看板子再选选接口不是拍脑袋得看三个现实条件。第一是引脚数I2C 只要 SCL、SDA 两根线SPI 至少要 SCLK、MOSI、MISO、CS 四根引脚紧张时 I2C 占优。第二是速率LIS3DH 的 I2C 最高 400kHzSPI 能到 10MHz要高频采样比如振动分析SPI 更稳。第三是总线冲突I2C 是共享总线挂多个器件时地址不能撞LIS3DH 的 SAD 引脚决定地址是 0x18 还是 0x19SPI 每片一个 CS互不干扰。我一般这么定慢速姿态检测、引脚少走 I2C高速数据采集、总线上还有别的 SPI 器件走 SPI。这份资料两套都给正好可以按项目切换。下面分别讲两套接口的落地步骤。3. I2C 接口驱动落地从 IO 模拟到数据读取3.1 软件模拟 I2C 的时序骨架资料里的 I2C 部分用的是 IO 模拟摘要里列了一串函数IIC2_Init、IIC2_Start、IIC2_Stop、IIC2_Send_Byte、IIC2_Read_Byte、IIC2_Wait_Ack、IIC2_Ack、IIC2_NAck。这套函数是标准软件 I2C 的骨架好处是不挑 MCU任何有普通 IO 的芯片都能跑。先看初始化和起始信号// 初始化 I2C 的 IO 口配置为开漏输出 void IIC2_Init(void) { GPIO_Init(GPIOB, GPIO_PIN_6 | GPIO_PIN_7, GPIO_MODE_OUT_OD_HIZ_FAST); // SCLPB6, SDAPB7 } // 发送 I2C 起始信号SCL 高时 SDA 由高变低 void IIC2_Start(void) { SDA_HIGH(); // 先拉高数据线 SCL_HIGH(); // 再拉高时钟线 delay_us(2); SDA_LOW(); // SCL 保持高时拉低 SDA产生起始条件 delay_us(2); SCL_LOW(); // 钳住总线准备传输 }逻辑说明I2C 的起始条件是 SCL 为高时 SDA 出现下降沿停止条件相反。delay_us(2) 是给信号建立时间具体值看 MCU 主频太快会导致从机来不及响应。参数上SCL 和 SDA 必须配成开漏输出否则无法实现线与多设备挂载时会烧引脚。3.2 字节收发与 ACK 处理字节收发是 I2C 最容易翻车的地方核心是「高位先发、每字节跟一个 ACK」。发送一个字节的写法// I2C 发送一个字节高位先发 void IIC2_Send_Byte(uint8_t txd) { uint8_t t; SDA_OUT(); // 切换 SDA 为输出 SCL_LOW(); // 拉低时钟准备放数据 for (t 0; t 8; t) { if (txd 0x80) SDA_HIGH(); else SDA_LOW(); txd 1; // 左移准备下一位 delay_us(2); SCL_HIGH(); // 时钟拉高从机采样 delay_us(2); SCL_LOW(); // 时钟拉低准备下一位 delay_us(2); } }逻辑说明循环 8 次每次先放数据再拉高 SCL从机在 SCL 高电平期间采样。发送完 8 位后要调用 IIC2_Wait_Ack 等从机拉低 SDA 应答。如果 Wait_Ack 返回 1说明从机没应答常见原因是地址写错或器件没上电。参数上txd 是要发的字节移位方向必须是左移因为 I2C 规定 MSB 先行。3.3 读 LIS3DH 加速度数据的完整流程通信打通后读数据就三步写寄存器地址、重启始条件、读字节。以读 WHO_AM_I 和 OUT_X_L 为例// 读 LIS3DH 单个寄存器 uint8_t LIS3DH_Read_Reg(uint8_t reg) { uint8_t val; IIC2_Start(); IIC2_Send_Byte(LIS3DH_ADDR 1); // 写地址 IIC2_Wait_Ack(); IIC2_Send_Byte(reg); // 寄存器地址 IIC2_Wait_Ack(); IIC2_Start(); // 重启始条件 IIC2_Send_Byte((LIS3DH_ADDR 1) | 0x01); // 读地址 IIC2_Wait_Ack(); val IIC2_Read_Byte(0); // 读一个字节NACK 结束 IIC2_Stop(); return val; }逻辑说明LIS3DH 的 I2C 地址由 SAD 引脚决定接地是 0x18接 VDD 是 0x19左移一位后最低位是读写位。读操作要先写寄存器地址再重启始条件切读模式。读加速度时OUT_X_L0x28到 OUT_Z_H0x2D是连续 6 个寄存器可以用连续读一次取完效率更高。参数上IIC2_Read_Byte 的参数 0 表示读完后发 NACK1 表示发 ACK 继续读。4. SPI 接口驱动落地模式配置与高速读取4.1 SPI 模式 0 与片选控制LIS3DH 的 SPI 支持模式 0CPOL0CPHA0和模式 3CPOL1CPHA1资料里 lis3dh_driver_spi.c 默认用模式 0。SPI 的关键是片选 CS 必须由软件控制每次传输前拉低、传输后拉高。先看字节收发// SPI 收发一个字节模式 0 uint8_t LIS3DH_SPI_ReadWriteByte(uint8_t txd) { uint8_t rx 0; for (uint8_t i 0; i 8; i) { if (txd 0x80) MOSI_HIGH(); else MOSI_LOW(); txd 1; SCLK_HIGH(); // 模式 0上升沿采样 delay_us(1); if (MISO_READ()) rx | 0x01; // 读 MISO rx 1; SCLK_LOW(); // 下降沿移出下一位 delay_us(1); } return rx; }逻辑说明模式 0 下数据在 SCLK 上升沿被采样下降沿切换。注意 rx 的移位顺序先读位再左移最后补正。如果读出来全是 0xFF 或 0x00先查 CS 有没有拉低、MISO 有没有接对。参数上delay_us(1) 决定 SPI 速率太快时杜邦线会引入振铃降速往往能救回来。4.2 多字节读取与地址自动递增LIS3DH 的 SPI 读操作有个细节寄存器地址的最高位MSB为 1 表示读为 0 表示写。连续读时地址会自动递增所以读 6 个加速度寄存器只需一次片选// 连续读 LIS3DH 多个寄存器 void LIS3DH_Read_Multi(uint8_t reg, uint8_t *buf, uint8_t len) { CS_LOW(); // 片选拉低 LIS3DH_SPI_ReadWriteByte(reg | 0x80); // 读标志位置 1 for (uint8_t i 0; i len; i) { buf[i] LIS3DH_SPI_ReadWriteByte(0x00); // 发空字节收数据 } CS_HIGH(); // 片选拉高结束传输 }逻辑说明reg | 0x80 是 SPI 读操作的地址格式写操作则直接发地址。连续读时芯片内部地址指针自增所以能一次取完 X/Y/Z 六个字节。参数上buf 要提前分配好空间len 别超过寄存器边界否则会读到保留区。读完后把低字节和高字节拼成 int16_t再按量程换算成 g 值。4.3 量程与输出速率配置读数据前得先配 CTRL_REG1 到 CTRL_REG4。常见配置CTRL_REG10x20设 ODR 和使能轴CTRL_REG40x23设量程和分辨率。比如 100Hz 输出、±2g 量程、使能 XYZ// 配置 LIS3DH100Hz±2gXYZ 使能 void LIS3DH_Config(void) { LIS3DH_Write_Reg(0x20, 0x57); // ODR100Hz, LPen0, XYZ enable LIS3DH_Write_Reg(0x23, 0x00); // BDU0, BLE0, FS±2g, HR0 }逻辑说明0x57 拆开是 0101 0111ODR 位是 0101 对应 100Hz低三位 111 使能三轴。0x23 写 0x00 表示连续更新、小端模式、±2g 量程。参数上量程越大分辨率越低±2g 时灵敏度是 1mg/digit±16g 时降到 12mg/digit按实际加速度范围选。5. 避坑与排查I2C 上拉、SPI 模式、时序错位5.1 I2C 波形出现台阶读数据全 0现象示波器看 SDA 上升沿有明显台阶读 WHO_AM_I 正常但读加速度全是 0。原因上拉电阻太大总线电容充电慢高速时电平没到阈值就被采样。解决把上拉从 10k 换成 4.7k 甚至 2.2k同时缩短走线。常见做法是 3.3V 系统用 4.7k5V 系统用 2.2k具体看总线电容。5.2 SPI 读出来全是 0xFF现象SPI 接法下读任何寄存器都返回 0xFF。原因MISO 没接或 CS 没拉低主机读到的是悬空高电平。解决先量 CS 引脚在传输时是否拉低再查 MISO 是否接到从机 SDO。如果 CS 用硬件片选注意有些 MCU 的硬件 CS 在传输间隙会自动拉高导致多字节读断掉这时改软件片选。5.3 加速度数据跳变或卡死现象数据偶尔跳变或者读几次后卡死。原因I2C 时序里 delay 太短从机没来得及应答或者 SPI 速率超过芯片上限。解决把 delay_us 从 1 加到 2 或 5SPI 时钟先降到 1MHz 试。另外检查电源去耦LIS3DH 的 VDD 和 VDD_IO 都要加 100nF 电容电源纹波大会导致内部逻辑误判。5.4 WHO_AM_I 读不到 0x33现象读 0x0F 寄存器返回不是 0x33。原因I2C 地址错SAD 接法不对或 SPI 模式不匹配。解决I2C 下确认 SAD 引脚电平接地 0x18、接 VDD 0x19SPI 下确认 CPOL/CPHA 是模式 0 还是模式 3两种都试一遍。如果还是不对查供电是否到 2.16V 以上LIS3DH 最低工作电压是 1.71V但 1.8V 以下时序会变慢。5.5 移植后编译报错找不到寄存器定义现象换平台后编译提示 LIS3DH_CTRL_REG1 未定义。原因lis3dh_driver.h 里的寄存器宏依赖具体头文件移植时没包含。解决把驱动层的 .h 文件路径加进工程或者把寄存器宏单独抽成一个头文件。那些 .bat 和 .browse 文件不用管它们不参与编译。6. 进阶技巧用 FIFO 和中断把采样做稳LIS3DH 有个很实用的 FIFO 模式能缓存 32 组数据配合中断引脚能大幅降低 MCU 轮询开销。配置 FIFO 用 CTRL_REG50x24和 FIFO_CTRL_REG0x2E。比如设成 Stream 模式FIFO 满 16 组触发水印中断// 配置 FIFOStream 模式水印 16 void LIS3DH_FIFO_Config(void) { LIS3DH_Write_Reg(0x2E, 0x80 | 16); // Stream 模式水印阈值 16 LIS3DH_Write_Reg(0x24, 0x40); // FIFO 使能 }逻辑说明0x2E 的高两位是 FIFO 模式10 表示 Stream低五位是水印阈值。0x24 的 bit6 是 FIFO_EN。配好后 INT1 引脚会在水印达到时输出高电平MCU 用外部中断触发读取一次读 16 组数据。参数上水印阈值别设满 32留点余量防止溢出丢数据。验证方法先读 FIFO_SRC_REG0x2F看 FSS 位确认存了多少组再批量读 OUT_X_L。我一般会写个简单的串口打印把三轴数据按 g 值输出晃动板子看波形是否连续。如果数据断断续续先查 ODR 和 FIFO 模式是否匹配Stream 模式下旧数据会被新数据覆盖FIFO 模式则会停止更新。从那以后我每次调 LIS3DH都强制先读 WHO_AM_I 确认通信再配量程和 ODR最后才开 FIFO 和中断这三步顺序乱了就容易出玄学问题。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑