基于STM32CubeMX驱动STHS34PF80热电堆传感器实现人体检测
简介面向STM32嵌入式开发者这份资料包围绕STHS34PF80红外存在检测传感器给出完整的人体检测方案。STHS34PF80属于非冷却、工厂校准的红外运动与存在检测器件工作波长520µm可感知视野内物体发出的红外辐射资源基于STM32CubeMX并采用HAL库搭建工程覆盖读取传感器ID、驱动人体检测、修改检测阈值三个递进阶段适合智能家居、安防感应等场景的开发者参考。资源共168个文件压缩包大小7.22MB其中以66个h头文件与24个c源文件为主的Keil工程源码构成核心另含uvprojx/uvoptx工程配置、ioc与mxproject图形化配置、PDF与txt/md说明文档以及编译生成的o、axf、hex等中间文件便于直接打开、编译和烧录验证。已有423人学习下载。借助该资源可快速获得可直接编译运行的STM32C011工程模板了解HAL库下I2C、UART、DMA、RCC、Flash等外设初始化与调用关系STHS34PF80.c驱动文件更是便于对照传感器手册进行寄存器配置和阈值调整支持二次开发。总体适合正在学习红外传感、低功耗存在检测或STM32CubeMX开发流程的电子工程师与学生。 这两年做人体检测方案我前前后后试过 PIR、毫米波雷达、摄像头视觉最后在一个低功耗门铃项目里把方案落到了 ST 的 TMOS 热电堆传感器 STHS34PF80 上。用 STM32CubeMX 配置工程、手写驱动、再配合这颗传感器的存在检测和运动检测输出调完一轮下来整体体验比 PIR 舒服太多——不用菲涅尔透镜、对静止人体也能识别、功耗还低。这篇文章就把我基于 STM32CubeMX 从零驱动 STHS34PF80 做人体检测的完整过程写出来包括硬件接线、CubeMX 配置、寄存器读写、判定逻辑和踩坑记录给后面做同类方案的朋友一个可以照抄的参考。1. 为什么用 STHS34PF80 而不是 PIR 或雷达1.1 TMOS 传感器到底和传统热释电有什么区别很多做嵌入式的人第一次看到 STHS34PF80 会以为它就是一颗高级点的 PIR实际上完全不是一回事。PIR 用的是热释电效应只能感应红外辐射的“变化”所以一旦人体静止下来它的输出就会归零这也是 PIR 误报和漏报的根源。STHS34PF80 用的是 ST 的 TMOS热 MOS技术本质上是一个微加工的热电堆阵列可以直接测量红外辐射的“绝对值”所以它对静止的人体同样有稳定的响应。这颗芯片内部还集成了信号处理链路能直接输出两大类人体检测信号目标存在信号AVG_TMOS_PRES和目标运动信号AVG_TMOS_MOT。存在信号告诉你视场里有没有人运动信号告诉你这个人有没有在动。这意味着主控完全不需要跑什么算法只要读寄存器、做阈值判断就行这对 MCU 选型压力非常小一颗便宜的 M0 内核芯片都能跑。1.2 这几种人体检测方案的横向对比我项目初期把市面上常见的检测方案都过了一遍列个表直接对比方案静止人体检测抗环境干扰光学要求平均功耗综合成本PIR不支持差热源/气流易误报需要菲涅尔透镜极低最低毫米波雷达支持较好但穿透墙体有隐私/串扰问题天线设计有门槛中等较高摄像头视觉支持受光照影响大涉及隐私需要镜头高高STHS34PF80 TMOS支持好只响应热目标平面窗口即可无需透镜低中等从表格能看出来如果产品需求是“检测区域内有没有人且不区分动静”PIR 基本被淘汰如果不想把方案搞得太复杂、又必须识别静止人员STHS34PF80 就是那个平衡点。我最终选它是综合了功耗、体积、光学设计和开发难度之后的结果实际用下来也确实省事。2. 硬件接线与 STM32CubeMX 工程配置2.1 最小系统接线与电平设计先讲最短能跑通的接线方式。STHS34PF80 的供电比较灵活VDD 和 VDD_IO 可以分开但通常直接都接 3.3V 就行。SDA 和 SCL 是 I2C 接口需要外部上拉电阻一般 4.7kΩ 到 3.3V。我建议在开发板上测量一下 I2C 引脚是否已有上拉否则通信会不稳定。传感器还有一个中断输出引脚 INT低电平有效可以接到 MCU 的任意带 EXTI 功能的 GPIO用于数据就绪唤醒。如果只是轮询读取INT 可以不接但后面做低功耗设计时它很重要。STHS34PF80 的 I2C 地址不是固定的芯片上有个 SA0 引脚接高电平时从机地址是 0x5A7 位地址接低电平时会偏移这个细节我在调第一块板子时踩过坑下文会细说。STM32 端我用的是一块常规的 STM32L4 系列开发板PB6/PB7 分别是 I2C1 的 SCL/SDA。用 STM32CubeMX 的好处就是引脚复用关系不用自己翻手册图形化点一下就能固化配置。2.2 CubeMX 的关键配置项打开 STM32CubeMX选择芯片型号后第一步是配置时钟树。我习惯让系统时钟跑到 80MHzI2C 外设时钟则保持默认即可CubeMX 会自动算出分频值。然后进入 Pinout 视图把 PB6 设置为 I2C1_SCL、PB7 设置为 I2C1_SDA。I2C1 的参数建议这样配I2C 速度模式Standard Mode速率 100kHz。这颗传感器虽然支持 400kHz但第一版调试时用低速更稳等通信没问题了再拉高。时钟无源滤波开启能滤掉总线上的毛刺。如果后续要做中断唤醒再额外把一个 GPIO 配置为外部中断输入模式下拉上升沿和下降沿都触发。生成代码前记得在 Project Manager 里勾选“Generate peripheral initialization as a pair of .c/.h files”这样驱动文件不会全堆在 main.c 里维护起来清爽很多。生成完工程后先编译一次确认环境没问题再开始动手写驱动。3. 驱动代码实现从 I2C 读写到读取人体数据3.1 先搞定传感器寄存器读写STHS34PF80 的操作方式和大多数 ST 传感器一样寄存器地址是 8 位数据可以单字节或连续多字节读取。我建议先封装两个底层函数一个是写寄存器另一个是读多字节后续所有功能都建立在它们之上。#define STHS34PF80_I2C_ADDR (0x5A 1) // 7位地址左移1位适配HAL static HAL_StatusTypeDef STHS34PF80_WriteReg(uint8_t reg, uint8_t *data, uint16_t len) { return HAL_I2C_Mem_Write(hi2c1, STHS34PF80_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, data, len, 100); } static HAL_StatusTypeDef STHS34PF80_ReadReg(uint8_t reg, uint8_t *data, uint16_t len) { return HAL_I2C_Mem_Read(hi2c1, STHS34PF80_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, data, len, 100); }HAL 库的 I2C_Mem_Read 本身就处理了“先写寄存器地址再读数据”的时序比手动控制 START/STOP 省心得多。注意 HAL 库的地址参数需要左移一位这是新手最容易忽略的地方。3.2 初始化流程与关键寄存器映射拿到一颗新传感器第一件事永远是读 WHO_AM_I 寄存器验证通信。STHS34PF80 的 WHO_AM_I 位于寄存器 0x0F读出来的值应该是 0xD3。通信都通了再往下走初始化。下面是我在项目里实际用的一套寄存器映射和初始化序列不同手册版本可能有差异以你手里的数据手册为准#define STHS34PF80_WHO_AM_I 0x0F #define STHS34PF80_SENSOR_CTRL 0x04 #define STHS34PF80_LPF1_TMOS 0x00 #define STHS34PF80_LPF2_TMOS 0x01 #define STHS34PF80_AVG_TMOS_PRES_L 0x30 #define STHS34PF80_AVG_TMOS_PRES_H 0x31 #define STHS34PF80_AVG_TMOS_MOT_L 0x32 #define STHS34PF80_AVG_TMOS_MOT_H 0x33 #define STHS34PF80_STATUS 0x18 uint8_t STHS34PF80_Init(void) { uint8_t id 0; STHS34PF80_ReadReg(STHS34PF80_WHO_AM_I, id, 1); if (id ! 0xD3) { return 0; // 通信异常返回失败 } // 配置低通滤波器平滑数据 uint8_t lpf1 0x12; // 截止频率约 500Hz 以下具体看手册 uint8_t lpf2 0x04; STHS34PF80_WriteReg(STHS34PF80_LPF1_TMOS, lpf1, 1); STHS34PF80_WriteReg(STHS34PF80_LPF2_TMOS, lpf2, 1); // 使能传感器开平均滤波 uint8_t ctrl 0x01; // 开启TMOS配置为连续测量模式 STHS34PF80_WriteReg(STHS34PF80_SENSOR_CTRL, ctrl, 1); return 1; }关键是 SENSOR_CTRL 这个寄存器它决定了传感器是否进入连续测量模式。如果这个寄存器没配置后面读出来的数据永远是 0 或者无效值。启动后建议延时 100ms 左右等传感器完成内部自校准再开始读数据。3.3 读取存在信号和运动信号数据读取没有任何玄学轮询状态寄存器看数据就绪位然后连续读 4 个字节就行。存在信号和运动信号都是 16 位有符号数低字节在前组合时要留意符号扩展。typedef struct { int16_t presence; int16_t motion; } STHS34PF80_Data; uint8_t STHS34PF80_GetData(STHS34PF80_Data *data) { uint8_t status 0; uint8_t buf[4] {0}; STHS34PF80_ReadReg(STHS34PF80_STATUS, status, 1); if ((status 0x01) 0) { return 0; // 数据未就绪 } STHS34PF80_ReadReg(STHS34PF80_AVG_TMOS_PRES_L, buf, 4); style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />