主控芯片: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
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
- VDD: Power supply
- SCL / SPC: I2C serial clock / SPI serial port clock
- DRDY: Data Ready output signal
- SDA / SDI / SDO: I2C serial data / 3-wrie SPI serial data input or output
- GND: Ground
- 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:
-
Master 发送 START 信号(SCL hold high, SDA high to low) -
Master 发送 Slave 的 7 位地址 + 写信号 -
Slave 进行地址匹配,匹配成功回应 SAK -
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)
-
Slave 应答 SubAddr -
Master 发送 DATA,Slave 应答 SAK -
Master 发送完后,产生 STOP 信号(SCL hold high, SDA lowto high)
Master read data from Slave
Steps:
- Master 发送 START 信号(SCL hold high, SDA high to low)
- Master 发送 Slave 的 7 位地址 + 写信号
- Slave 进行地址匹配,匹配成功回应 SAK
- Master 发送 SubAddress,即 Slave 内部寄存器地址,操作同上
- Slave 应答 SubAddr
- Master 发送 RESTART 信号,重新发送 Slave 的 7 位地址 + 读信号,Slave 回应 SAK
- Slave 发送 Data,Master 应答 MAK
- 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
-
PD 控制传感器开关,置 1 为 active;置 0 为 power-down -
BDU 控制 MSB 和 LSB 数据读取的连续性,置 1 为读完 MSB 和 LSB 之后再更新输出数据;置 0 为实时更新,可能会导致读取的 MSB 和 LSB 不是同一组 -
ODR1 / ODR0 控制数据读取频率,00 为 one-shot,01 为 1Hz,10 为 7Hz,11 为 12.5Hz -
CTRL_REG2 Reg Address is 0x21, R/W and default is 0x00
-
BOOT 用于重置寄存器的默认值 -
Heater 用于启动加热器,恢复传感器环境温度 -
ONE_SHOT 用于当 ODR 为 00 时,ONE_SHOT 置 1 为使能一次读数据指令,读完后该位会硬件清 0 -
CTRL_REG3 Reg Address is 0x22, R/W and default is 0x00
-
DRDY_H_L 配置 DRDY_EN 即 Pin3 管脚低有效还是高有效,0 为高有效,1 为低有效 -
PP_OD 控制 Pin3 管脚,置 1 为 open drain,置 0 为 push pull -
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
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 得到准确的温湿度数据
-
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 -
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);
H_Cal_InitStruct.H1_rH = (int16_t)(buffer[1] >> 1);
if(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,虽然我以前并没有意识到我是这样的……
- 压力都是自己给的,批评也是自己给的
- 从不后悔自己的决定,但是希望世界毁灭或人生重来
- 每个 INFP 都想成为艺术家,但因为怕吃不饱饭而没有成为艺术家
- 让我郁闷的是,我并不擅长 INFP 性格擅长的东西
|