软件I2C sht3x温湿度传感器 学习过程记录

软件I2C sht3x温湿度传感器


  • 硬件方式实现传送门
  • Datasheet SHT3x-DIS 官网数据手册的阅读
  • embedded-sht 例程

硬件I2C方式实现传送门

硬件I2C sht3x温湿度传感器 学习过程记录


Datasheet SHT3x-DIS

datasheet

这里没什么好说的,电压,协议,精度,封装之类的。
直接跳过,到影响整体方案的地方。。。

这里写图片描述

时间上的问题,高精度的情况下典型值要12.5ms,其实在官方给的例程中则选用的是最大值15ms,相应精度的发送命令不同。(软件I2C利用的GPIO以及延时,即阻塞处理,如果说对系统实时性较高的,不用往下看了。)

这里写图片描述

这里写图片描述

SCL和SDA都是开漏输出,外部上拉电阻,PP推挽输出也是可以的。注意是否单片机内部已经打开上拉电阻了(这句话不鸟他)

这里写图片描述

意思就是你传输过程中,不要乱动他的地址,传完了再动。

这里写图片描述

地址有两个,引脚拉低即0x44,引脚拉高即0x45

这里写图片描述

复位的引脚,不用就建议浮空,或者>=2KΩ上拉到VDD

这里写图片描述

支持I2C的fast mode(200khz),实际上它可以1000khz,即0.1us/bit

这里写图片描述

命令的间隙是1ms,也就是你发一次命令要等一会才能发。

这里写图片描述

7bits的地址 + 1bit的0(write)

支持两种查询模式:单次查询、周期查询
两种查询模式的命令不同,查手册吧,不贴了。

这里写图片描述

embedded-sht 例程

github地址例程地址

定义软件I2C位操作(GPIO方式)

sensirion_sw_i2c_implementation.c

#include <stm32f4xx.h>
#include "sensirion_sw_i2c_gpio.h"
#include "sensirion_arch_config.h"
#include "Timer.h"
void sensirion_init_pins() {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
    sensirion_SDA_in();
    sensirion_SCL_in();
}

void sensirion_SDA_in() {
    GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOG, &GPIO_InitStruct);
}

void sensirion_SDA_out() {
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOG, &GPIO_InitStruct);
    GPIO_ResetBits(GPIOG, GPIO_Pin_3);
}

u8 sensirion_SDA_read() {
    return (u8)GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_3) == 1;
}

void sensirion_SCL_in() {
    GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOG, &GPIO_InitStruct);
}

void sensirion_SCL_out() {
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOG, &GPIO_InitStruct);
    GPIO_ResetBits(GPIOG, GPIO_Pin_2);
}

u8 sensirion_SCL_read() {
    return (u8)GPIO_ReadInputDataBit(GPIOG, GPIO_Pin_2) == 1;
}

