I2C协议之SHT20传感器采样温湿度

关于I2C协议的文章有很多,在本博客中不涉及I2C协议的基础知识,主要是讲解如何利用SHT20传感器采样温湿度。

SHT20基础知识

在编写代码之前需要先了解SHT20的数据手册,了解发生采样温度、湿度的命令,以及如何根据采样结果计算具体的温湿度的值。数据手册的中文版本

通过官方提供的信息和i2c-tools了解sht20的从设备地址为0x40,常见的命令如下所示:

我们需要使用的命令如红色框中所示,其中T表示温度,RH表示相对湿度。非保持主机是指在SHT20测量期间会释放i2c总线,使得主机可以处理其他从设备的通信任务。软复位用于在无需关闭和再次打开电源的情况下,重新启动传感器系统。在接收到这个命令之后,传感器系统开始重新初始化,并恢复默认设置状态。

在了解了测量命令后,还需知道SHT20的通信时序。

如上图所示,SHT20通信的步骤,大体分为两步:

  • 写入模式,对SHT20写入非主机湿度测量命令
  • 读取模式,读取SHT20返回的值

由上图可知,SHT20返回的数据包含3个字节,其中前两个字节包含测量数据,而第三个字节是校验和。高字节’0110 0011‘,低字节’0101 0010‘,低字节的最后两位是状态位,由图中可知,第43位为1(1:湿度,0:温度),表示这段数据得到的是湿度值。因此可以得知实际有效位只有14位,在计算的过程中需要将最后两位置为0,用16进制表示就是0x6350=25424。得到这个值后再根据以下公式计算湿度值,结果表示为%:

温度的计算公式:

不同测量类型和分辨率的最长测量时间如下:

硬件准备

将SHT20的VCC引脚接在引脚1(3.3V),GND接在引脚6,SDA接在引脚3, SCL接在引脚5.

树莓派默认情况下没有使能i2c接口,因此需要先使能i2c接口

sudo raspi-config

进入配置界面,使用⬆,⬇选择5 Interfacing Opions,按enter进入,选择yes,最后选择ok,按esc键退出配置界面。(如果配置界面出现白色的,只有第一行,不用担心,使用上下箭头选择后也会出现5)

配置完成后,检查i2c驱动是否使能成功

ls /dev/*i2c*

出现 /dev/i2c-0或dev/i2c-1即是成功。

安装i2c-tools

sudo apt-get install i2c-tools

使用i2c-tools查看SHT20地址

i2cdetect -y 1

1表示查看的总线设备。在ls /dev/*i2c*后返回的是 /dev/i2c-0,这个地方就填0,返回的是 /dev/i2c-1,就填1.

代码解析

代码流程:

初始化i2c总线设备--->发送测量命令---> 读取测量数据--->根据公式计算测量值--->得到最终结果

1. 初始化i2c总线

  • 参数合法性检验
  • 以读写模式打开i2c总线
  • 使用ioctl配置i2c总线,I2C_TENBIT用于设置从设备的地址长度,0表示使用7位地址(1表示10位地址);I2C_SLAVE用于设置从设备地址,SHT20的地址为0x40.
  • 使用软复位1111 1110,软复位的休眠时间不超过15毫秒。
int sht2x_init(char *i2c_dev)
{
	int         fd;

	if(i2c_dev == NULL)
	{
		printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
		return -1;
	}

	fd = open(i2c_dev, O_RDWR);
	if(fd < 0)
	{
		printf("i2c device open failed: %s\n", strerror(errno));
		return -1;
	}

	ioctl(fd, I2C_TENBIT, 0);
	ioctl(fd, I2C_SLAVE, 0x40);

	if(sht2x_softreset(fd) < 0)
	{
		printf("SHT2x softreset failure\n");
		return -2;
	}

	return fd;
}
int sht2x_softreset(int fd)
{
    uint8_t           buf[4];

    if( fd<0 )
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    /* software reset SHT2x */
    memset(buf, 0, sizeof(buf));

    /* 发送软复位命令 并休眠需要的时间 */
    buf[0] = 0xFE;
    write(fd, buf, 1);

    msleep(50);

    return 0;
}

2. 获取温湿度

