MM32F3277 I2C 访问 HTS221 温湿度传感器

时间: 2024-11-10 admin IT培训

MM32F3277 I2C 访问 HTS221 温湿度传感器

MM32F3277 I2C 访问 HTS221 温湿度传感器

主控芯片:MM32F3277

传感器:HTS221 温湿度传感器

开发环境:IAR 7.80.4

I2C 原理

I2C 总线接口使用两条串行线,SDA 和 SCL,分别用于数据和时钟的传输,工作方式是半双工。I2C 分为主从模式,Master 和 Slave,每一个 I2C 设备都有一个固定的地址。

I2C 总线物理拓扑结构

主控作为 Master,总线上可以挂多个 Slave 设备,Master 通过寻址控制各个 Slave

SCL 和 SDA 在总线空闲时,由上拉电阻拉高,保持高电平

I2C Start / Stop 条件

START 和 STOP 位由 Master 发送,

  • Start:SCL 为高时,SDA 从高到低
  • DATA:SCL 为高时获取稳定数据,SCL 为低时数据高低变化
  • Stop:SCL 为高时,SDA 从低到高

I2C 寻址

MM32F3277 支持 7 位和 10 位地址,Master 发送 START 位之后,发送 Slave 的地址,如果 Slave 地址匹配上,Slave 应答 SAK

Read 为 1,Write 为 0

  • 7 位地址

  • 10 位地址

I2C Master 发送

Master 发送 DATA 后,Slave 回应 SAK;Master 发送完 DATA 后,产生 STOP 信号

I2C Master 接收

Master 接收 DATA 后,回应 MAK;接收完所有的 DATA 后,回应 NMAK,产生 STOP 信号

HTS221 温湿度传感器

[ST HTS221 温湿度传感器资料][hts221]

Features

  • 16-bit humidity and temperature output data
  • Embedded 16-bit ADC
  • SPI and I2C interface
  • Humidity accuracy: ± 3.5% rH, 20 to +80% rH
  • Temperature accuracy: ±0.5℃, 15 to +40℃

Pin

  1. VDD: Power supply
  2. SCL / SPC: I2C serial clock / SPI serial port clock
  3. DRDY: Data Ready output signal
  4. SDA / SDI / SDO: I2C serial data / 3-wrie SPI serial data input or output
  5. GND: Ground
  6. CS: Set 1 when use I2C; Set 0 when use SPI

这里选择通过 I2C 进行通讯,所以 CS 管脚接 VDD (或者不接,内部可以上拉);若果选择 SPI 进行通讯,需要将 CS 管脚接地。

SCL 和 SDA 两个管脚必须是通过上拉电阻连接到 VDD!!!因为 START 信号是 SDA 由高到低,所以得先把 SDA 拉高。

HTS221 Address

HTS221 作为 I2C Slave,其地址为:

// HTS221 Address
#define SLAVE_ADDRESS                   0x5F
#define SLAVE_ADDRESS_WRITE             SLAVE_ADDRESS << 1				// 0xBE
#define SLAVE_ADDRESS_READ              (SLAVE_ADDRESS << 1) | 0x01		// 0xBF

Master write data to Slave

Steps

  1. Master 发送 START 信号(SCL hold high, SDA high to low)

  2. Master 发送 Slave 的 7 位地址 + 写信号

  3. Slave 进行地址匹配,匹配成功回应 SAK

  4. Master 发送 SubAddress,即 Slave 内部寄存器地址

    如果一次 I2C 数据传输只操作一个寄存器

    #define HTS221_CTRL_REG1                0x20
    HTS221_Reg_Write(I2Cx, HTS221_CTRL_REG1, buffer, 1)
    

    如果一次 I2C 数据传输操作多个地址连续的寄存器,将 SubAddr 的 MSB 置 1,SubAddr[6:0] 为寄存器地址

    #define CONTINUOUS_ADDRESS(REG_ADDR)    REG_ADDR | 0x80
    HTS221_Reg_Write(I2Cx, CONTINUOUS_ADDRESS(HTS221_CTRL_REG1), buffer, 3)
    
  5. Slave 应答 SubAddr

  6. Master 发送 DATA,Slave 应答 SAK

  7. Master 发送完后,产生 STOP 信号(SCL hold high, SDA lowto high)

Master read data from Slave

Steps

  1. Master 发送 START 信号(SCL hold high, SDA high to low)
  2. Master 发送 Slave 的 7 位地址 + 写信号
  3. Slave 进行地址匹配,匹配成功回应 SAK
  4. Master 发送 SubAddress,即 Slave 内部寄存器地址,操作同上
  5. Slave 应答 SubAddr
  6. Master 发送 RESTART 信号,重新发送 Slave 的 7 位地址 + 读信号,Slave 回应 SAK
  7. Slave 发送 Data,Master 应答 MAK
  8. Master 接收最后一个数据后,不应答 NMAK,产生 STOP 信号(SCL hold high, SDA lowto high)

