2.11 SHT30 温湿度传感器
2.11.1 模块来源
采购链接: GY-SHT31-D 数字温湿度传感器模块 资料下载链接: https://pan.baidu.com/s/1kisMJspcV6Qdr1ye9ElOlQ
2.11.2 规格参数
工作电压: 2.4-5.5V
工作电流: 0.2~1500uA
温度测量范围: -40~125℃
温度测量精度: ±0.3℃
湿度测量范围: 0~100%RH
湿度测量精度: ±2%RH
输出方式: IIC
管脚数量: 4 Pin
2.11.3 移植过程
我们的目标是在梁山派 GD32F470 上能够测量温湿度的功能。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
2.11.3.1 查看资料
模块原理图
SHT30 地址
数据手册上说明,当 ADDR 引脚接入 VSS(接地)时,地址为 0X44。而原理图上已经通过 R14 这个下拉电阻接地。不过需要注意的是,实际地址为 0X44 左移一位,因需要空出最低位给读写位,所以实际的地址是 0X44<<1。
测量模式
SHT30 有两种测量模式,分别是单次测量模式和周期测量模式。
在单次测量模式下,发出一个测量命令就触发一次数据采集。每个数据都由一个 16 位的温度值和一个 16 位的湿度值(按此顺序)组成。在传输过程中,每个数据值后面总是跟着一个 CRC 校验和。但是在该模式下又分有时钟拉伸模式和时钟不拉伸模式,具体情况见下图。
并且在单次测量模式下,可以选择不同的测量命令。它们在可重复性(低、中、高)和时钟拉伸(启用或禁用)方面有所不同。这里的可重复性设置影响测量持续时间,从而影响传感器的总体能耗。
在周期测量模式下,时钟拉伸模式禁用,但是可以分为高中低的可重复性测量,测量周期为 0.5、1、2、4、10(单位 次/秒)(这种模式下最快的测量速度是 1 秒 10 次)如果传感器在一种工作模式下正在测量数据,此时要发送其他命令(推荐先发送一次中断命令),让传感器停止当前的测量,进入单次测量模式,然后再发送命令。这里需要注意:如果测量频率过高,会导致传感器自热。
设置好周期测量模式的测量周期和可重复性强度后,随时可以进行测量读取数据,需要发送一个读取命令(0XE000)。一旦读取时序结束之后,寄存器中的数值就会清零,如果这时再一次读取数据将得到 0。下一次测量结束后,寄存器的值就会重新写入。
2.11.3.2 引脚选择
2.11.3.3 移植至工程
移植步骤中的导入.c 和.h 文件与上一节相同,只是将.c 和.h 文件更改为 bsp_sht30.c 与 bsp_sht30.h。见 2.2.3.3 移植至工程。这里不再过多讲述。移植完成后面修改相关代码。
在文件 bsp_sht30.c 中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_sht30.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2023年04月19日
* 功能介绍:
******************************************************************************
* 注意事项:
*********************************************************************************/
#include "bsp_sht30.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "systick.h"
//端口移植
#define RCU_SDA RCU_GPIOB
#define PORT_SDA GPIOB
#define GPIO_SDA GPIO_PIN_8
#define RCU_SCL RCU_GPIOB
#define PORT_SCL GPIOB
#define GPIO_SCL GPIO_PIN_9
//设置SDA输出模式
#define SDA_OUT() gpio_mode_set(PORT_SDA,GPIO_MODE_OUTPUT,GPIO_MODE_OUTPUT,GPIO_SDA)
//设置SDA输入模式
#define SDA_IN() gpio_mode_set(PORT_SDA,GPIO_MODE_INPUT,GPIO_MODE_OUTPUT,GPIO_SDA)
//获取SDA引脚的电平变化
#define SDA_GET() gpio_input_bit_get(PORT_SDA,GPIO_SDA)
//SDA与SCL输出
#define SDA(x) gpio_bit_write(PORT_SDA,GPIO_SDA, (x?SET:RESET))
#define SCL(x) gpio_bit_write(PORT_SCL,GPIO_SCL, (x?SET:RESET))
double Temperature = 0.0, Humidity = 0.0;
/******************************************************************
* 函 数 名 称:SHT30_GPIO_Init
* 函 数 说 明:SHT30的引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void SHT30_GPIO_Init(void)
{
/* 使能时钟 */
rcu_periph_clock_enable(RCU_SCL);
rcu_periph_clock_enable(RCU_SDA);
/* 配置SCL为输出模式 */
gpio_mode_set(PORT_SCL,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO_SCL);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(PORT_SCL,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,GPIO_SCL);
/* 配置SDA为输出模式 */
gpio_mode_set(PORT_SDA,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO_SDA);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(PORT_SDA,GPIO_OTYPE_PP,GPIO_OSPEED_50MHZ,GPIO_SDA);
}
/******************************************************************
* 函 数 名 称: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 = 10;
SCL(0);
SDA(1);
SDA_IN();
SCL(1);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
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 );
__nop();
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_1ms(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("校验失败\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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
在文件 bsp_sht30.h 中,编写如下代码。
#ifndef _BSP_SHT30_H_
#define _BSP_SHT30_H_
#include "gd32f4xx.h"
extern double Temperature, Humidity;
#define u8 unsigned char
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
2.11.4 移植验证
在自己工程中的 main 主函数中,编写如下。
/********************************************************************************
* 文 件 名: main.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月19日
* 功能介绍:
******************************************************************************
* 注意事项:
*********************************************************************************/
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "bsp_led.h"
#include "sys.h"
#include "bsp_usart.h"
#include "bsp_key.h"
#include "bsp_basic_timer.h"
#include "bsp_pwm.h"
#include "stdlib.h"
#include "string.h"
#include "bsp_sht30.h"
/************************************************
函数名称 : main
功 能 : 主函数
参 数 : 无
返 回 值 : 无
作 者 : LC
*************************************************/
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
systick_config(); // 滴答定时器初始化
usart_gpio_config(9600U); // 串口0初始化
SHT30_GPIO_Init();
printf("start\r\n");
while(1)
{
SHT30_Read(0xe000);
delay_1ms(1000);
}
}
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
移植现象:每隔 1 秒读取一次温湿度,并通过串口输出。
移植成功示例,见文件 2.11.4-1 。
文件 2.11.4-1 移植成功示例