分别读取温度和湿度值

  • 参数合法性检测
  • 发送测量温度的命令:0xF3 = 1111 0011,由表7可知,当分辨率为14位时,测量温度需要的时间最长为85ms,测量完成后读取3个字节的数据,前两个字节包含测量数据,最后一个字节是CRC校验和。根据公式计算温度值

  • 发送测量湿度的命令:0xF5 = 1111 1001。

    int sht2x_get_temp_humidity(int fd, float *temp, float *rh)
    {
        uint8_t           buf[4];
    
        if( fd<0 || !temp || !rh )
        {
            printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
            return -1;
        }
    
        memset(buf, 0, sizeof(buf));
        /* 发送非主机保持的温度测量命令 */
        buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
        write(fd, buf, 1);
    
        msleep(85); 
    
        memset(buf, 0, sizeof(buf));
        /* 读取三个字节的数据,最后一个字节为crc检验值 */
        read(fd, buf, 3);
        dump_buf("Temperature sample data: ", buf, 3);
        /* 计算实际温度值*/
        *temp = 175.72 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 46.85;
    
        memset(buf, 0, sizeof(buf));
        /* 发送非主机保持的湿度测量命令 */
        buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
        write(fd, buf, 1);
    
        msleep(29); 
        memset(buf, 0, sizeof(buf));
    
        read(fd, buf, 3);
        dump_buf("Relative humidity sample data: ", buf, 3);
        /* 计算实际湿度值*/
        *rh = 125 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 6;
    
        return 0;
    }

    3. 完整可运行的代码

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/types.h>
    #include <sys/stat.h>
    #include <linux/i2c-dev.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <string.h>
    #include <stdint.h>
    #include <time.h>
    #include <errno.h>
    #include <string.h>
    
    #define SOFTRESET                        0xFE
    #define TRIGGER_TEMPERATURE_NO_HOLD      0xF3
    #define TRIGGER_HUMIDITY_NO_HOLD         0xF5
    
    static inline void msleep(unsigned long ms);
    int sht2x_init(char *i2c_dev);
    int sht2x_get_temp_humidity(int fd, float *temp, float *rh);
    
    int main(int argc, char **argv)
    {
        int         fd;
        float       temp;
        float       rh;
    
        if( argc != 2)
        {
            printf("This program used to do I2C test by sht20 temperature & humidity module.\n");
            printf("Usage: %s /dev/i2c-x\n", argv[0]);
            return 0;
        }
    
        fd = sht2x_init(argv[1]);
        if(fd < 0)
        {
            printf("SHT2x initialize failure\n");
            return 1;
        }
    
        if( sht2x_get_temp_humidity(fd, &temp, &rh) < 0 )
        {
            printf("SHT2x get get temperature and relative humidity failure\n");
            return 3;
        }
    
        printf("Temperature=%lf ℃ relative humidity=%lf%\n", temp, rh);
    
        close(fd);
    }
    
    /* ms级休眠函数 */
    static inline void msleep(unsigned long ms)
    {
        struct timespec cSleep;
        unsigned long ulTmp;
    
        cSleep.tv_sec = ms / 1000;
        if (cSleep.tv_sec == 0)
        {
            ulTmp = ms * 10000;
            cSleep.tv_nsec = ulTmp * 100;
        }
        else
        {
            cSleep.tv_nsec = 0;
        }
    
        nanosleep(&cSleep, 0);
    }
    
    /* sht20命令复位 */
    int sht2x_softreset(int fd)
    {
        uint8_t           buf[4];
    
        if( fd<0 )
        {
            printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
            return -1;
        }
    
        /* software reset SHT2x */
        memset(buf, 0, sizeof(buf));
    
        /* 发送软复位命令 并休眠需要的时间 */
        buf[0] = SOFTRESET;
        write(fd, buf, 1);
    
        msleep(50);
    
        return 0;
    }
    
    /* 初始化sht20 形参为i2c总线节点 */
    int sht2x_init(char *i2c_dev)
    {
        int          fd;
    
        if( (fd=open(i2c_dev, O_RDWR)) < 0)
        {
            printf("i2c device open failed: %s\n", strerror(errno));
            return -1;
        }
    
        /* set I2C mode and SHT2x slave address */
        ioctl(fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
        ioctl(fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40*/
    
        if( sht2x_softreset(fd) < 0 )
        {
            printf("SHT2x softreset failure\n");
            return -2;
        }
    
        return fd;
    }
    
    /* 获取sht20的温度湿度值 */
    int sht2x_get_temp_humidity(int fd, float *temp, float *rh)
    {
        uint8_t           buf[4];
    
        if( fd<0 || !temp || !rh )
        {
            printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
            return -1;
        }
    
        /* send trigger temperature measure command and read the data */
        memset(buf, 0, sizeof(buf));
        /* 发送非主机保持的温度测量命令 */
        buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
        write(fd, buf, 1);
    
        /* 等待需要的时间 数据手册可知 */
        msleep(85); /* datasheet: typ=66, max=85 */
    
        memset(buf, 0, sizeof(buf));
        /* 读取三个字节的数据,最后一个字节为crc检验值 */
        read(fd, buf, 3);
        dump_buf("Temperature sample data: ", buf, 3);
        /* 计算实际温度值*/
        *temp = 175.72 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 46.85;
    
        /* send trigger humidity measure command and read the data */
        memset(buf, 0, sizeof(buf));
        /* 发送非主机保持的湿度测量命令 */
        buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
        write(fd, buf, 1);
    
        msleep(29); /* datasheet: typ=22, max=29 */
        memset(buf, 0, sizeof(buf));
    
        read(fd, buf, 3);
        dump_buf("Relative humidity sample data: ", buf, 3);
        /* 计算实际湿度值*/
        *rh = 125 * (((((int) buf[0]) << 8) + (buf[1] & 0xfc)) / 65536.0) - 6;
    
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值