void sensirion_sleep_usec(u32 useconds) {
    BaseTimer::Instance()->delay_ms(useconds);
}
  • SDA、SCL引脚根据自己的实际情况配置
  • sensirion_sleep_usec()函数需要自己实现(微秒级延时

sensirion_sw_i2c.c

#include "sensirion_arch_config.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sensirion_sw_i2c_gpio.h"

#define DELAY_USEC (SENSIRION_I2C_CLOCK_PERIOD_USEC / 50)

static u8 sensirion_wait_while_clock_stretching(void)
{
    u8 timeout = 2;

    while (--timeout) {
        if (sensirion_SCL_read())
            return STATUS_OK;
        sensirion_sleep_usec(DELAY_USEC);
    }

    return STATUS_FAIL;
}

static s8 sensirion_i2c_write_byte(u8 data)
{
    s8 nack, i;
    for (i = 7; i >= 0; i--) {
        sensirion_SCL_out();
        if ((data >> i) & 0x01)
            sensirion_SDA_in();
        else
            sensirion_SDA_out();
        sensirion_sleep_usec(DELAY_USEC);
        sensirion_SCL_in();
        sensirion_sleep_usec(DELAY_USEC);
        if (sensirion_wait_while_clock_stretching())
            return STATUS_FAIL;
    }
    sensirion_SCL_out();
    sensirion_SDA_in();
    sensirion_sleep_usec(DELAY_USEC);
    sensirion_SCL_in();
    if (sensirion_wait_while_clock_stretching())
        return STATUS_FAIL;
    nack = (sensirion_SDA_read() != 0);
    sensirion_SCL_out();

    return nack;
}

static u8 sensirion_i2c_read_byte(u8 ack)
{
    s8 i;
    u8 data = 0;
    sensirion_SDA_in();
    for (i = 7; i >= 0; i--) {
        sensirion_sleep_usec(DELAY_USEC);
        sensirion_SCL_in();
        if (sensirion_wait_while_clock_stretching())
            return STATUS_FAIL;
        data |= (sensirion_SDA_read() != 0) << i;
        sensirion_SCL_out();
    }
    if (ack)
        sensirion_SDA_out();
    else
        sensirion_SDA_in();
    sensirion_sleep_usec(DELAY_USEC);
    sensirion_SCL_in();
    sensirion_sleep_usec(DELAY_USEC);
    if (sensirion_wait_while_clock_stretching())
        return STATUS_FAIL;
    sensirion_SCL_out();
    sensirion_SDA_in();

    return data;
}

static u8 sensirion_i2c_start(void)
{
    sensirion_SCL_in();
    if (sensirion_wait_while_clock_stretching())
        return STATUS_FAIL;

    sensirion_SDA_out();
    sensirion_sleep_usec(DELAY_USEC);
    sensirion_SCL_out();
    sensirion_sleep_usec(DELAY_USEC);
    return STATUS_OK;
}

static void sensirion_i2c_stop(void)
{
    sensirion_SDA_out();
    sensirion_sleep_usec(DELAY_USEC);
    sensirion_SCL_in();
    sensirion_sleep_usec(DELAY_USEC);
    sensirion_SDA_in();
    sensirion_sleep_usec(DELAY_USEC);
}

s8 sensirion_i2c_write(u8 address, const u8* data, u16 count)
{
    s8 ret;
    u16 i;

    ret = sensirion_i2c_start();
    if (ret != STATUS_OK)
        return ret;

    ret = sensirion_i2c_write_byte(address << 1);
    if (ret != STATUS_OK) {
        sensirion_i2c_stop();
        return ret;
    }
    for (i = 0; i < count; i++) {
        ret = sensirion_i2c_write_byte(data[i]);
        if (ret != STATUS_OK) {
            sensirion_i2c_stop();
            break;
        }
    }
    sensirion_i2c_stop();
    return ret;
}

s8 sensirion_i2c_read(u8 address, u8* data, u16 count)
{
    s8 ret;
    u8 send_ack;
    u16 i;

    ret = sensirion_i2c_start();
    if (ret != STATUS_OK)
        return ret;

    ret = sensirion_i2c_write_byte((address << 1) | 1);
    if (ret != STATUS_OK) {
        sensirion_i2c_stop();
        return ret;
    }
    for (i = 0; i < count; i++) {
        send_ack = i < (count - 1); 
        data[i] = sensirion_i2c_read_byte(send_ack);
    }
    sensirion_i2c_stop();
    return STATUS_OK;
}

void sensirion_i2c_init()
{
    sensirion_init_pins();
    sensirion_SCL_in();
    sensirion_SDA_in();
}
  • DELAY_USEC 这个是根据手册1000khz推算的,也就是bit时间
  • sensirion_i2c_write()函数根据协议通过GPIO及延时模拟通信过程中的报文

sht3x.c

#include "sensirion_arch_config.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sht.h"
#include "sht_common.h"
#include "Timer.h"
#include "Console.h"
/* all measurement commands return T (CRC) RH (CRC) */
#if USE_SENSIRION_CLOCK_STRETCHING
static const u8 CMD_MEASURE_HPM[]     = { 0x2C, 0x06 };
static const u8 CMD_MEASURE_LPM[]     = { 0x2C, 0x10 };
#else
static const u8 CMD_MEASURE_HPM[]     = { 0x24, 0x00 };
static const u8 CMD_MEASURE_LPM[]     = { 0x24, 0x16 };
#endif /* USE_SENSIRION_CLOCK_STRETCHING */
static const u8 CMD_READ_STATUS_REG[] = { 0xF3, 0x2D };
static const u8 COMMAND_SIZE = sizeof(CMD_MEASURE_HPM);
#ifdef SHT_ADDRESS
static const u8 SHT3X_ADDRESS = SHT_ADDRESS;
#else
static const u8 SHT3X_ADDRESS = 0x44;
#endif

static const u16 MEASUREMENT_DURATION_USEC = 15000;

static const u8 *cmd_measure = CMD_MEASURE_HPM;

s8 sht_measure_blocking_read(s32 *temperature, s32 *humidity)
{
	#if DEBUGTRANSFER
		int32_t start_time;
		int32_t end_time;
		int32_t period_time;
		start_time = BaseTimer::Instance()->getTime();	
		Console::Instance()->printf("transfer time is %d \n",start_time);
		s8 ret = sht_measure();
		end_time = BaseTimer::Instance()->getTime();
		Console::Instance()->printf("transfer_end time is %d \n",end_time);
		period_time = end_time - start_time;
		Console::Instance()->printf("transfer_period time is %d \n",period_time);
	#else
		s8 ret = sht_measure();
	#endif
    if (ret == STATUS_OK) {
        sensirion_sleep_usec(MEASUREMENT_DURATION_USEC);
	#if DEBUGREAD
		int32_t start_time;
		int32_t end_time;
		int32_t period_time;
		start_time = BaseTimer::Instance()->getTime();	
		Console::Instance()->printf("read_time is %d \n",start_time);
		s8 ret = sht_measure();
		end_time = BaseTimer::Instance()->getTime();
		Console::Instance()->printf("read_end time is %d \n",end_time);
		period_time = end_time - start_time;
		Console::Instance()->printf("read_period time is %d \n",period_time);
	#else
		ret = sht_read(temperature, humidity);
	#endif   
    }
    return ret;
}

s8 sht_measure()
{
    return sensirion_i2c_write(SHT3X_ADDRESS, CMD_MEASURE_HPM, COMMAND_SIZE);
}

s8 sht_read(s32 *temperature, s32 *humidity)
{
    return sht_common_read_measurement(SHT3X_ADDRESS, temperature, humidity);
}

s8 sht_probe()
{
    u8 data[3];
    sensirion_i2c_init();
    s8 ret = sensirion_i2c_write(SHT3X_ADDRESS, CMD_READ_STATUS_REG, COMMAND_SIZE);
    if (ret)
        return ret;

    ret = sensirion_i2c_read(SHT3X_ADDRESS, data, sizeof(data));
    if (ret)
        return ret;

    ret = sensirion_common_check_crc(data, 2, data[2]);
    if (ret)
        return ret;
    return STATUS_OK;
}

s8 sht_disable_sleep(u8 disable_sleep)
{
    return STATUS_FAIL; /* sleep mode not supported */
}

void sht_enable_low_power_mode(u8 enable_low_power_mode)
{
    cmd_measure = enable_low_power_mode ? CMD_MEASURE_LPM : CMD_MEASURE_HPM;
}

//const char *sht_get_driver_version()
//{
//    return 11;
//}

u8 sht_get_configured_sht_address()
{
    return 11;
}

  • const char *sht_get_driver_version()需要注掉,并没有定义驱动版本,你也可以自己定义,否则会报错。
  • 有很多封装好的函数可以调用,睡眠,低功耗模式等等(其实鸟用也没用,反正我没用)
  • sht_probe()探针,用来检测是不是正常的,主程序中会用到。
  • 还有一些开头定义的命令数组及地址
  • 忽略那些宏定义debug下的console,我用来算时间用的。(以前大佬写的console就是好用,hahahah)

mian.c

while (sht_probe() != STATUS_OK) {
         Console::Instance()->printf("SHT sensor probing failed\n"); 
    }
		Console::Instance()->printf("SHT sensor probing successful\n"); 

    while (1) {
        s32 temperature, humidity;
        /* Measure temperature and relative humidity and store into variables
         * temperature, humidity (each output multiplied by 1000).
         */
		#if DEBUGMAIN
		int32_t start_time;
		int32_t end_time;
		int32_t period_time;
		start_time = BaseTimer::Instance()->getTime();	
		Console::Instance()->printf("start time is %d \n",start_time);
		s8 ret = sht_measure_blocking_read(&temperature, &humidity);
		end_time = BaseTimer::Instance()->getTime();
		Console::Instance()->printf("end time is %d \n",end_time);
		period_time = end_time - start_time;
		Console::Instance()->printf("period time is %d \n",period_time);
		#else
		s8 ret = sht_measure_blocking_read(&temperature, &humidity);
		#endif
        if (ret == STATUS_OK) {
            Console::Instance()->printf("measured temperature: %0.2f degreeCelsius, "
                      "measured humidity: %0.2f percentRH\n",
                      temperature / 1000.0f,
                      humidity / 1000.0f); 
        } else {
            Console::Instance()->printf("error reading measurement\n");
        }

       BaseTimer::Instance()->delay_ms(280); 
    }
	return 0;

最后一行的延时不用管它了,delay_ms()内部已经被我改成us级的了。(功能测试,各位大佬不要抓这个)。至于等待多少也没有意义了,手册中是1ms的间隔才能发送命令进行查询,实际上这其中的过程早就超过1ms了。

程序跑起来的输出:
这里写图片描述
发送所需的时间:0.6ms
这里写图片描述
读取所需的时间:0.5ms
这里写图片描述
完整读取的时间:16ms(传感器自身测量占了15ms见手册)

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: sht3x温湿度传感器是一种高精度和可靠性的数字输出传感器,用于测量环境中的温度和湿度。 sht3x传感器具有多种优点,包括快速响应时间、广泛的测量范围、低功耗和精确的测量精度。它可以在各种应用领域中使用,如气象观测、工业自动化、温室栽培和医疗设备。 sht3x温湿度传感器的数据手册提供了关于传感器的详细信息。手册包括传感器的规格参数、电气特性、通信接口和阻抗匹配要求。这些信息对于设计和集成传感器到系统中非常重要。 手册中还提供了关于传感器的使用方法和技术指导,包括传感器的初始化、测量和校准。此外,手册还解释了如何解读传感器的输出数据,以及如何计算和转换温度和湿度值。 sht3x传感器的数据手册还包括了传感器的供电电压和功耗要求,以及传感器的工作温度范围和储存条件。这些信息对于使用者来说非常重要,可以确保传感器在正确的环境下工作并正常运行。 总之,sht3x温湿度传感器的数据手册提供了设计者和使用者所需的详尽信息。用户可以根据手册中的指南和建议,将传感器集成到他们的应用中,并准确地测量温度和湿度。这样可以提高系统的性能和稳定性,满足用户的需求。 ### 回答2: 温湿度传感器SHT3x是一款集成的数字温湿度传感器,具有高精度和可靠性。SHT3x采用Sensirion公司独家的传感器技术,能够提供准确的温度和湿度测量结果。 SHT3x的数据手册(中文版)提供了关于传感器的详细信息和使用说明。手册中包含了传感器的物理特性、电气规格和通信协议等重要参数。用户可以根据手册中提供的连接图和引脚定义来正确连接传感器。手册还详细介绍了传感器的工作原理和测量原理,以帮助用户更好地理解传感器的工作方式。 手册提供了传感器的使用和配置指南,包括传感器的初始化、测量模式的选择以及测量周期的设置等。此外,手册还介绍了传感器的输出数据格式和校准方法。用户可以根据手册中提供的示例代码和指令来正确读取和解析传感器的数据。 对于用户的常见问题,手册提供了详细的常见问题解答和故障排除指南。用户可以根据手册中提供的指南来解决传感器使用中遇到的问题。 总体而言,温湿度传感器SHT3x数据手册(中文版)是一份非常有价值的文档,能够帮助用户正确、高效地使用SHT3x传感器。用户可以依靠手册中提供的信息和指南,轻松地集成传感器到自己的应用中,并获得准确可靠的温湿度测量结果。 ### 回答3: 温湿度传感器SHT3x是一款高精度的数字式温湿度传感器,由瑞士公司Sensirion开发和生产。该传感器采用最新的湿度和温度传感器技术,具有优秀的性能和稳定性。 SHT3x温湿度传感器的数据手册提供了详细的产品规格和使用说明。首先,手册介绍了传感器的技术参数,包括测量范围、精度、分辨率、响应时间等。例如,SHT3x温度测量范围为-40°C至125°C,湿度测量范围为0% RH至100% RH。温度测量精度为±0.3°C,湿度测量精度为±2% RH。 手册还描述了传感器的封装和接口特性。SHT3x采用了小型的DIP封装,便于集成和安装。传感器通过I2C接口与主控设备进行通信,通信速率高达1 MHz。此外,手册还提供了传感器的引脚定义和电气特性。 手册的使用说明部分详细介绍了如何正确配置和使用SHT3x传感器。用户可以根据实际需求设置不同的测量模式和频率。传感器支持单次测量模式和周期性测量模式,用户可以根据需求选择合适的模式。手册还提供了使用传感器的示例代码和命令集,方便用户进行开发和集成。 此外,手册还包含了传感器的典型应用示例和性能测试结果。用户可以了解到传感器在不同应用场景下的表现和可靠性。 总之,SHT3x温湿度传感器的数据手册提供了全面而详细的产品信息和使用指南,对于开发和应用人员来说是非常有价值的参考资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值