ENS160是一款基于MOX技术的数字多气体传感器,集成TrueVOC®算法,可直接输出TVOC(ppb)、eCO2(ppm)和AQI(1-5级)等高级空气质量数据,大幅降低主控算力负担。其采用双电源设计(VDD 1.8V核心供电,VDDIO 1.71–3.6V通信供电),实现电平兼容,避免通信失败。硬件上需注意VDD严禁接3.3V以防烧毁。通过STM32 HAL库可轻松实现I2C通信,初始化后进入标准工作模式,读取状态寄存器判断新数据,再获取各参数值,支持低功耗深度睡眠模式切换,适用于智能环境监测系统。
一、ENS160 多气体传感器
1.1 手册
拿到手册,千万不要从第一页死磕。
1.2 主要特性
ENS160 是一款基于金属氧化物(MOX)技术的数字多气体传感器,内部集成了 4 个独立的传感器元件。
1.2.1自带算法,直出高级数据
它最强大的地方在于内部集成了 TrueVOC® 算法,单片机不需要跑复杂的转换库,可以直接从寄存器读取传感器数据。这极大地释放了主控芯片(如 STM32)的算力。
就像大家想吃鱼或鸡肉,我们在市场买完以后不能直接用,因为鱼肉和鸡肉都需要进行处理、剁块以后才能再做。有的是摊主会在用户买完鱼以后帮我们收拾出来,这让我们做饭得到了极大的便利,节省了时间,ENS160对STM32来说就是这样一款传感器,它能对传感器数据进行算法处理,用户直接读就行。
能测量的环境指标有
TVOC(总挥发性有机物):直接输出浓度,单位是 ppb。
eCO2(等效二氧化碳):根据人类呼吸和异味推算出的等效 CO2,单位是 ppm。
AQI-UBA(空气质量指数):直接给出一个 1 到 5 的等级(1 是极好,5 是极差)。
1.2.2抗干扰能力
具备抗湿度和抗臭氧干扰的能力。
1.2.3供电极度敏感
VDD:核心主电源 (Main Supply Voltage)
它是干什么的:VDD 负责给芯片内部最核心的模块供电,包括内部的微型加热器(Heaters)、ADC 采样电路、以及计算 TrueVOC 算法的数字逻辑大脑。
电压要求:它的正常工作电压范围被严格限制在1.71V 到 1.98V(典型值是 1.8V)。
如果接错:如果随手把它接到了单片机常用的 3.3V 上,过高的电压会瞬间击穿内部脆弱的 1.8V 硅结构,直接烧毁芯片。
VDDIO:接口通信电源 (Interface Supply Pins)
它是干什么的:VDDIO (I/O 即 Input/Output) 只负责给芯片对外的通信引脚(比如 I2C 的 SDA/SCL、SPI 的 MOSI/MISO、以及中断引脚 INTn)提供电平参考。
电压要求(非常宽容):它的允许电压范围是1.71V 到 3.6V。
为什么芯片厂商非要这么“麻烦”地设计两个电源?
为了解决“电平兼容性”。
随着芯片工艺越来越先进,为了降低功耗和发热,传感器内部的微观电路只能承受很低的电压(比如 1.8V 甚至 1.2V)。所以 ENS160 的 VDD 必须是 1.8V。
但是,你的主控单片机(比如 STM32)通常是跑在 3.3V 的。如果 ENS160 只有一个 1.8V 的电源,那么它输出的 I²C 高电平最高也只有 1.8V。STM32 去读这个 1.8V 的信号时,可能根本识别不出这是高电平,导致通信完全失败。
VDDIO 的出现完美解决了这个问题:
把 ENS160 的 VDDIO 直接接到单片机的 3.3V 电源上。这样一来,ENS160 的内部核心依然在安全的 1.8V 下工作,但它的通信引脚(SDA、SCL)在输出高电平时,会被拉高到 VDDIO 的电压(即 3.3V)。STM32 就能完美接收数据了!
总结:
分开设计 VDD 和 VDDIO,相当于芯片自带了一个“电平转换器”。硬件工程师在画图时,只需要给 VDD 加一个 1.8V 的 LDO 降压芯片,然后把 VDDIO 和 STM32 的 3.3V 连在一起,软硬件就能无缝握手了。
1.3 硬件电路设计
1.3.1 引脚功能定义
9 个引脚的详细功能拆解:
引脚 1:MOSI/SDA (输入/输出)
SPI 模式下:作为 MOSI(主输出从输入),用于单片机向传感器发送数据。
I2C 模式下:作为 SDA(串行数据线),用于双向传输数据。
引脚 2:SCLK/SCL (输入)
SPI 模式下:作为 SCLK,接收单片机发来的串行时钟信号。
I2C模式下:作为 SCL,接收 I2C总线的串行时钟信号。
引脚 3:MISO/ADDR (输入/输出)
SPI 模式下:作为 MISO(主输入从输出),用于传感器向单片机返回数据。
I2C 模式下:作为 ADDR(地址选择引脚)。在上电时,如果该引脚接低电平(GND),传感器的 I2C 地址为
0x52;如果接高电平(VDDIO),地址为0x53。
引脚 4:VDD (电源)
核心主电源。这是最关键的引脚,负责给内部核心逻辑和微加热器供电。电压必须严格控制在 1.71V 到 1.98V 之间(通常接 1.8V),绝对不能直接接 3.3V,否则会烧毁芯片。
引脚 5:VDDIO (电源)
接口/通信电源。决定了通信引脚(SDA/SCL 等)的高电平电压。它的范围较宽,可以接 1.71V 到 3.6V。通常直接与单片机的 3.3V 系统电源相连,实现电平匹配。
引脚 6:INTn (输出)
中断引脚。当传感器有新的空气质量数据(如 AQI、TVOC 更新)产生,或者达到某个设定的阈值时,这个引脚会向主机(如 STM32)发送中断信号。可以在寄存器中配置为高电平有效或低电平有效。
引脚 7:CSn (输入)
片选与通信协议选择引脚。这是决定芯片“开口说哪种语言”的开关。
如果拉低(接 GND 或由主机拉低),芯片进入SPI 模式。
如果拉高(接 VDDIO),芯片进入I2C模式。
引脚 8 和 9:VSS (电源)
地线 (GND)。这两个引脚在内部是连通的,画电路板时需要将它们一起连接到系统的地线上。
1.3.2 典型应用电路
1.3.3 拓展版实际电路设计
1.4 使用方式
1、该传感器分为三种不同的工作模式
模式0 深度睡眠模式 该模式下功耗极低 只能从该模式进入空闲模式
模式1 为 IDLE MODE空闲模式 该模式下可以识别很多指令
模式2 为 工作模式 该模式下传感器会不停的进行空气质量检测
当进入到模式2,新数据产生时会有两种通知方式:1 通过中断,传输通过6号INTn引脚传递;2 通过传感器自动写DEVICE_STATUS寄存器实现。
产生的新数据会被存放在DATA_XXX寄存器组中
1.4.1 STM32 I2C 读取示例
下面给出一个基于 STM32 HAL 库的 I2C 读取示例,演示如何初始化 ENS160、设置工作模式并读取 TVOC、eCO2 和 AQI 数据。代码中包含了寄存器地址、模式设置、状态判断和数据读取的完整流程。
/* ENS160 寄存器地址定义 */ #define ENS160_I2C_ADDR 0x52 /* ADDR 引脚接 GND 时的 I2C 地址 */ #define ENS160_REG_PART_ID 0x00 /* 产品 ID 寄存器(只读) */ #define ENS160_REG_OPMODE 0x10 /* 工作模式寄存器(读写) */ #define ENS160_REG_CONFIG 0x11 /* 配置寄存器(读写) */ #define ENS160_REG_STATUS 0x20 /* 状态寄存器(只读) */ #define ENS160_REG_DATA_TVOC 0x22 /* TVOC 数据寄存器(只读,2 字节) */ #define ENS160_REG_DATA_ECO2 0x24 /* eCO2 数据寄存器(只读,2 字节) */ #define ENS160_REG_DATA_AQI 0x21 /* AQI 数据寄存器(只读,1 字节) */ /* 工作模式定义 / #define ENS160_OPMODE_SLEEP 0x00 / 深度睡眠模式 / #define ENS160_OPMODE_IDLE 0x01 / 空闲模式 / #define ENS160_OPMODE_STANDARD 0x02 / 标准工作模式 */ /* 状态寄存器位定义 / #define ENS160_STATUS_NEW_DATA 0x02 / 第 1 位:有新数据产生 */ /* 全局 I2C 句柄(由 CubeMX 生成) */ extern I2C_HandleTypeDef hi2c1; /** @brief 向 ENS160 写入一个字节寄存器 @param reg 寄存器地址 @param data 要写入的数据 @retval HAL 状态 */ HAL_StatusTypeDef ENS160_WriteReg(uint8_t reg, uint8_t data) { return HAL_I2C_Mem_Write(&hi2c1, ENS160_I2C_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 100); } /** @brief 从 ENS160 读取多个字节寄存器 @param reg 起始寄存器地址 @param buf 数据缓冲区 @param len 读取长度 @retval HAL 状态 */ HAL_StatusTypeDef ENS160_ReadRegs(uint8_t reg, uint8_t *buf, uint8_t len) { return HAL_I2C_Mem_Read(&hi2c1, ENS160_I2C_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100); } /** @brief 初始化 ENS160 传感器 @note 上电后先等待 10ms,再进入空闲模式,最后切换到标准工作模式 @retval 0 成功,-1 失败 */ int8_t ENS160_Init(void) { uint8_t part_id[2] = {0}; /* 1. 等待传感器上电稳定 */ HAL_Delay(10); /* 2. 读取产品 ID,验证 I2C 通信是否正常 / if (ENS160_ReadRegs(ENS160_REG_PART_ID, part_id, 2) != HAL_OK) { return -1; } / ENS160 的产品 ID 固定为 0x0160,高字节在前 */ if ((part_id[0] != 0x01) || (part_id[1] != 0x60)) { return -1; } /* 3. 先进入空闲模式(IDLE),等待内部算法稳定 */ if (ENS160_WriteReg(ENS160_REG_OPMODE, ENS160_OPMODE_IDLE) != HAL_OK) { return -1; } HAL_Delay(10); /* 4. 切换到标准工作模式,开始持续检测空气质量 */ if (ENS160_WriteReg(ENS160_REG_OPMODE, ENS160_OPMODE_STANDARD) != HAL_OK) { return -1; } HAL_Delay(10); return 0; } /** @brief 读取 TVOC、eCO2 和 AQI 数据 @param tvoc 输出 TVOC 浓度(单位 ppb) @param eco2 输出 eCO2 浓度(单位 ppm) @param aqi 输出 AQI 等级(1~5) @retval 0 成功,-1 无新数据,-2 读取失败 */ int8_t ENS160_ReadData(uint16_t *tvoc, uint16_t *eco2, uint8_t *aqi) { uint8_t status = 0; uint8_t buf[5] = {0}; /* 1. 读取状态寄存器,判断是否有新数据 */ if (ENS160_ReadRegs(ENS160_REG_STATUS, &status, 1) != HAL_OK) { return -2; } /* 2. 检查 NEW_DATA 位,没有新数据则直接返回 */ if (!(status & ENS160_STATUS_NEW_DATA)) { return -1; } /* 3. 一次性读取 AQI、TVOC、eCO2 共 5 个字节 */ if (ENS160_ReadRegs(ENS160_REG_DATA_AQI, buf, 5) != HAL_OK) { return -2; } /* 4. 解析数据:AQI 占 1 字节,TVOC 和 eCO2 各占 2 字节(大端) */ *aqi = buf[0]; *tvoc = ((uint16_t)buf[1] << 8) | buf[2]; *eco2 = ((uint16_t)buf[3] << 8) | buf[4]; return 0; } /* 主循环中的调用示例 */ void main_loop(void) { uint16_t tvoc = 0, eco2 = 0; uint8_t aqi = 0; /* 初始化传感器 */ if (ENS160_Init() != 0) { /* 初始化失败,可在此处打印错误信息 */ return; } while (1) { /* 读取数据,返回 0 表示成功 */ if (ENS160_ReadData(&tvoc, &eco2, &aqi) == 0) { /* 在此处打印或上传数据 */ /* printf("TVOC: %u ppb, eCO2: %u ppm, AQI: %u\n", tvoc, eco2, aqi); */ } HAL_Delay(1000); /* 每秒读取一次 */ } }代码说明:
- 寄存器地址:ENS160 的寄存器采用 8 位地址,通过 I2C 的
Mem_Read/Mem_Write接口访问。 - 模式设置:上电后先进入空闲模式(0x01),再切换到标准工作模式(0x02),确保内部算法完成初始化。
- 状态判断:每次读取前先检查状态寄存器(0x20)的
NEW_DATA位,避免读到旧数据。 - 数据读取:AQI、TVOC、eCO2 三个数据连续存放在 0x21~0x25,可以一次读取 5 个字节。
总结
1、进入0X02模式
2、读DEVICE_STATUS寄存器判断是否有新数据产生
3、有的话就读DATA_XXX寄存器,没有新数据就等待
4、如果没有通信需求,可以让其进入到0x00模式,降低功耗