新闻详情

新闻详情

首页 / 资讯中心 / 详情

STM32C5 SPI驱动IIS3DWB振动计:从CubeMX配置到FIFO读取实战

发布时间:2026/9/28 19:48:48来源:尧图网络
STM32C5 SPI驱动IIS3DWB振动计:从CubeMX配置到FIFO读取实战
1. 拿到IIS3DWB这颗震动计为什么我第一反应是走SPI而不是I2CIIS3DWB 是意法半导体推出的一颗面向工业级振动监测的三轴数字加速度计量程覆盖 ±2g 到 ±16g带宽能拉到 6kHz 以上内部还带一个 3KB 的 FIFO。它的定位很明确机器状态监测、预测性维护、结构健康监测这类需要连续采振动波形的场景。我第一次拿到这块芯片的时候评估板上的通信接口同时引出了 SPI 和 I2C但我几乎没有犹豫就选了 SPI原因很直接——IIS3DWB 在 I2C 模式下最高只能跑到 1MHz 左右的时钟而 SPI 可以轻松跑到 10MHz 甚至更高。对于振动监测来说采样率和数据吞吐量是命门I2C 那点带宽根本喂不饱它。STM32C5 是 ST 新出的 Cortex-M33 内核产品线主频和总线架构相比老的 F103 系列提升明显SPI 外设的时钟源也更灵活。我这次用的板子是 STM32C5 的 Nucleo 开发板配合 IIS3DWB 的评估子板通过飞线把 SPI 四根线接起来。整个链路是STM32C5 做 SPI MasterIIS3DWB 做 SPI Slave模式选 CPOL1/CPHA1也就是 SPI Mode 3这是 IIS3DWB 数据手册里明确要求的。很多人第一次调这颗芯片会卡在时序模式上后面我会专门讲这个坑。这篇文章适合谁看如果你手上有 STM32C5 或者类似的 STM32 新系列芯片想通过 SPI 把 IIS3DWB 的振动数据读出来但不确定 CubeMX 里怎么配、寄存器怎么读写、FIFO 怎么用那这篇内容基本能覆盖你从零到跑通的全过程。我会把 CubeMX 的配置逻辑、SPI 读写函数的封装、WHO_AM_I 验证、单次读取和 FIFO 批量读取都讲清楚同时把我在调试过程中踩过的几个坑原原本本还原出来。注意IIS3DWB 的 SPI 接口不支持三线模式必须用标准的四线 SPISCLK、MOSI、MISO、CSCS 建议用硬件片选软件片选在高速率下容易出问题。2. STM32C5 的 SPI 外设配置CubeMX 里那几个参数到底怎么选2.1 时钟树与 SPI 时钟源的确定打开 STM32CubeMX选好 STM32C5 对应的芯片型号之后第一件事是配时钟树。STM32C5 的 SPI1 挂在 APB2 总线上SPI2/SPI3 挂在 APB1 上。我这次用的是 SPI1因为它的时钟源上限更高。在 Clock Configuration 页面里把 SYSCLK 拉到芯片允许的最高频率具体取决于你用的具体型号我手上这颗跑到了 144MHz然后确认 APB2 的分频系数。假设 APB2 最终是 144MHzSPI1 的波特率预分频器选 16那 SCLK 就是 9MHz。这个速率对 IIS3DWB 来说完全在安全范围内它的 SPI 时钟最高支持 10MHz。这里有个细节值得说STM32C5 的 SPI 外设支持可配置的 FIFO 阈值和 8/16 位数据宽度。IIS3DWB 的寄存器地址是 7 位读写位在最高位数据是 8 位。所以 SPI 数据宽度必须设成 8 位不能设 16 位否则地址和数据会错位。我在第一次配的时候手滑选了 16 位结果读出来的 WHO_AM_I 永远是 0xFFFF排查了半天才发现是这个原因。2.2 SPI 模式与片选管理在 SPI1 的 Parameter Settings 里几个关键参数这样设Frame FormatMotorolaData Size8 BitsFirst BitMSB FirstClock Polarity (CPOL)HighClock Phase (CPHA)2 EdgeNSSHardware NSS Output如果你用硬件片选或者 Disable用软件片选控制 GPIOCPOLHigh、CPHA2Edge 合起来就是 SPI Mode 3。IIS3DWB 数据手册里写的时序图明确是 Mode 3SCLK 空闲高电平数据在第二个边沿采样。如果你配成 Mode 0读出来的数据会整体偏移一位表现为数值乱跳或者固定错位。关于片选我强烈建议用硬件 NSS。STM32C5 的 SPI 外设支持硬件 NSS 输出配置之后片选信号由硬件自动拉低和拉高时序精准不会因为软件干预导致 CS 提前拉高或延后拉低。如果你用软件片选在 9MHz 的速率下GPIO 翻转的延迟可能导致最后一个字节还没移完 CS 就抬起来了数据就丢了。我实测过软件片选在 1MHz 以下问题不大但上了 5MHz 之后误码率明显上升。2.3 DMA 通道的预留虽然这一篇主要讲轮询方式读数据但我在 CubeMX 里还是把 DMA 配上了。SPI1_RX 和 SPI1_TX 各分配一个 DMA 通道模式选 Normal数据宽度 Byte。为什么要提前配因为 IIS3DWB 的 FIFO 一旦启用一次要读几百个字节轮询方式会占满 CPU后面升级到 DMA 就是改几行代码的事。提前把 DMA 配好后面切换的时候不用重新生成工程。配置完成后生成代码CubeMX 会自动生成MX_SPI1_Init()函数。我习惯在这个函数基础上再包一层自己的 SPI 读写函数不直接调 HAL 的HAL_SPI_TransmitReceive因为 IIS3DWB 的读写时序有特殊要求——地址字节的最高位是读写标志位读操作要置 1写操作要置 0。3. IIS3DWB 的 SPI 读写时序地址字节里藏着的读写位3.1 单寄存器读写的底层逻辑IIS3DWB 的 SPI 通信协议不复杂但有一个地方容易搞错它的寄存器地址是 7 位的传输的时候要把地址放在一个字节的低 7 位最高位bit7作为读写控制位。读操作时 bit71写操作时 bit70。也就是说如果你想读WHO_AM_I寄存器地址 0x0F实际发送的字节是0x0F | 0x80 0x8F。写操作则是0x0F 0x7F 0x0F。SPI 的传输过程是这样的CS 拉低先发一个字节的地址带读写位然后紧接着发一个字节的 dummy读操作时或者要写的数据写操作时。读操作时从机在第二个字节的时钟周期把数据放到 MISO 上主机收回来就是寄存器的值。写操作时主机在第二个字节把数据放到 MOSI 上从机接收。我封装了两个函数一个读一个写uint8_t IIS3DWB_ReadReg(uint8_t reg) { uint8_t tx_buf[2]; uint8_t rx_buf[2]; tx_buf[0] reg | 0x80; // 读操作bit7置1 tx_buf[1] 0x00; // dummy byte HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); return rx_buf[1]; } void IIS3DWB_WriteReg(uint8_t reg, uint8_t data) { uint8_t tx_buf[2]; tx_buf[0] reg 0x7F; // 写操作bit7清0 tx_buf[1] data; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(hspi1, tx_buf, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }如果你用的是硬件 NSS那HAL_GPIO_WritePin那两行可以去掉硬件会自动控制。但我个人还是习惯手动控制 CS因为调试的时候用逻辑分析仪抓波形更直观能看到 CS 和 SCLK 的对应关系。3.2 多字节连续读取的实现IIS3DWB 支持地址自动递增读多个连续寄存器的时候只需要发一次地址然后连续读多个字节就行。比如读三轴加速度的 6 个字节X_L、X_H、Y_L、Y_H、Z_L、Z_H地址从OUTX_L_A0x28开始连续读 6 个字节。这里要注意IIS3DWB 的加速度数据是 16 位有符号数低字节在前高字节在后拼的时候要(int16_t)((high 8) | low)。void IIS3DWB_ReadAccel(int16_t *accel) { uint8_t tx_buf[7] {0}; uint8_t rx_buf[7] {0}; tx_buf[0] 0x28 | 0x80; // OUTX_L_A 地址读操作 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, tx_buf, rx_buf, 7, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); accel[0] (int16_t)((rx_buf[2] 8) | rx_buf[1]); accel[1] (int16_t)((rx_buf[4] 8) | rx_buf[3]); accel[2] (int16_t)((rx_buf[6] 8) | rx_buf[5]); }注意tx_buf的长度是 7第一个字节是地址后面 6 个是 dummy。rx_buf[0]是发地址时收回来的垃圾数据从rx_buf[1]开始才是有效数据。这个偏移量很容易搞错我第一次写的时候把rx_buf[0]当成了 X_L结果读出来的值整体错了一位。3.3 用逻辑分析仪验证时序调 SPI 最怕的就是时序不对但代码看起来没问题。我的做法是拿一个逻辑分析仪或者带 SPI 解码功能的示波器抓 CS、SCLK、MOSI、MISO 四根线。重点看几个地方CS 拉低之后第一个 SCLK 边沿是不是在半个周期之后出现地址字节的 bit7 是不是正确的读写位MISO 上的数据是不是在第二个边沿稳定。我抓过一次波形发现 CS 拉低和第一个 SCLK 之间几乎没有延迟导致从机还没准备好就收到了时钟读出来的数据全是 0xFF。后来在 CS 拉低之后加了一个微秒级的延时问题就解决了。虽然 IIS3DWB 手册上说 CS 建立时间只要 10ns但实际板子上有走线电容留点余量更稳。4. 从 WHO_AM_I 到 FIFO分阶段验证你的 SPI 链路4.1 第一步永远是读 WHO_AM_I不管调什么传感器我第一步永远是读 WHO_AM_I 寄存器。IIS3DWB 的WHO_AM_I地址是 0x0F返回值应该是 0x6B。如果你读出来不是 0x6B那说明 SPI 链路有问题后面的寄存器配置都不用看了。我遇到过几种情况读出来是 0x00说明 MISO 没接好或者从机没供电读出来是 0xFF说明 MISO 被拉高了可能是 CS 没拉低或者 SPI 模式不对读出来是 0x6B 但偶尔跳变说明时序余量不够需要降速或者加延时。uint8_t whoami IIS3DWB_ReadReg(0x0F); if (whoami 0x6B) { printf(IIS3DWB detected!\n); } else { printf(WHO_AM_I 0x%02X, expected 0x6B\n, whoami); }这一步过了心里就有底了。接下来配置CTRL1_XL寄存器设置量程和输出数据率。IIS3DWB 的CTRL1_XL地址是 0x10bit7-4 是 ODR 设置bit3-2 是量程设置。我一般先用 1.6kHz 的 ODR 和 ±4g 的量程做验证这个组合比较中庸不容易出问题。4.2 单次读取验证数据通路配置完CTRL1_XL之后等个几毫秒让传感器稳定然后读STATUS_REG地址 0x1E看XLDA位是不是置 1。如果置 1说明有新数据了这时候读OUTX_L_A开始的 6 个字节就能拿到加速度值。我习惯把原始值转成 mg 来验证±4g 量程下灵敏度是 0.122 mg/LSB所以accel_mg raw * 0.122。把板子平放Z 轴应该接近 1000mgX 和 Y 接近 0。如果 Z 轴是负的说明传感器贴反了或者坐标轴定义和你的预期不一致。这一步能过说明 SPI 读写、寄存器配置、数据解析都是对的。但单次读取有个问题它依赖STATUS_REG的轮询在高速 ODR 下会丢数据。比如 ODR 设到 6.6kHz你轮询的速度根本跟不上读到的永远是旧数据。所以真正做振动监测的时候必须用 FIFO。4.3 FIFO 批量读取的配置与陷阱IIS3DWB 内部有一个 3KB 的 FIFO可以存 512 组三轴数据每组 6 字节。配置 FIFO 涉及几个寄存器FIFO_CTRL1到FIFO_CTRL4还有CTRL4_INT1之类的引脚配置。我一般用 Stream 模式或者 FIFO 模式Watermark 设到 256 组这样半满的时候触发中断一次性读 256 组数据CPU 占用率很低。配置 FIFO 的时候有个坑FIFO_CTRL4里的FIFO_MODE字段必须最后设置因为一旦设成 FIFO 模式FIFO 就开始工作了这时候再改其他参数可能会丢数据。正确的顺序是先设 Watermark再设 ODR最后设 FIFO_MODE。我一开始没注意顺序先把 FIFO_MODE 设成了 Stream然后去改 Watermark结果 FIFO 里的数据全乱了。读 FIFO 的时候地址从FIFO_DATA_OUT_TAG0x78开始每个数据组有一个 Tag 字节加 6 个数据字节一共 7 个字节。Tag 字节的高 5 位是标签低 3 位是计数。连续读的时候每 7 个字节解析一组。这里要注意SPI 读 FIFO 的时候地址发完之后要连续读 N*7 个字节中间不能断 CS否则 FIFO 指针会复位。#define FIFO_WATERMARK 256 uint8_t fifo_buf[FIFO_WATERMARK * 7 1]; fifo_buf[0] 0x78 | 0x80; // FIFO_DATA_OUT_TAG HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(hspi1, fifo_buf, fifo_buf, FIFO_WATERMARK * 7 1, 1000); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); // 解析从 fifo_buf[1] 开始每 7 个字节一组 for (int i 0; i FIFO_WATERMARK; i) { uint8_t tag fifo_buf[1 i * 7]; int16_t x (int16_t)((fifo_buf[2 i * 7] 8) | fifo_buf[1 i * 7 1]); // ... 解析 Y 和 Z }提示FIFO 读取的缓冲区大小要算准FIFO_WATERMARK * 7 1是必须的那个 1 是地址字节。少一个字节就会越界我因为这个原因调试了一下午。5. 调试过程中踩过的三个坑和对应的排查思路5.1 坑一SPI 模式配错导致数据整体偏移第一次读WHO_AM_I的时候返回值是 0xD6正好是 0x6B 左移一位。这个现象很典型就是 SPI 模式配错了。IIS3DWB 要求 Mode 3我配成了 Mode 0导致数据在错误的边沿被采样整体偏移了一位。把 CPOL 改成 High、CPHA 改成 2Edge 之后WHO_AM_I立刻变成了 0x6B。这个坑的排查思路是如果读出来的值是预期值的移位版本优先检查 SPI 模式。5.2 坑二CS 拉高太早导致最后一个字节丢失用软件片选的时候我在HAL_SPI_TransmitReceive返回之后立刻拉高了 CS。但在 9MHz 的速率下HAL 函数的返回和最后一个 SCLK 边沿之间可能有几十纳秒的延迟CS 提前拉高会让从机认为传输结束最后一个字节就丢了。表现是读多字节数据的时候最后一个字节永远是 0x00 或者 0xFF。解决办法有两个一是改用硬件 NSS二是拉高 CS 之前加一个__NOP()或者几微秒的延时。我最后改成了硬件 NSS问题彻底消失。5.3 坑三FIFO 读取时 CS 中断导致指针复位这个坑最隐蔽。我在读 FIFO 的时候为了打印调试信息在读取过程中插入了一个printf结果printf耗时太长CS 在传输过程中被其他中断打断拉高了一下又拉低。IIS3DWB 的 FIFO 指针在 CS 拉高的时候会复位所以第二次拉低之后读到的数据是从 FIFO 头部重新开始的导致数据重复。排查这个问题花了我最久的时间因为逻辑分析仪抓到的波形看起来只是 CS 上有一个毛刺很容易忽略。后来我把 FIFO 读取放在了一个关中断的临界区里问题就解决了。现象可能原因排查方法解决方案WHO_AM_I 返回 0x00MISO 未连接或从机未供电检查硬件连线测量从机供电重新焊接或更换板子WHO_AM_I 返回 0xFFCS 未拉低或 SPI 模式错误逻辑分析仪抓 CS 和 SCLK检查 CS 控制逻辑确认 Mode 3数据整体偏移一位SPI 模式配错对比预期值和实际值的二进制改为 CPOL1, CPHA1多字节读取最后一字节丢失CS 拉高太早抓 CS 和最后一个 SCLK 的时序改用硬件 NSS 或加延时FIFO 数据重复传输过程中 CS 被中断打断抓 CS 波形看是否有毛刺关中断或改用 DMA6. 从轮询到 DMA下一步的优化方向轮询方式读 FIFO 在 1.6kHz ODR 下勉强够用但 ODR 拉到 6.6kHz 的时候CPU 大部分时间都在等 SPI 传输完成根本干不了别的事。我下一步的计划是改成 DMA 模式SPI1_RX 用 DMA 通道FIFO 的 Watermark 中断触发 DMA 传输传输完成中断里再解析数据。这样 CPU 只在数据搬完之后介入一次占用率能降到 5% 以下。DMA 配置的时候要注意SPI 的 DMA 请求要在HAL_SPI_TransmitReceive_DMA之前使能而且 DMA 的传输长度要算准。IIS3DWB 的 FIFO 读取是单向的只读所以其实只需要 RX DMATX 那边发完地址字节之后就可以不管了。但 HAL 库的HAL_SPI_TransmitReceive_DMA要求 TX 和 RX 都配 DMA所以我一般用HAL_SPI_Receive_DMA配合手动发地址的方式或者干脆用 LL 库直接操作寄存器更灵活。另外STM32C5 的 SPI 支持 FIFO 阈值中断可以设成 RX FIFO 达到 8 字节就触发中断这样配合 DMA 的循环模式可以实现连续不断的采集。不过这是后话了这一篇先把轮询方式跑通把数据链路验证清楚后面再折腾 DMA 就有底气了。我在实际使用中发现IIS3DWB 这颗芯片的 SPI 接口虽然标称支持 10MHz但在长走线或者飞线的情况下7-8MHz 更稳。如果你发现高速下数据偶尔出错不妨先把 SCLK 降到 5MHz 试试确认链路没问题之后再逐步往上提。振动监测这个场景数据完整性比速度更重要宁可降速也不能丢数据。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

写论文时的那些小尴尬 —— 谁还没出过洋相 2026/9/28 20:35:35

写论文时的那些小尴尬 —— 谁还没出过洋相

写论文过程中,谁还没出过几个小洋相?这些尴尬事说出来,大家都一样,你不是一个人。汇写(https://www.huixielunwen.com/tool/graduationThesis)帮你避免了一些技术尴尬,但有些还是要靠自己。 尴…

阅读更多 →
17 嵌入式操作系统 | uloop 事件循环:TCP 客户端 2026/9/28 20:35:34

17 嵌入式操作系统 | uloop 事件循环:TCP 客户端

嵌入式操作系统 | uloop 事件循环:TCP 客户端 本课程开源地址(Gitee):https://gitee.com/fujianxinxi/qianrushixitongyingyongkaifa.git 课件、示例代码与验收脚本都在该仓库,可直接 git clone 或下载 ZIP 使用。 模块…

阅读更多 →
Spirula Studio内置SfM揭秘:如何在一个进程里彻底替代COLMAP 2026/9/28 20:35:34

Spirula Studio内置SfM揭秘:如何在一个进程里彻底替代COLMAP

Spirula Studio内置SfM揭秘:如何在一个进程里彻底替代COLMAP 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio S…

阅读更多 →
投稿前最后防线:用research-writing-skill的peer-review技能完成投稿前自审清单 2026/9/28 20:35:21

投稿前最后防线:用research-writing-skill的peer-review技能完成投稿前自审清单

投稿前最后防线:用research-writing-skill的peer-review技能完成投稿前自审清单 【免费下载链接】research-writing-skill 科研写作助手 (Research Writing Assistant) 项目地址: https://gitcode.com/gh_mirrors/re/research-writing-skill 论文写完到正式投…

阅读更多 →
网课录音笔记效率翻倍!2024年4款宝藏工具实测横评,从学生党到考研党都适用 2026/9/28 20:35:21

网课录音笔记效率翻倍!2024年4款宝藏工具实测横评,从学生党到考研党都适用

你是不是也经历过这种场景?网课直播时老师语速飞快,光顾着听讲来不及记笔记,课后回看视频又找不到重点;录播课好不容易听完,却发现满屏知识点根本不知从哪下笔复习;考研、考公辅导班线下课挤满人&#xff0…

阅读更多 →
设备管理:数据传输控制方式与磁盘调度算法 2026/9/28 20:35:21

设备管理:数据传输控制方式与磁盘调度算法

AI模型:Deepseek仅供参考Lets Go!我是分割线数据传输控制方式:在计算机中,数据传输控制方式按 CPU 干预程度从高到低、并行性从低到高,常见例子如下:1. 程序控制方式(轮询) CPU 不断查询设备状…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