ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

STM32驱动SPL06-001气压传感器:I2C通信与温度补偿实战

2026/9/21 0:38:54 拓冰建站 浏览量
STM32驱动SPL06-001气压传感器:I2C通信与温度补偿实战 1. 项目缘起与整体设计思路1.1 为什么选SPL06-001这颗气压传感器先说选型这件事。市面上做气压测量的传感器不少BMP280、BME280、MS5611、DPS310这些我都用过最后在这个项目里定下SPL06-001原因很实在它的相对精度能到±0.06 hPa换算成高度大概是±0.5米绝对精度±1 hPa这个指标对于做无人机定高、室内楼层定位、气象站这类场景完全够用。而且它同时输出气压和温度两路数据温度用来做气压的温度补偿省得再外挂一颗温度传感器。SPL06-001是歌尔Goertek出的封装是2.5mm×2.5mm的LGA-8体积小贴片焊接对新手稍微有点挑战但用热风枪或者回流焊台问题不大。供电范围1.7V到3.6V和STM32的3.3V系统天然匹配不需要额外做电平转换这一点比某些5V器件省心。它支持I2C和SPI两种接口。我选I2C理由有两个一是STM32的硬件I2C外设成熟二是I2C只需要两根线SCL、SDA布线简单对于板子空间紧张的项目很友好。SPL06-001的I2C地址是固定的0x767位地址写操作时左移一位变成0xEC读操作是0xED这个后面代码里会体现。1.2 整体方案怎么搭整个项目的硬件链路很清晰STM32通过I2C总线读SPL06-001的原始数据经过温度补偿和气压解算得到真实的气压值单位Pa或hPa再通过串口打印到电脑上看。如果你想做高度测量还可以用气压高度公式把气压换算成海拔。软件层面我分成三层来写这样结构清楚、方便移植底层驱动层封装I2C的读写函数负责和SPL06-001的寄存器打交道包括读寄存器、写寄存器、读多字节数据。传感器中间层负责初始化配置、读取原始数据、执行温度补偿算法、输出校准后的气压和温度。应用层主循环里定时读取数据通过串口打印或者做进一步的高度解算。这么分层的好处是以后你换一颗别的I2C传感器底层I2C函数几乎不用动只改中间层就行。我踩过的坑是一开始把所有代码堆在main.c里后来想移植到另一个项目改得头大所以强烈建议一开始就分好层。1.3 开发环境与工具准备开发环境我用的是Keil MDK5配合STM32CubeMX生成初始化代码。STM32CubeMX这个工具真的省事I2C外设、时钟、GPIO的初始化代码点几下就出来了不用手撸寄存器。芯片我选的是STM32F103C8T6也就是常说的“最小系统板”便宜、资料多、够用。烧录工具用ST-Link驱动装好之后Keil里直接能识别。串口调试用CH340模块电脑上装好CH340驱动用串口助手看打印数据。这里提醒一句ST-Link驱动和CH340驱动一定要装对版本我见过太多人卡在“设备管理器里有黄色感叹号”这一步后面会专门讲排查。硬件连接就四根线VCC接3.3VGND接GNDSCL接STM32的PB6SDA接PB7这是STM32F103的I2C1默认引脚。注意SPL06-001的I2C需要外部上拉电阻一般用4.7kΩ到10kΩ接到3.3V。有些模块板已经自带上拉如果你买的是裸芯片一定要自己加否则I2C通信根本起不来这是新手最容易忽略的点。2. SPL06-001核心细节与I2C通信要点2.1 传感器内部寄存器地图要驱动一颗传感器第一件事就是把它的寄存器手册啃明白。SPL06-001的寄存器不多但每个都有讲究我挑几个关键的讲。0x00到0x02是压力数据寄存器PSR_B2、PSR_B1、PSR_B024位有符号数高字节在前。0x03到0x05是温度数据寄存器TMP_B2、TMP_B1、TMP_B0同样是24位。读的时候要一次性把三个字节读出来再拼接不能分三次读否则中间数据可能被刷新导致数据错乱。0x06是压力配置寄存器PRS_CFG用来设置压力测量的速率和过采样率。0x07是温度配置寄存器TMP_CFG。0x08是测量配置寄存器MEAS_CFG控制传感器的工作模式比如连续测量、单次测量、待机等。0x09是FIFO配置0x0A是FIFO状态0x0C是中断状态。0x10到0x15是校准系数寄存器这是最容易被忽略但最关键的部分。SPL06-001出厂时每颗芯片都做了校准校准系数存在这些寄存器里包括c0、c1、c00、c10、c01、c11、c20、c30、c31、c40等。你读出来的原始气压和温度必须用这些系数做补偿计算否则数据偏差会很大。我一开始偷懒没读校准系数直接拿原始值算结果气压差了十几个hPa完全不能用。2.2 I2C通信协议的关键细节I2C这个协议说简单也简单两根线搞定说坑也多时序稍微不对就通信失败。我结合SPL06-001的实际操作讲几个要点。起始和停止条件SCL高电平时SDA从高变低是起始条件SCL高电平时SDA从低变高是停止条件。STM32的硬件I2C外设会自动处理这些你调HAL库函数就行不用手动翻转GPIO。地址帧SPL06-001的7位地址是0x76发送时左移一位最低位是读写位。写操作是0xEC读操作是0xED。这个在HAL库里的写法是0x76 1然后根据读写或上0x01。应答机制每发送一个字节接收方要拉低SDA一个时钟周期作为应答ACK。如果从机没应答NACK说明通信有问题可能是地址错了、器件没上电、或者上拉电阻没接。我在调试时专门写了一个检测ACK的函数一旦NACK就打印错误码定位问题快很多。为什么I2C要用开漏输出加外部上拉这是I2C的经典问题。I2C总线是“线与”逻辑多个设备可以同时拉低总线但不能同时拉高否则会短路。所以SDA和SCL都配置成开漏输出只能主动拉低拉高靠外部上拉电阻。如果上拉电阻太小电流大、功耗高还可能拉不到低电平上拉电阻太大上升沿变缓高速通信时波形失真。4.7kΩ在3.3V、100kHz到400kHz的场景下是甜点值我实测很稳。时钟频率SPL06-001支持标准模式100kHz和快速模式400kHz。我一般先用100kHz调通确认数据正确后再提到400kHz。有些便宜的模块板走线不好400kHz下波形很差这时候降回100kHz反而更可靠。2.3 校准系数怎么读、怎么用校准系数是SPL06-001数据准确性的命根子。这些系数存在0x10到0x15这6个寄存器里每个寄存器8位但系数有的是12位、有的是16位、有的是20位需要跨寄存器拼接。具体来说c0和c1是12位存在0x10和0x11c00和c10是20位存在0x12到0x15c01、c11、c20、c30、c31、c40这些是补码形式需要根据最高位判断正负。手册里给了详细的拼接公式我建议直接照着手册写别自己瞎猜。这里有个大坑校准系数是有符号数拼接完之后要做符号扩展。我一开始忘了做符号扩展结果c00算出来是个巨大的正数气压补偿完全跑偏。后来加了符号判断数据立刻就对了。所以读校准系数的时候一定要仔细看手册里每个系数的位宽和符号位。3. 实操过程与核心代码实现3.1 STM32CubeMX配置I2C外设打开STM32CubeMX选好芯片型号STM32F103C8T6然后按下面几步配置第一步配置时钟。在RCC里把HSE设为Crystal/Ceramic Resonator然后在Clock Configuration里把系统时钟拉到72MHz。I2C1挂在APB1上时钟是36MHz。第二步配置I2C1。在Connectivity里选I2C1模式选I2C速度模式选Standard Mode或者Fast Mode。我一般先选Standard Mode100kHz调通后再改Fast Mode。引脚会自动分配到PB6SCL和PB7SDA。第三步配置USART1用于串口打印。模式选Asynchronous波特率1152008位数据无校验1位停止位。引脚是PA9TX和PA10RX。第四步生成代码。工程名随便起工具链选MDK-ARM V5然后点GENERATE CODE。生成完之后Keil里打开工程编译一下确认没错误。这时候I2C和串口的初始化代码都已经在main.c里了你只需要在main函数里加自己的逻辑。3.2 底层I2C读写函数封装HAL库提供了HAL_I2C_Mem_Read和HAL_I2C_Mem_Write这两个函数直接用来读写寄存器很方便。我封装了两个函数#define SPL06_ADDR_W 0xEC #define SPL06_ADDR_R 0xED uint8_t SPL06_ReadReg(uint8_t reg) { uint8_t data 0; HAL_I2C_Mem_Read(hi2c1, SPL06_ADDR_R, reg, I2C_MEMADD_SIZE_8BIT, data, 1, 100); return data; } void SPL06_WriteReg(uint8_t reg, uint8_t data) { HAL_I2C_Mem_Write(hi2c1, SPL06_ADDR_W, reg, I2C_MEMADD_SIZE_8BIT, data, 1, 100); }读多字节数据的时候比如读气压的三个字节用HAL_I2C_Mem_Read一次读三个void SPL06_ReadMulti(uint8_t reg, uint8_t *buf, uint8_t len) { HAL_I2C_Mem_Read(hi2c1, SPL06_ADDR_R, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100); }这里的超时时间我设的是100ms实际通信几毫秒就完成了设大一点是防止偶尔总线忙的时候直接返回错误。3.3 传感器初始化与配置初始化分几步先读校准系数存起来然后配置压力和温度的测量速率与过采样率最后设置工作模式。压力和温度配置寄存器0x06和0x07的格式是最高位是测量速率选择位低四位是过采样率。过采样率越高数据越准但转换时间越长。我一般用压力过采样率64倍、温度过采样率8倍速率选每秒8次测量这个组合在精度和速度之间平衡得比较好。void SPL06_Init(void) { // 读校准系数 SPL06_ReadCalibration(); // 配置压力速率8次/秒过采样64 SPL06_WriteReg(0x06, 0x33); // 配置温度速率8次/秒过采样8 SPL06_WriteReg(0x07, 0x23); // 设置连续测量模式压力和温度都测 SPL06_WriteReg(0x08, 0x07); }0x08寄存器写0x07表示连续测量模式压力和温度都使能。如果你想省电可以写0x02做单次测量测完自动回待机。3.4 温度补偿与气压解算这是整个项目最核心的算法部分。SPL06-001输出的原始气压和温度是24位有符号数需要先转成实际物理量再用校准系数补偿。先看温度补偿。原始温度值除以温度过采样率对应的缩放因子得到温度值。然后温度补偿后的气压用这个公式P_comp P_raw (c00 c10 * T c20 * T^2 c30 * T^3) (c01 c11 * T c21 * T^2) * P_raw (c02 c12 * T) * P_raw^2 c03 * P_raw^3这个公式看着吓人其实就是个多项式补偿。实际写代码的时候把系数和原始值代进去算就行。我建议用float类型虽然STM32F103没有硬件浮点但72MHz的主频算这点东西绰绰有余一次解算也就几百微秒。float SPL06_GetPressure(void) { uint8_t buf[3]; int32_t p_raw; float p_comp; SPL06_ReadMulti(0x00, buf, 3); p_raw (int32_t)((buf[0] 16) | (buf[1] 8) | buf[2]); // 24位有符号数符号扩展 if (p_raw 0x800000) p_raw | 0xFF000000; // 代入补偿公式系数在初始化时已读入全局变量 p_comp p_raw (c00 c10 * T c20 * T * T c30 * T * T * T) (c01 c11 * T c21 * T * T) * p_raw (c02 c12 * T) * p_raw * p_raw c03 * p_raw * p_raw * p_raw; return p_comp; }温度的计算类似原始温度值先除以缩放因子再代入温度补偿公式。温度补偿后的值单位是摄氏度。3.5 串口打印与数据验证主循环里每隔500ms读一次数据通过串口打印出来while (1) { float pressure SPL06_GetPressure(); float temperature SPL06_GetTemperature(); printf(Pressure: %.2f Pa, Temperature: %.2f C\r\n, pressure, temperature); HAL_Delay(500); }验证数据对不对最简单的办法是拿一个已知准确的气压计对比。我在家里测海平面附近标准大气压约101325 Pa实际读数在100000到102000 Pa之间波动都正常因为天气和海拔会影响。温度读数用手摸传感器数值应该上升松开后慢慢回落这说明温度通道工作正常。如果你想测高度可以用这个公式height 44330 * (1 - (pressure / 101325) ^ 0.1903)这个公式算出来的是相对海平面的高度精度受天气影响但做相对高度变化测量比如无人机定高完全够用。4. 常见问题与排查技巧实录4.1 I2C通信失败怎么查I2C通信失败是最常见的问题表现是HAL_I2C_Mem_Read返回HAL_ERROR或者HAL_TIMEOUT。排查按这个顺序来先查硬件连接。VCC是不是3.3VGND是不是共地SCL和SDA有没有接反。我见过有人把SCL和SDA接反了调了半天以为是代码问题结果线一换就好了。再查上拉电阻。用万用表量SCL和SDA对VCC的电阻正常应该是4.7kΩ到10kΩ。如果量出来是无穷大说明没接上拉I2C根本起不来。如果量出来是几百欧姆说明上拉太小可能拉不低。然后查地址。SPL06-001的地址是0x76但有些模块板可能做了地址跳线实际地址不一样。用I2C扫描程序扫一下总线看看能不能扫到0x76。扫不到就说明器件没响应。最后查时序。用示波器看SCL和SDA的波形起始条件、地址帧、应答位是不是正常。没有示波器的话把I2C速度降到100kHz甚至50kHz试试有时候是速度太快导致时序不满足。4.2 数据跳动大或不准确数据跳动大先看是不是过采样率设太低。过采样率越低噪声越大。把压力过采样率从8倍提到64倍数据会稳很多。如果数据整体偏差大检查校准系数有没有读对。我踩过的坑是校准系数拼接时忘了符号扩展导致补偿完全错误。还有一个坑是校准系数读完之后没存成全局变量每次解算都重新读结果读的时候数据被刷新了系数错乱。另外SPL06-001对温度变化比较敏感如果传感器靠近发热元件比如STM32芯片本身温度读数会偏高进而影响气压补偿。建议传感器远离热源或者做温度隔离。4.3 常见问题速查表问题现象可能原因排查方法解决方案I2C返回HAL_ERROR地址错误或器件未响应用I2C扫描程序扫总线确认地址0x76检查供电I2C返回HAL_TIMEOUT总线被拉低或上拉缺失万用表量SCL/SDA对VCC电阻补上4.7kΩ上拉电阻气压数据偏差大校准系数未读或读错打印校准系数检查按手册正确拼接并符号扩展数据跳动大过采样率太低查看0x06和0x07寄存器配置提高过采样率到64倍温度读数偏高传感器靠近热源手摸传感器看温度变化远离发热元件或加隔热串口无输出波特率不对或TX/RX接反检查串口助手波特率改为115200交换TX/RXST-Link识别不到驱动未装或版本不对设备管理器查看重装ST-Link驱动CH340无串口驱动未装设备管理器查看安装CH340驱动4.4 几个独家避坑心得第一先调通I2C再调传感器。我建议写一个简单的I2C扫描程序确认能扫到0x76再开始写传感器驱动。这样能把硬件问题和软件问题分开定位快很多。第二校准系数一定要验证。读完之后打印出来对照手册里的典型值范围看看是否合理。如果某个系数明显超出范围大概率是拼接或符号扩展错了。第三电源要干净。SPL06-001对电源噪声敏感如果STM32板上有电机、继电器这类负载建议给传感器单独加一个LDO或者RC滤波。我试过用同一个3.3V给电机和传感器供电数据跳得厉害加了滤波电容之后就稳了。第四I2C总线上拉电阻不要省。有些模块板自带上拉但阻值可能不合适。如果通信不稳定把模块板上的上拉去掉自己外接4.7kΩ往往能解决问题。第五调试时多用串口打印。把原始数据、校准系数、补偿后的数据都打印出来一眼就能看出哪一步出了问题。别怕打印多调试阶段信息越多越好。5. 项目扩展与进阶玩法5.1 做高度计和楼层定位气压传感器最经典的扩展就是做高度计。用前面提到的气压高度公式把气压换算成海拔高度。如果做室内楼层定位可以记录基准气压然后测相对高度变化。一层楼大概3米对应气压变化约0.36 hPaSPL06-001的±0.06 hPa精度完全能分辨出来。不过要注意天气变化会导致基准气压漂移所以做楼层定位的时候要定期校准基准值。我的做法是开机时记录当前气压作为基准然后每隔一段时间用已知楼层的气压重新校准。5.2 结合OLED做便携气压计加一个0.96寸OLED用I2C接口地址一般是0x3C和SPL06-001挂在同一条I2C总线上。STM32轮流读两个设备OLED显示实时气压、温度和高度。这样就是一个便携式数字气压计成本不到30块钱。OLED和SPL06-001共用I2C总线的时候注意两个设备的地址不能冲突。SPL06-001是0x76OLED是0x3C不冲突可以直接挂一起。5.3 数据记录与上位机显示如果想做长期气象监测可以加一个SD卡模块把气压和温度数据按时间戳存成CSV文件。或者用蓝牙模块把数据传到手机上看曲线。我用的是一个简单的方案STM32通过串口把数据发给电脑电脑上用Python的matplotlib实时画曲线效果很直观。Python这边用pyserial读串口解析数据然后实时绘图。代码不长几十行就能搞定。这样你就能看到气压随时间的变化曲线天气变化的时候曲线会有明显波动挺有意思的。5.4 低功耗优化如果是电池供电的项目低功耗很重要。SPL06-001支持待机和单次测量模式测完自动回待机功耗很低。STM32也可以用停机模式定时唤醒读数据。我实测下来用单次测量模式每秒测一次整体功耗能降到毫安级用一节18650电池能跑好几天。具体做法是把0x08寄存器写成0x02单次测量然后每次测量前写一次0x08等测量完成后再读数据。测量完成可以通过读0x08寄存器的状态位判断或者简单点延时等待。6. 写在最后的实操体会这个项目我从选型到调通大概花了两天时间其中大部分时间卡在校准系数和I2C上拉电阻这两个坑上。现在回头看如果一开始就把I2C扫描程序写好先确认硬件没问题再啃校准系数的手册能省下至少半天时间。SPL06-001这颗传感器性能在这个价位里算是很能打的精度够、体积小、接口简单。唯一需要注意的是校准系数和上拉电阻把这两点搞定剩下的就是按部就班写代码。我后来把它移植到另一个STM32F4的项目上底层I2C函数只改了一下句柄中间层和应用层几乎没动这就是分层设计的好处。如果你也在做气压相关的项目建议先把I2C调通再读校准系数最后做补偿解算一步一步来别想着一次写完。调试的时候多用串口打印把中间变量都打出来问题往往就藏在那些你以为没问题的细节里。