
上一期把STM32C5的开发环境、时钟树和最小工程跑通之后轮到真正的硬骨头了让IIS3DWB这颗震动传感器在IIC总线上开口说话。IIS3DWB是一款面向工业状态监测的宽带宽加速度计最大采样率能到26.7kHz带宽6.4kHz专门用来捕捉轴承振动、齿轮箱异常这类高频信号STM32C5则是ST新推出的Cortex-M33系列MCU定位正好卡在低成本与高性能之间。这篇博文重点讲怎么用C5的I2C1外设把IIS3DWB的原始震动数据读出来从寄存器配置到读取代码再到问题排查都会覆盖正在做振动分析、预测性维护或者刚接触IIS3DWB的工程师可以直接照着抄。1. IIS3DWB振动传感器要点梳理1.1 为什么偏偏选它工业振动监测有个麻烦普通加速度计带宽做不高三五百赫兹就到顶了而轴承故障特征频率经常跑到几kHz以上齿轮啮合频率更是轻松过万。IIS3DWB就是为了这个场景设计的它的输出数据速率最高26.7kHz平坦带宽6.4kHz能直接覆盖绝大多数旋转机械的早期故障频段。另外一个实用点在于它仍然是标准MEMS加速度计接口量程可选±2g/±4g/±8g/±16gIIC地址固定为0x5ASDO/SA0接GND时寄存器布局和LIS3DH这一脉很接近。这意味着你完全可以把以前写LIS3DH、IIS2DH的代码框架搬过来只改寄存器和时序细节上手的心理负担小很多。1.2 必须提前认识的寄存器拿数据手册对照着看常用的寄存器就那么几个WHO_AM_I0x0F身份寄存器IIS3DWB固定返回0x4A。上电后第一件事就是读它读出来的值和预期不符就别往下走了先把通信链路搞定再说。CTRL10x20主控制寄存器ODR位段在bit7:4带宽控制在bit3:2量程FS在bit1:0。不同驱动代码对这里的位定义表述略有差异实际配置时以你手上数据手册的CTRL1描述为准。CTRL20x21包含BDU块数据更新、IF_ADD_INC寄存器地址自增。BDU务必置1否则读取高低字节时数据可能在中间被更新造成错位数据。STATUS0x27bit0/bit1/bit2分别是X/Y/Z轴数据就绪标志读取前先轮询它能避免拿到半更新的数据。OUT_X_L0x28到OUT_Z_H0x2D6个连续字节按小端存放三轴16位带符号数据。我习惯把传感器放在一个独立头文件里管理地址和常用指令全部宏定义看起来只是多写了三行实际调试的时候能省很多翻手册的时间。2. STM32C5的IIC外设与硬件设计2.1 C5和G4的I2C差异别照着老代码硬抄现在网上搜STM32C5最常被拿来对比的就是G4。就I2C外设而言两者大体同源都支持FM模式最高1MHz也都带数字滤波和模拟滤波但C5毕竟是新内核新外设TIMINGR寄存器的建议初始值、时钟树分频关系都和G4不一样。用CubeMX生成初始化代码时C5会自动按你选的速率填好TIMINGR这时候别手贱去把G4项目的值直接粘过来内部时钟源不同填错的结果就是SCL波形时序不对传感器一言不合就NACK。C5的I2C还有一个好处是支持从standby模式快速唤醒这对电池供电的无线振动节点很关键。普通传感器巡检设备当然无所谓靠这个特性倒是能实现在低功耗轮询模式下让I2C总线上电即通不用等外设重新初始化。2.2 IIC为什么必须用开漏输出很多刚接触IIC的人都会问SCL和SDA为什么不能配成推挽输出答案是协议设计上就是按“线与”逻辑来的。IIC总线上可以挂多个主机多个从机任何设备都可以把总线拉低如果某个设备推挽输出高电平、另一个设备同时拉低总线直接短路轻则波形畸变重则烧引脚。所以GPIO必须配置为开漏输出AF_OD再靠外部上拉电阻把总线拉到高电平。这也是IIC和SPI在电气规范上最根本的区别——SPI是单主机推挽输出没问题IIC是多主机仲裁协议开漏是底线。2.3 上拉电阻到底取多大热词里有人问“IIC上拉电阻取多大”这个问题没有统一答案但可以算出来。两个边界公式最小电阻由灌电流决定Rp(min) (VDD - VOL(max)) / IOL以3.3V供电、VOL(max)按0.4V、IOL按3mA算Rp(min) (3.3 - 0.4) / 0.003 ≈ 967Ω所以不能小于1kΩ。最大电阻由上升时间决定tr 0.8473 × Rp × Cb400kHz快速模式要求tr不超过300ns假设总线上传感器加连线电容大约100pF那么Rp(max) 300e-9 / (0.8473 × 100e-12) ≈ 3.5kΩ。结论就是400kHz下取1kΩ到3.3kΩ之间兼顾功耗和波形2.2kΩ最稳妥。如果跑100kHz标准模式对上升时间的要求放宽到1μs上拉可以取到4.7kΩ甚至10kΩ功耗更低但波形边沿明显变缓。实测下来IIS3DWB和C5之间距离在10cm以内2.2kΩ在400kHz下波形干净利落直接照这个选就行。3. IIC初始化与配置流程3.1 CubeMX里的关键配置我用CubeMX新建C5工程时外设配置顺序是固定的先把RCC里的HSE打开再配I2C1I2C1 Speed Mode选择Fast Mode勾选Fast Mode Plus如果传感器支持且布线够短Timing Configuration里Target Speed填400000 HzCubeMX会自动算好TIMINGR值GPIO PC0/PC1设为Alternate Function Open-Drain上拉选择Pull-up但注意内部上拉只是辅助外部2.2kΩ电阻必须焊上C5在CubeMX里可以看到GPIO自动翻转速度建议FM模式下建议选Very High否则边沿太慢和上拉电阻配合不好时会产生振铃。3.2 时钟占空比为什么会影响通信STM32的I2C用TIMINGR寄存器里的SCLL和SCLH分别控制SCL低电平和高电平时间SCLL/(SCLLSCLH)就是SCL的占空比。标准模式推荐接近50%比50%快速模式有些芯片推荐高:低1:2甚至1:3这跟器件的输入门槛和建立保持时间要求有关。实际操作中我遇到过一次匪夷所思的问题IIS3DWB能正常应答WHO_AM_I但批量读六个数据字节时最后一个字节总出错。查波形发现SCL占空比严重失衡高电平时间太短传感器在SCL高电平期间还没来得及把数据稳定输出就被C5采走了。CubeMX自动生成的值在正常分频下没问题但如果你手动改了AHB分频或I2C外设时钟源一定要回来看一眼TIMINGRSCLL别小于手册建议的最小低电平时间。3.3 寄存器读写基础函数IIS3DWB用的是8位寄存器地址直接套HAL库的Mem接口最方便#define IIS3DWB_I2C_ADDR 0x5A #define IIS3DWB_WHO_AM_I 0x0F #define IIS3DWB_CTRL1 0x20 #define IIS3DWB_CTRL2 0x21 #define IIS3DWB_STATUS 0x27 #define IIS3DWB_OUT_X_L 0x28 static I2C_HandleTypeDef *sensor_i2c hi2c1; uint8_t IIS3DWB_ReadReg(uint8_t reg) { uint8_t data 0; HAL_I2C_Mem_Read(sensor_i2c, (IIS3DWB_I2C_ADDR 1), reg, I2C_MEMSIZE_8BIT, data, 1, 100); return data; } void IIS3DWB_WriteReg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(sensor_i2c, (IIS3DWB_I2C_ADDR 1), reg, I2C_MEMSIZE_8BIT, val, 1, 100); } void IIS3DWB_ReadXYZ(int16_t *x, int16_t *y, int16_t *z) { uint8_t buf[6]; HAL_I2C_Mem_Read(sensor_i2c, (IIS3DWB_I2C_ADDR 1), IIS3DWB_OUT_X_L, I2C_MEMSIZE_8BIT, buf, 6, 100); *x (int16_t)((buf[1] 8) | buf[0]); *y (int16_t)((buf[3] 8) | buf[2]); *z (int16_t)((buf[5] 8) | buf[4]); }这里有两个细节容易踩坑地址左移一位是因为HAL的Mem函数参数需要带上R/W位多字节读取时一定要确认CTRL2里的IF_ADD_INC已经置1否则传感器不会自动把寄存器地址递增到下一个轴你会不停读到同一个轴的重复数据。4. 读取震动数据的完整实现4.1 上电自检先读WHO_AM_I初始化第一步永远是读身份寄存器这一步通过就说明IIC链路、地址、上拉电阻都没问题uint8_t who IIS3DWB_ReadReg(IIS3DWB_WHO_AM_I); if (who ! 0x4A) { // 打印错误检查接线和地址 }实测中如果返回值是0xFF大概率是上拉没焊或者地址不对如果返回0x00往往是SDA被某个设备拉死了或者传感器供电没起来。先把这两个方向查清楚再谈后续。4.2 配置CTRL1进入连续测量模式IIS3DWB上电默认是掉电模式必须写完CTRL1才开始输出数据。我推荐的一组起步配置是26.7kHz采样率、±2g量程、带宽拉满对应的CTRL1写入0x72左右具体数值对照手册的ODR位段定义确认。void IIS3DWB_Init(void) { // 使能BDU和地址自增 IIS3DWB_WriteReg(IIS3DWB_CTRL2, 0x04 | 0x10); // 设置ODR 26.7kHz, FS ±2g IIS3DWB_WriteReg(IIS3DWB_CTRL1, 0x72); }BDU对应CTRL2的bit2置1后高低字节锁定更新能避免读出“半个新数据半个旧数据”的错位。IF_ADD_INC对连续读取很重要一定要和BDU一起开了。4.3 轮询STATUS再读数传感器数据就绪后STATUS寄存器对应位置1先轮询再读取读取逻辑很清晰void IIS3DWB_ReadData(void) { uint8_t status 0; do { status IIS3DWB_ReadReg(IIS3DWB_STATUS); } while ((status 0x07) 0); int16_t x, y, z; IIS3DWB_ReadXYZ(x, y, z); }如果读到的数据波动看起来像噪声先别怀疑传感器坏了把代码改成循环读STATUS观察连续两个周期的数据是否稳定递增。很多时候问题出在读取频率和ODR不匹配CPU轮询太快会把还没更新的数据反复读出来。4.4 原始数据换算成mg16位数据是带符号整数换算成实际加速度要看量程对应的灵敏度。±2g量程下每LSB对应0.061mg±4g对应0.122mg±8g对应0.244mg±16g对应0.488mg。所以我读出来的原始值要先乘灵敏度系数再参与后续计算float x_mg (float)x * 0.061f; float y_mg (float)y * 0.061f; float z_mg (float)z * 0.061f;静置时三轴合成应该接近1g其中Z轴大约显示1000mg附近X/Y接近0。震动状态下看XYZ方向的AC分量如果敲击桌面时对应轴的数值瞬时跳动超过500mg说明传感器链路已经完整跑通了。5. 常见问题与排查技巧实录5.1 读WHO_AM_I失败先查电气我调试时遇到过两种情况第一次焊完板子上电读WHO_AM_I返回0xFF查了半天发现是传感器电源脚虚焊GND没接通另一次同样的现象原因是SCL引脚在CubeMX里配成了推挽复用总线被拉死。建议排查顺序是先量传感器VDD是否有电再示波器看SCL/SDA空闲电平是否为高最后才怀疑程序。5.2 数据全是0怎么办如果WHO_AM_I能读出来但OUT数据读回来全部是0大概率是传感器还在掉电模式CTRL1没写成功。HAL的Mem_Write函数返回HAL_OK不代表传感器真的接受了配置可以在写完CTRL1后马上回读一次确认数据写进去了。5.3 高低字节错位的坑不置BDU时偶发读到错误数据尤其在振动剧烈、数据更新快的场景下特别容易出现。X轴刚读完低字节传感器就把新数据更新了读高字节时拼出来一个对不上的值。开BDU之后这类现象基本消失。另外连续读6字节时务必把IF_ADD_INC置1否则每个字节都读同一个OUT_X_L地址Y/Z轴数据自然全是乱码。5.4 硬件IIC和模拟IIC怎么选到了C5这个级别硬件I2C外设在400kHz甚至1MHz下的时序是靠TIMINGR保证的可靠性和CPU占用都比模拟IIC强。模拟IIC唯一的优势是移植方便逻辑分析仪下也容易往上凑时序但CPU会被该死循环占住高频读取时严重影响其他任务。C5主频足够直接硬件I2C配DMA读取才是振动监测的正确姿势。下面把我在调试阶段遇到的典型问题和解决方向整理成速查表方便现场排查现象可能原因处理方式读WHO_AM_I返回0xFF上拉电阻缺失、SCL/SDA接反、传感器未供电万用表量VDD与GND示波器看空闲电平读WHO_AM_I返回0x00SDA被拉低、设备地址错误断开传感器单独测总线核对0x5A地址SCL无波形GPIO误配为推挽、I2C外设未使能检查GPIO复用配置和CubeMX外设开关数据全为0CTRL1未成功写入、传感器仍掉电写完立即回读CTRL1确认偶发错位数据BDU未开启CTRL2置位BDU重新读取连续读多字节数据重复IF_ADD_INC未置位CTRL2置位地址自增位400kHz下波形振铃上拉电阻太小或走线过长增大上拉电阻缩短IIC走线WHO_AM_I正确但数据波动异常采样率与读取频率不匹配增加STATUS轮询等待数据就绪5.5 几个细节经验关于STM32C5本身目前芯片还处于早期供货阶段市面上不太容易零售到建议直接找ST代理申请样片或者先用C0/G4系列把代码逻辑验证完等C5量产再移植。外设差异集中在TIMINGR和时钟树分频驱动代码层面基本是无缝切换。还有一点关于IIC总线上拉很多开发板默认已经有上拉电阻如果你自己又焊了一颗并联后阻值会变小400kHz也许没事但1MHz FM模式下可能超过最小电阻限制导致灌电流过大。算总线上拉阻值时要把板上已有的电阻一起算进去别只盯着自己焊的那颗。实测下来IIS3DWB在26.7kHz采样率下对轻微敲击的响应非常灵敏AX方向的AC分量能轻松超过几百mg轴承故障那种周期性冲击信号用这个配置抓取正合适。后续如果要上FFT频谱分析建议把读取改成DMA加FIFO模式让数据连续性更好。我个人体会是先把IIC链路和寄存器配置跑稳比一上来就堆算法更重要——传感器数据质量不行后端的频谱特征提取全是白费功夫。