软件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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值