Register Map

  • WHO_AM_I

    Reg Address is 0x0F, read only and default is 0xBC

    // HTS221 Device ID
    #define DEVICE_ID_WHO_AM_I              0xBC
    
  • AV_CONF

    Reg Address is 0x10, R/W and default is 0x1B

    该寄存器主要控制温度和湿度的样本个数

  • CTRL_REG1

    Reg Address is 0x20, R/W and default is 0x00

    1. PD 控制传感器开关,置 1 为 active;置 0 为 power-down

    2. BDU 控制 MSB 和 LSB 数据读取的连续性,置 1 为读完 MSB 和 LSB 之后再更新输出数据;置 0 为实时更新,可能会导致读取的 MSB 和 LSB 不是同一组

    3. ODR1 / ODR0 控制数据读取频率,00 为 one-shot,01 为 1Hz,10 为 7Hz,11 为 12.5Hz

  • CTRL_REG2

    Reg Address is 0x21, R/W and default is 0x00

    1. BOOT 用于重置寄存器的默认值

    2. Heater 用于启动加热器,恢复传感器环境温度

    3. ONE_SHOT 用于当 ODR 为 00 时,ONE_SHOT 置 1 为使能一次读数据指令,读完后该位会硬件清 0

  • CTRL_REG3

    Reg Address is 0x22, R/W and default is 0x00

    1. DRDY_H_L 配置 DRDY_EN 即 Pin3 管脚低有效还是高有效,0 为高有效,1 为低有效

    2. PP_OD 控制 Pin3 管脚,置 1 为 open drain,置 0 为 push pull

    3. DRDY_EN 输出到 Pin3 管脚,为 1 时说明数据可读,为 0 时数据不可读,数据读取之后会自动清 0

      DRDY_EN = STATUS_REG[1] | STATUS_REG[0]

  • STATUS_REG

    Reg Address is 0x27, read only and default is 0x00

    1. H_DA / T_DA 在 one-shot 或者每一个 ODR 频率周期时,自动置 1,表示数据可读,读取后自动清 0
  • HUMIDITY_OUT_L

    Reg Address is 0x28, read only

    湿度数据 LSB

  • HUMIDITY_OUT_H

    Reg Address is 0x29, read only

    湿度数据 MSB

    HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_HUMIDITY_OUT_L), buffer, 2);
    *H_T_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];
    
  • TEMP_OUT_L

    Reg Address is 0x2A, read only

    温度数据 LSB

  • TEMP_OUT_H

    Reg Address is 0x2B, read only

    温度数据 MSB

    HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_TEMP_OUT_L), buffer, 2);
    *T_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];
    
  • CALIB_0…F

    Reg Address is 0x30 - 0x3F, R/W

    用于校准,出厂时存在内部 non-volatile memory 中,配合 HUMIDITY_OUT / TEMP_OUT 得到准确的温湿度数据

    1. Humidity Calculation
      H R H [ % ] = ( H 1 _ r H − H 0 _ r H ) × ( H _ T _ O U T − H 0 _ T 0 _ O U T ) H 1 _ T 0 _ O U T − H 0 _ T 0 _ O U T + H 0 _ r H H_{RH}[\%] = \frac{(H1\_rH - H0\_rH) × (H\_T\_OUT - H0\_T0\_OUT)}{H1\_T0\_OUT - H0\_T0\_OUT} + H0\_rH HRH​[%]=H1_T0_OUT−H0_T0_OUT(H1_rH−H0_rH)×(H_T_OUT−H0_T0_OUT)​+H0_rH

    2. Temperature Calculation
      T [ d e g C ] = ( T 1 _ d e g C − T 0 _ d e g C ) × ( T _ O U T − T 0 _ O U T ) T 1 _ O U T − T 0 _ O U T + T 0 _ d e g C T[degC] = \frac{(T1\_degC - T0\_degC) × (T\_OUT - T0\_OUT)}{T1\_OUT - T0\_OUT} + T0\_degC T[degC]=T1_OUT−T0_OUT(T1_degC−T0_degC)×(T_OUT−T0_OUT)​+T0_degC

HTS221 Humidity and Temperature Calculation

//
HTS221_Error_Typedef HTS221_Humidity_Calibration_Get(I2C_TypeDef *I2Cx)
{u8 buffer[2] = {0, 0};if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_H0_RH_X2), buffer, 2))return HTS221_ERROR;H_Cal_InitStruct.H0_rH = (int16_t)(buffer[0] >> 1);                         // H0_rH = H0_rH_x2 / 2H_Cal_InitStruct.H1_rH = (int16_t)(buffer[1] >> 1);                         // H1_rH = H1_rH_x2 / 2if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_H0_T0_OUT_L), buffer, 2))return HTS221_ERROR;H_Cal_InitStruct.H0_T0_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_H1_T0_OUT_L), buffer, 2))return HTS221_ERROR;H_Cal_InitStruct.H1_T0_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];return HTS221_OK;
}//
HTS221_Error_Typedef HTS221_Temperature_Calibration_Get(I2C_TypeDef *I2Cx)
{u8 buffer[4] = {0, 0, 0, 0};uint8_t temp;if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_T0_DEGC_X8), buffer, 2))return HTS221_ERROR;if(HTS221_Reg_Read(I2Cx, HTS221_T1_T0_MSB, &temp, 1))return HTS221_ERROR;T_Cal_InitStruct.T0_degC_MSB = (int16_t)(temp & 0x03);T_Cal_InitStruct.T1_degC_MSB = (int16_t)((temp & 0x0C) >> 2);T_Cal_InitStruct.T0_degC = ((T_Cal_InitStruct.T0_degC_MSB << 8) | buffer[0]) >> 3;T_Cal_InitStruct.T1_degC = ((T_Cal_InitStruct.T1_degC_MSB << 8) | buffer[1]) >> 3;if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_H0_T0_OUT_L), buffer, 4))return HTS221_ERROR;T_Cal_InitStruct.T0_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];T_Cal_InitStruct.T1_OUT = (uint16_t)(buffer[3] << 8) | (uint16_t)buffer[2];return HTS221_OK;
}
HTS221_Error_Typedef HTS221_HUMIDITY_OUT_Get(I2C_TypeDef *I2Cx, int16_t *H_T_OUT)
{u8 buffer[2] = {0, 0};u8 one_shot = HTS221_CR2_ONE_SHOT;if(HTS221_Reg_Write(I2Cx, HTS221_CTRL_REG2, &one_shot, 1))return HTS221_ERROR;if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_HUMIDITY_OUT_L), buffer, 2))return HTS221_ERROR;*H_T_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];return HTS221_OK;
}
HTS221_Error_Typedef HTS221_TEMPERATURE_OUT_Get(I2C_TypeDef *I2Cx, int16_t *T_OUT)
{u8 buffer[2] = {0, 0};u8 one_shot = HTS221_CR2_ONE_SHOT;if(HTS221_Reg_Write(I2Cx, HTS221_CTRL_REG2, &one_shot, 1))return HTS221_ERROR;if(HTS221_Reg_Read(I2Cx, CONTINUOUS_ADDRESS(HTS221_TEMP_OUT_L), buffer, 2))return HTS221_ERROR;*T_OUT = (uint16_t)(buffer[1] << 8) | (uint16_t)buffer[0];return HTS221_OK;
}//
HTS221_Error_Typedef HTS221_Humidity_Calculation(I2C_TypeDef *I2Cx, uint16_t *value)
{int16_t H_T_OUT = 0x00;if(HTS221_HUMIDITY_OUT_Get(I2Cx, &H_T_OUT))return HTS221_ERROR;int32_t temp;temp = ((int32_t)(H_T_OUT - H_Cal_InitStruct.H0_T0_OUT)) * ((int32_t)(H_Cal_InitStruct.H1_rH - H_Cal_InitStruct.H0_rH) * 10);*value = (uint16_t)((temp / (H_Cal_InitStruct.H1_T0_OUT - H_Cal_InitStruct.H0_T0_OUT)) + H_Cal_InitStruct.H0_rH * 10);if(*value > 1000){*value = 1000;}printf("Get Humidity value : %d.%d %% \n", *value / 10, *value % 10);return HTS221_OK;
}//
HTS221_Error_Typedef HTS221_Temperature_Calculation(I2C_TypeDef *I2Cx, int16_t *value)
{int16_t T_OUT = 0x00;if(HTS221_TEMPERATURE_OUT_Get(I2Cx, &T_OUT))return HTS221_ERROR;int32_t temp;temp = ((int32_t)(T_OUT - T_Cal_InitStruct.T0_OUT)) * (((int32_t)(T_Cal_InitStruct.T1_degC - T_Cal_InitStruct.T0_degC) * 10));*value = (temp / (T_Cal_InitStruct.T1_OUT - T_Cal_InitStruct.T0_OUT)) + T_Cal_InitStruct.T0_degC * 10;printf("Get Temperature value : %d.%d C \n", *value / 10, *value % 10);return HTS221_OK;
}

The End

发现我内心世界真的非常 INFP,虽然我以前并没有意识到我是这样的……

  1. 压力都是自己给的,批评也是自己给的
  2. 从不后悔自己的决定,但是希望世界毁灭或人生重来
  3. 每个 INFP 都想成为艺术家,但因为怕吃不饱饭而没有成为艺术家
  4. 让我郁闷的是,我并不擅长 INFP 性格擅长的东西