2.24 SHT20温湿度传感器
由瑞士Sensirion推出的 SHT20数字温湿度传感器,基于领先世界的CMOSens ® 数字传感技术,具有极高的可靠性和卓越的长期稳定性。全量程标定,两线数字接口,可与单片机直接相连,大大缩短研发时间、简化外围电路并降低费用。此外,体积微小、响应迅速、低能耗、可浸没、抗干扰能力强、温湿一体,兼有露点测量,性价比高,使该产品能够适于多种场合的应用。
2.24.1 模块来源
采购链接:
SHT20温湿度传感器模块/数字型温湿度测量模块 I2C通讯小体积模块
资料下载链接:
https://pan.baidu.com/s/1HrQkwECvGgQSHvt_RNdLdA
2.24.2 规格参数
工作电压:2.1~3.6V
工作电流:0.1~1000uA
温度精度:±0.3℃
温度范围:-40~125℃
湿度范围:0~100 %RH
湿度精度:±3%RH
输出方式: IIC
管脚数量:4 Pin
2.24.3 移植过程
我们的目标是在梁山派GD32F470上能够测量环境温湿。首先要获取资料,查看数据手册应如何实现,再移植至我们的工程。
2.24.3.1 查看资料
开始传输后,随后先传输首字节包括I2C设备地址(7bit)和一个SDA方向位(R:1, W:0)。
一个时钟发送一个位,在第8个下降沿之后,通过拉低SDA引脚(ACK位 为0),只是传感器数据接收正常。在发出测量命令之后(‘1110’‘0011’代表温度测量,‘1110’‘0101’代表相对湿度测量 ,这种为主机模式),MCU必须等待测量完成。
主机模式和主机模式:①主机模式:测量过程中,SCL线被封锁(由传感器控制),在测量时,SHT2x将SCL拉低强制主机进入等待状态。当释放SCL线,表示传感器内部工作接收,可以继续进行数据传送
灰色的部分是传感器控制的,当传感器给MCU返回数据时,每返回一个字节,MCU要返回一个ACK信号,当接收完毕之后,返回一个NACK并接着传输停止时序(P)。
注:校验和可以不需要,不需要则就在数据接收完之后返回NACK
②非主机模式:测量过程中,SCL线是开发状态,可进行其它通信,可以在总线上处理其它I2C总线通信任务。
当MCU要对传感器状态进行查询时,先发起一个开始信号,在发送从机地址和SDA方向位(写),此时从机匹配地址成功,则发送ACK信号,并开始测量。如果传感器完成了测量过程,并且发送ASK信号,那么MCU就可以读取相关数据。如果测量没有完成,传感器发送NACK信号,此时MCU必须重新发送启动传输时序,直至测量完成,MCU读取数据。
注意:测量的数据,温度和湿度均为两个字节。而且无论哪一种传输模式,测量的最大分辨率最大是14bit,数据的第二个字节SDA上最后两位是用来标记相关状态信息。其中bit1表示测量类型(0是温度,1是湿度)
灰色的区域是传感器控制的,如果不需要校验和,那么在接收完两个字节的数据之后就MCU直接发出NACK信号再接着发出停止时序§,则结束通信。
通常测量的最长时间取决于测量类型和分辨率.
在计算MCU通信时间时,测量温度选择最长测量时间是85ms,而测量相对湿度选择最长的时间是29ms。
传感器内部设置的默认分辨率为相对湿度12位和温度14位。SDA的输出数据被转换成两个字节的数据包,高字节MSB在前(左对齐)。每个字节后面都跟随一个应答位。两个状态位,即 LSB的后两位在进行物理计算前须置0。
计算湿度:其中SRH表示我们读取到的湿度原始数据。
计算温度:其中ST表示我们读取到的温度原始数据。
2.24.3.2 引脚选择
2.24.3.3 移植至工程
移植步骤中的导入.c和.h文件与上一节相同,只是将.c和.h文件更改为bsp_sht20.c与bsp_sht20.h。见2.2.3.3 移植至工程。这里不再过多讲述。移植完成后面修改相关代码。
在文件bsp_sht20.c中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_sht20.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2023年04月27日
* 功能介绍:
******************************************************************************
* 注意事项:
*********************************************************************************/
#include "bsp_sht20.h"
#include "systick.h"
#include "stdio.h"
/******************************************************************
* 函 数 名 称:SHT20_GPIO_Init
* 函 数 说 明:对IIC引脚初始化
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:
******************************************************************/
void SHT20_GPIO_Init(void)
{
//打开SDA与SCL的引脚时钟
rcu_periph_clock_enable(RCU_SCL);
rcu_periph_clock_enable(RCU_SDA);
//设置SCL引脚模式为上拉输出
gpio_mode_set(PORT_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SCL);
//设置引脚为开漏模式,翻转速度2MHz
gpio_output_options_set(PORT_SCL, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_SCL);
//设置引脚输出高电平SCL等待信号
gpio_bit_write(PORT_SCL, GPIO_SCL, SET);
//设置SDA引脚
gpio_mode_set(PORT_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SDA);
gpio_output_options_set(PORT_SDA, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO_SDA);
gpio_bit_write(PORT_SDA, GPIO_SDA, SET);
}
/******************************************************************
* 函 数 名 称:IIC_Start
* 函 数 说 明:IIC起始信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_1us(5);
SCL(1);
delay_1us(5);
SDA(0);
delay_1us(5);
SCL(0);
delay_1us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Stop
* 函 数 说 明:IIC停止信号
* 函 数 形 参:无
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_1us(5);
SDA(1);
delay_1us(5);
}
/******************************************************************
* 函 数 名 称:IIC_Send_Ack
* 函 数 说 明:主机发送应答
* 函 数 形 参:0应答 1非应答
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_1us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_1us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* 函 数 名 称:IIC_Wait_Ack
* 函 数 说 明:等待从机应答
* 函 数 形 参:无
* 函 数 返 回:1=无应答 0=有应答
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char IIC_Wait_Ack(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SDA_IN();
SDA(1);
delay_1us(5);
SCL(1);
delay_1us(5);
while( (GETSDA()==1) && ( ack_flag ) )
{
ack_flag--;
delay_1us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* 函 数 名 称:IIC_Write
* 函 数 说 明:IIC写一个字节
* 函 数 形 参:dat写入的数据
* 函 数 返 回:无
* 作 者:LC
* 备 注:无
******************************************************************/
void IIC_Write(unsigned char dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
__nop();
__nop();
__nop();
dat<<=1;
delay_1us(6);
SCL(1);
delay_1us(4);
SCL(0);
delay_1us(4);
}
}
/******************************************************************
* 函 数 名 称:IIC_Read
* 函 数 说 明:IIC读1个字节
* 函 数 形 参:无
* 函 数 返 回:读出的1个字节数据
* 作 者:LC
* 备 注:无
******************************************************************/
unsigned char IIC_Read(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_1us(5);
SCL(1);
delay_1us(5);
receive<<=1;
if( GETSDA() )
{
receive|=1;
}
delay_1us(5);
}
return receive;
}
/******************************************************************
* 函 数 名 称:SHT20_Read
* 函 数 说 明:测量温湿度
* 函 数 形 参:regaddr寄存器地址 regaddr=0xf3测量温度 =0x35测量湿度
* 函 数 返 回:regaddr=0xf3时返回温度,regaddr=0xf5时返回湿度
* 作 者:LC
* 备 注:无
******************************************************************/
float SHT20_Read(unsigned char regaddr)
{
unsigned char data_H = 0;
unsigned char data_L = 0;
float temp = 0;
IIC_Start();
IIC_Write(0x80|0);
if( IIC_Wait_Ack() == 1 ) printf("error -1\r\n");
IIC_Write(regaddr);
if( IIC_Wait_Ack() == 1 ) printf("error -2\r\n");
do{
delay_1us(10);
IIC_Start();
IIC_Write(0x80|1);
}while( IIC_Wait_Ack() == 1 );
delay_1us(20);
data_H = IIC_Read();
IIC_Send_Ack(0);
data_L = IIC_Read();
IIC_Send_Ack(1);
IIC_Stop();
if( regaddr == 0xf3 )
{
temp = ((data_H<<8)|data_L) / 65536.0 * 175.72 - 46.85;
}
if( regaddr == 0xf5 )
{
temp = ((data_H<<8)|data_L) / 65536.0 * 125.0 - 6;
}
return temp;
}
在文件bsp_sht20.h中,编写如下代码。
/********************************************************************************
* 文 件 名: bsp_sht20.h
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月27日
* 功能介绍:
******************************************************************************
* 开发板官网:www.lckfb.com
*********************************************************************************/
#ifndef _BSP_SHT20_H_
#define _BSP_SHT20_H_
#include "gd32f4xx.h"
#define RCU_SCL RCU_GPIOA
#define PORT_SCL GPIOA
#define GPIO_SCL GPIO_PIN_5
#define RCU_SDA RCU_GPIOA
#define PORT_SDA GPIOA
#define GPIO_SDA GPIO_PIN_6
#define SDA_IN() {gpio_mode_set(PORT_SDA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_SDA);} //SDA输入模式
#define SDA_OUT() {gpio_mode_set(PORT_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_SDA);} //SDA输出模式
#define SCL(BIT) gpio_bit_write( PORT_SCL, GPIO_SCL, BIT?SET:RESET)
#define SDA(BIT) gpio_bit_write( PORT_SDA, GPIO_SDA, BIT?SET:RESET)
#define GETSDA() gpio_input_bit_get( PORT_SDA, GPIO_SDA)
void SHT20_GPIO_Init(void);
float SHT20_Read(unsigned char regaddr);
#endif
2.24.4 移植验证
在自己工程中的main主函数中,编写如下。
/********************************************************************************
* 文 件 名: main.c
* 版 本 号: 初版
* 修改作者: LC
* 修改日期: 2022年04月27日
* 功能介绍:
******************************************************************************
* 开发板官网:www.lckfb.com
*********************************************************************************/
#include "gd32f4xx.h"
#include "systick.h"
#include "bsp_usart.h"
#include "stdio.h"
#include "bsp_sht20.h"
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
systick_config(); //滴答定时器初始化 1ms
usart_gpio_config(115200);
SHT20_GPIO_Init();
delay_1ms(1000);
printf("demo start\r\n");
while(1)
{
printf("temperature = %.2f ℃\r\n",SHT20_Read(0xf3));
printf("humidity = %.2f %%RH\r\n",SHT20_Read(0xf5));
delay_1ms(1000);
}
}
移植现象:每隔一秒测量一次温湿度,并发送至串口0。
移植成功示例,见文件2.24.4-1 。