SHT30温湿度传感器
模块来源
采购链接:
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.204e2e8d2hPeAl&id=556043263770&_u=h2t4uge5cf4f
资料下载链接:
https://pan.baidu.com/s/1kisMJspcV6Qdr1ye9ElOlQ
规格参数
工作电压:2.4-5.5V
工作电流:0.2~1500uA
温度测量范围:-40~125℃
温度测量精度:±0.3℃
湿度测量范围:0~100%RH
湿度测量精度:±2%RH
输出方式: IIC
管脚数量:4 Pin
原理解析
模块原理图
SHT30地址
数据手册上说明,当ADDR引脚接入VSS(接地)时,地址为0X44。而原理图上已经通过R14这个下拉电阻接地。不过需要注意的是,实际地址为0X44左移一位,因需要空出最低位给读写位,所以实际的地址是 0X44<<1。
测量模式
SHT30有两种测量模式,分别是单次测量模式和周期测量模式。
在单次测量模式下,发出一个测量命令就触发一次数据采集。每个数据都由一个16位的温度值和一个16位的湿度值(按此顺序)组成。在传输过程中,每个数据值后面总是跟着一个CRC校验和。但是在该模式下又分有时钟拉伸模式和时钟不拉伸模式,具体情况见下图。
并且在单次测量模式下,可以选择不同的测量命令。它们在可重复性(低、中、高)和时钟拉伸(启用或禁用)方面有所不同。这里的可重复性设置影响测量持续时间,从而影响传感器的总体能耗。
在周期测量模式下,时钟拉伸模式禁用,但是可以分为高中低的可重复性测量,测量周期为0.5、1、2、4、10(单位 次/秒)(这种模式下最快的测量速度是1秒10次)如果传感器在一种工作模式下正在测量数据,此时要发送其他命令(推荐先发送一次中断命令),让传感器停止当前的测量,进入单次测量模式,然后再发送命令。这里需要注意:如果测量频率过高,会导致传感器自热。
设置好周期测量模式的测量周期和可重复性强度后,随时可以进行测量读取数据,需要发送一个读取命令(0XE000)。一旦读取时序结束之后,寄存器中的数值就会清零,如果这时再一次读取数据将得到0。下一次测量结束后,寄存器的值就会重新写入。
移植过程
引脚选择
移植至工程
我们的目标是将例程移植至ESP32-S3开发板上。已经为大家提供了完整的驱动代码,按照以下步骤,即可完成移植。
具体新建文件夹和新建c和h文件在 【DHT11温湿度传感器】章节中的1.4.2小节中有详细的教学,这里就不再多说了。
只不过这里我们将文件名 bsp_dht11.c 和 bsp_dht11.h 换成 bsp_sht30.c 和 bsp_sht30.h,文件夹名字改为SHT30。
代码写入
在 bsp_sht30.c 文件中写入:
#include "bsp_sht30.h"
#include "stdio.h"
double Temperature = 0.0, Humidity = 0.0;
void delay_ms(unsigned int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
void delay_us(unsigned int us)
{
ets_delay_us(us);
}
/******************************************************************
* 函 数 名 称:SHT30_GPIO_Init
* 函 数 说 明:SHT30的引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void SHT30_GPIO_Init(void)
{
gpio_config_t SHT30_config = {
.pin_bit_mask = (1ULL<<SHT30_SCL_PIN)|(1ULL<<SHT30_SDA_PIN), //配置引脚
.mode =GPIO_MODE_OUTPUT, //输出模式
.pull_up_en = GPIO_PULLUP_ENABLE, //使能上拉
.pull_down_en = GPIO_PULLDOWN_DISABLE, //不使能下拉
.intr_type = GPIO_INTR_DISABLE //不使能引脚中断
};
gpio_config(&SHT30_config);
}
/******************************************************************
* 函 数 名 称:IIC_Start
* 函 数 说 明:IIC起始时序
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SCL(1);
SDA(0);
SDA(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
}
/******************************************************************
* 函 数 名 称:IIC_Stop
* 函 数 说 明:IIC停止信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Send_Ack
* 函 数 说 明:主机发送应答或者非应答信号
* 函 数 形 参:0发送应答 1发送非应答
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* 函 数 名 称:I2C_WaitAck
* 函 数 说 明:等待从机应答
* 函 数 形 参:无
* 函 数 返 回:0有应答 1超时无应答
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char I2C_WaitAck(void)
{
char ack = 0;
unsigned char ack_flag = 50;
SCL(0);
SDA(1);
SDA_IN();
SCL(1);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(1);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* 函 数 名 称:Send_Byte
* 函 数 说 明:写入一个字节
* 函 数 形 参:dat要写人的数据
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void Send_Byte(u8 dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
delay_us(2);
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
/******************************************************************
* 函 数 名 称:Read_Byte
* 函 数 说 明:IIC读时序
* 函 数 形 参:无
* 函 数 返 回:读到的数据
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( SDA_GET() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
/******************************************************************
* 函 数 名 称:SHT31_Write_mode
* 函 数 说 明:在周期模式下设置测量周期与可重复性命令
* 函 数 形 参:dat设置命令
常用的有:每一秒采集0.5次 0x2024
每一秒采集1次 0x2126
每一秒采集2次 0x2220
每一秒采集4次 0x2334
每一秒采集10次 0x2721
* 函 数 返 回:
* 作 者:LC
* 备 注:
******************************************************************/
char SHT31_Write_mode(uint16_t dat)
{
IIC_Start();
// << 1 是将最后一位置0,设置为写命令
Send_Byte((0X44 << 1) | 0 );
//返回0为产生了应答,返回1说明通信失败
if( I2C_WaitAck() == 1 )return 1;
//发送命令的高8位
Send_Byte((dat >> 8 ) );
//返回0为产生了应答,返回1说明通信失败
if( I2C_WaitAck() == 1 )return 2;
//发送命令的低8位
Send_Byte(dat & 0xff );
//返回0为产生了应答,返回1说明通信失败
if( I2C_WaitAck() == 1 )return 3;
// IIC_Stop();
return 0;
}
/******************************************************************
* 函 数 名 称:crc8
* 函 数 说 明:CRC校验
* 函 数 形 参:data要校验的数据地址 len要校验的长度
* 函 数 返 回:校验后的值
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char crc8(const unsigned char *data, int len)
{
const unsigned char POLYNOMIAL = 0x31;
unsigned char crc = 0xFF;
int j, i;
for (j=0; j<len; j++)
{
crc ^= *data++;
for ( i = 0; i <8; i++ )
{
crc = ( crc & 0x80 ) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
}
}
return crc;
}
/******************************************************************
* 函 数 名 称:SHT30_Read
* 函 数 说 明:读取温湿度值
* 函 数 形 参:dat读取的命令
周期模式命令为:0xe000
单次模式命令为:0x2c06 or 0x2400
* 函 数 返 回:0读取成功 其他失败
* 作 者:LC
* 备 注:当前为周期模式读取,如使用单次模式,则将
* 【设置周期模式命令】下的命令注释即可。
******************************************************************/
char SHT30_Read(uint16_t dat)
{
uint16_t i = 0;
unsigned char buff[6] = {0};
uint16_t data_16 = 0;
float temp = 0;
float humi = 0;
//设置周期模式命令
SHT31_Write_mode(0x2130);//每1秒一次高重复测量(需要在周期模式下才有用)
IIC_Start();
Send_Byte( (0x44<<1) | 0);
if( I2C_WaitAck() == 1 )return 1;
Send_Byte((dat >> 8 ));
if( I2C_WaitAck() == 1 )return 2;
Send_Byte( dat & 0xff );
if( I2C_WaitAck() == 1 )return 3;
//如不使用超时判断,很容易数据错乱
do
{
//超时判断
i++;
if( i > 20 ) return 4;
delay_ms(2);
IIC_Start();
Send_Byte((0X44 << 1) | 1 );//读
}while(I2C_WaitAck() == 1); //读取到温湿度数据则结束读命令
//温度高8位
buff[0] = Read_Byte();
IIC_Send_Ack(0);
//温度低8位
buff[1] = Read_Byte();
IIC_Send_Ack(0);
//温度CRC校验值
buff[2] = Read_Byte();
IIC_Send_Ack(0);
//湿度高8位
buff[3] = Read_Byte();
IIC_Send_Ack(0);
//湿度低8位
buff[4] = Read_Byte();
IIC_Send_Ack(0);
//湿度CRC校验值
buff[5] = Read_Byte();
IIC_Send_Ack(1);
IIC_Stop();
//CRC校验(将要校验的数值带入,查看计算后的校验值是否和读取到的校验值一致)
if( (crc8(buff,2) == buff[2]) && ( crc8(buff+3,2) == buff[5]) )
{
//计算温度值
data_16 =(buff[0]<<8) | buff[1];
Temperature = (data_16/65535.0)*175.0 - 45;
//计算湿度值
data_16 = 0;
data_16 =(buff[3]<<8) | buff[4];
Humidity = (data_16/65535.0) * 100.0;
printf("temp = %.2f\r\n",Temperature);
printf("humi = %.2f\r\n",Humidity);
return 0;
}
else
{
printf("Check failure!\r\n");
}
return 5;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
在 bsp_sht30.h文件中写入:
#ifndef _BSP_SHT30_H_
#define _BSP_SHT30_H_
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "freertos/queue.h"
#include <inttypes.h>
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "rom/ets_sys.h"
#include "esp_system.h"
#include "driver/gpio.h"
extern double Temperature, Humidity;
//端口移植
#define SHT30_SCL_PIN 10
#define SHT30_SDA_PIN 40
//设置SDA输出模式
#define SDA_OUT() {gpio_set_direction(SHT30_SDA_PIN, GPIO_MODE_OUTPUT);}
//设置SDA输入模式
#define SDA_IN() {gpio_set_direction(SHT30_SDA_PIN, GPIO_MODE_INPUT);}
//获取SDA引脚的电平变化
#define SDA_GET() gpio_get_level(SHT30_SDA_PIN)
//SDA与SCL输出
#define SDA(x) gpio_set_level(SHT30_SDA_PIN, x?1:0)
#define SCL(x) gpio_set_level(SHT30_SCL_PIN, x?1:0)
#define u8 unsigned char
void delay_us(unsigned int us);
void delay_ms(unsigned int ms);
void SHT30_GPIO_Init(void);
char SHT30_Read(uint16_t dat);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
移植验证
在main.c中输入代码如下
#include <stdio.h>
#include "bsp_sht30.h"
void app_main(void)
{
SHT30_GPIO_Init();
printf("SHT30 Start...\n");
while(1)
{
int i = SHT30_Read(0xe000);
delay_ms(1000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
上电现象:
驱动代码(已适配):