目录
一、简单介绍

I2C通信,典型温度精度0.1度,湿度1.5%。
参考典型应用。

原理图如下:

RESET引脚使能芯片,拉高使能。

ADDR配置I2C总线地址。

如图是0x44。

SHT30上电后等待一段时间才能接收指令,没收到指令就进idle,是为了降低功耗。

采样时间越久,采样一致性越高。
二、cubeMX配置
开调试口

开高速外部晶振

开I2C1

时钟设置

三、驱动开发
芯片有两种测量模式,单次采样和周期采样模式
单次采样

MCU根据采样时间短中长和时钟是否延伸,有6种命令选择。
MCU用命令触发采样后,需要一小会等待,才能从SHT30获取数据。
图中空白是MCU发出,灰色是SHT30发出。
SHT30返回数据被MCU接收到,MCU要发ACK,否则SHT30就不继续回传数据。
先返回温度数据+CRC再返回湿度数据+CRC。
有一个时钟延伸的概念

不开启时钟延伸则发送读请求,SHT30回复NACK直到采集完毕。
开启时钟延伸则SHT30拉低SCL,直到采集完毕。
触发采样
cpp
void SHT30_Single_Meas()
{
uint8_t temp[2] = {0x24, 0x0B};
HAL_I2C_Master_Transmit(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 2, 0xFF);
}

读温湿度
cpp
HAL_StatusTypeDef SHT30_RD_Meas()
{
HAL_StatusTypeDef ret;
ret = HAL_I2C_Master_Receive(SHT30_I2C, SHT30_I2C_ADDRESS, re, 6, 0xFF);
return ret;
}

|----|--------|------|
| | 数据 | CRC |
| 温度 | 0x6663 | 0x66 |
| 湿度 | 0x4AC8 | 0x18 |
转换公式如下:

以摄氏度为例,对温度而言一个LSB大约175/65535=0.00267度。
湿度值=采样值/65565 * 100%。
温度值=采样值*0.00267度 - 45。
当前温度就是0x6663*0.00267 - 45=24.716度。
温湿度解析
cpp
SHT30_ValueType SHT30_GetValue()
{
SHT30_ValueType value;
value.temp = (float)(re[0] * 256 + re[1]) * 0.00267f - 40.0f;
value.humid = (float)(re[3] * 256 + re[4]) * 100.0f / 65535.0f;
if (SHT30_CRC(re, 3) | SHT30_CRC(re + 3, 3))
{
/* crc error */
value.crcError = 1;
}
else
{
value.crcError = 0;
}
return value;
}
周期采样

最快可以1秒采10次,最慢2秒1次。

触发周期采样
cpp
void SHT30_Period_Meas()
{
// uint8_t temp[2] = {0x27, 0x21}; /* 10 MPS */
// uint8_t temp[2] = {0x23, 0x22}; /* 4 MPS */
uint8_t temp[2] = {0x21, 0x26};/* 1 MPS */
HAL_I2C_Master_Transmit(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 2, 0xFF);
}
读温湿度


与单次采样不同,读取周期采样的数据,需要先发送Fetch指令,再读取数据
cpp
void SHT_Fetch_Meas()
{
uint8_t temp[2] = {0xE0, 0x00};
HAL_I2C_Master_Transmit(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 2, 0xFF);
}
如果选择1sample/s,但是读数据频率却比1hz快,就有概率读不到东西

波形如下

直到数据采集完毕

读芯片状态


发送0xF32D后就可以读回芯片的状态寄存器内容
cpp
void SHT30_RD_Status()
{
uint8_t temp[3] = {0xF3, 0x2D, 0x00};
HAL_I2C_Master_Transmit(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 2, 0xFF);
HAL_I2C_Master_Receive(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 3, 0xFF);
SHT30_status = temp[0] * 256 + temp[1];
}

CRC校验

SHT采用的是CRC8算法。
cpp
static uint8_t SHT30_CRC(uint8_t* data, uint8_t len)
{
uint8_t crc = 0xFF;
while (len--)
{
crc ^= *(data++);
for (uint8_t i = 0; i < 8; i++)
{
if (crc & 0x80)
{
crc = (crc << 1) ^ 0x31;
}
else
{
crc = (crc << 1);
}
}
}
return crc;
}
复位

芯片支持reset引脚复位和i2c总线指令复位,后者可以单独发送复位指令或者通用复位。
区别在于单独复位指定地址的SHT30还是复位这条i2c总线上的所有SHT30
芯片也提供一个加热器,为了避免出现"少玩5毛钱"的情况,笔者也使用一番。
加热效果微弱,正如手册描述的那样"lies in the range of a few degrees centigrade."
cpp
void SHT30_Reset()
{
uint8_t temp[2] = {0x00, 0x06};
HAL_I2C_Master_Transmit(SHT30_I2C, SHT30_I2C_ADDRESS, temp, 2, 0xFF);
}
主函数
使用1.54寸TFT彩屏打印采集到的温湿度数据和芯片状态
cpp
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t str[20];
SHT30_ValueType temp_humid;
uint16_t deva;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2C1_Init();
MX_SPI2_Init();
/* USER CODE BEGIN 2 */
ST7789_Init();
ST7789_Clear(BLACK);
SHT30_Reset();
SHT30_CLR_Status();
HAL_Delay(10);
#ifdef PERIOD
SHT30_Period_Meas();
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
sprintf(str, "temp:%.2f'C ", temp_humid.temp);
ST7789_ShowString(0, 90, str, Font_16x26, ST7789_RGB565(0xc0a91e), BLACK);
sprintf(str, "humid:%.0f%% ", temp_humid.humid);
ST7789_ShowString(0, 120, str, Font_16x26, ST7789_RGB565(0x1185e5), BLACK);
sprintf(str, "CRC:%d", temp_humid.crcError);
ST7789_ShowString(0, 150, str, Font_16x26, ST7789_RGB565(0xe74033), BLACK);
sprintf(str, "status:0x%04X", SHT30_status);
ST7789_ShowString(0, 180, str, Font_16x26, ST7789_RGB565(0x43b131), BLACK);
sprintf(str, "DEVA:%d", deva);
ST7789_ShowString(0, 210, str, Font_16x26, ST7789_RGB565(0xca64ea), BLACK);
#ifdef PERIOD
SHT_Fetch_Meas();
#else
SHT30_Single_Meas();
HAL_Delay(20);
#endif
if (SHT30_RD_Meas() != HAL_OK)
{
deva++;
}
temp_humid = SHT30_GetValue();
SHT30_RD_Status();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
效果如下
