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

版权声明:darboy https://blog.csdn.net/weixin_38428827/article/details/82690881

硬件I2C sht3x温湿度传感器


  • stm32的硬件I2C,非中断方式
  • 代码及stm32工程分享
  • 软件方式实现传送门
  • sht3x温湿度传感器的资料解析

背景

  • 在此之前,对sht3x这款传感器的开发也是用的软件I2C模拟验证的,但项目中要求不能有阻塞行为,故软件I2C中的delay等操作就不能用了,那么stm32有硬件I2C,为什么不能用呢。(虽然网上大量搜索表明stm32的I2C的坑很深。)

代码及stm32工程分享


软件I2C方式实现传送门

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


sht3x温湿度传感器的资料解析


定义硬件I2C底层实现

sensirion_sw_i2c_implementation.c

#include <stm32f4xx.h>
#include <stdint.h>
#include "sensirion_arch_config.h"
#include "sensirion_i2c.h"

static I2C_InitTypeDef hi2c1;
static GPIO_InitTypeDef gpioinit;
static uint32_t I2Cx_TIMEOUT_UserCallback(char value);

#define I2Cx_FLAG_TIMEOUT			((uint32_t) 900)
#define I2Cx_LONG_TIMEOUT 			((uint32_t)(300 * I2Cx_FLAG_TIMEOUT))
#define WAIT_FOR_FLAG(flag, value, timeout, errorcode)	I2CTimeout = timeout;\
														while(I2C_GetFlagStatus(I2C1, flag) != value){\
														if((I2CTimeout--) == 0) return I2Cx_TIMEOUT_UserCallback(errorcode);\
														}\

#define CLEAR_ADDR_BIT			I2C_ReadRegister(I2C1, I2C_Register_SR1);\
								I2C_ReadRegister(I2C1, I2C_Register_SR2);\

static uint32_t I2Cx_TIMEOUT_UserCallback(char value)
{
	I2C_InitTypeDef hi2c1;
	I2C_GenerateSTOP(I2C1, ENABLE);
	I2C_SoftwareResetCmd(I2C1, ENABLE);
	I2C_SoftwareResetCmd(I2C1, DISABLE);
	I2C_DeInit(I2C1);
	hi2c1.I2C_ClockSpeed = 100000;
    hi2c1.I2C_Mode = I2C_Mode_I2C;
    hi2c1.I2C_DutyCycle = I2C_DutyCycle_2;
    hi2c1.I2C_OwnAddress1 = 0;
    hi2c1.I2C_Ack = I2C_Ack_Enable;
    hi2c1.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_Cmd(I2C1, ENABLE);
	I2C_Init(I2C1, &hi2c1);
	//Console::Instance()->printf("I2C1 Restarted. \n");
	return 1;
}
 
 s8 I2C_WriteBlock(uint8_t w_addr, const uint8_t* p_data, uint16_t len)
{
	uint16_t i = 0;
	__IO uint32_t I2CTimeout = I2Cx_LONG_TIMEOUT;
	WAIT_FOR_FLAG(I2C_FLAG_BUSY,RESET,I2Cx_LONG_TIMEOUT,1);	
	I2C_GenerateSTART(I2C1, ENABLE);
	WAIT_FOR_FLAG(I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 2);
	I2C_Send7bitAddress(I2C1,(w_addr << 1), I2C_Direction_Transmitter);
	WAIT_FOR_FLAG(I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 3);
	CLEAR_ADDR_BIT;
	while (i < len) 
	{
		I2C_SendData(I2C1, *p_data);
		WAIT_FOR_FLAG(I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 4);
		i++; p_data++;
	}
	WAIT_FOR_FLAG(I2C_FLAG_BTF, SET, I2Cx_FLAG_TIMEOUT, 5);
	I2C_GenerateSTOP(I2C1, ENABLE);
	return 0;
}

s8 I2C_ReadBlock(uint8_t r_addr, uint8_t* p_data, uint16_t len)
{
	__IO uint32_t I2CTimeout = I2Cx_LONG_TIMEOUT;
	WAIT_FOR_FLAG(I2C_FLAG_BUSY,RESET,I2Cx_LONG_TIMEOUT,6);
	I2C_GenerateSTART(I2C1, ENABLE);
	WAIT_FOR_FLAG(I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 7);
	I2C_Send7bitAddress(I2C1, (r_addr << 1), I2C_Direction_Transmitter);
	WAIT_FOR_FLAG(I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 8);
	CLEAR_ADDR_BIT;
	WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 9);
	I2C_SendData(I2C1,r_addr);
	WAIT_FOR_FLAG (I2C_FLAG_TXE, SET, I2Cx_FLAG_TIMEOUT, 10); 
	I2C_GenerateSTART(I2C1, ENABLE);
	WAIT_FOR_FLAG (I2C_FLAG_SB, SET, I2Cx_FLAG_TIMEOUT, 11);
	I2C_Send7bitAddress(I2C1,(r_addr << 1), I2C_Direction_Receiver);
	WAIT_FOR_FLAG (I2C_FLAG_ADDR, SET, I2Cx_FLAG_TIMEOUT, 12);
	/*规定死就2个长度*/
	while(len) 
	{
		if (len == 1)
		{
			I2C_AcknowledgeConfig(I2C1, DISABLE);
			I2C_GenerateSTOP(I2C1, ENABLE);
		}
		if (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
		{
			*p_data = I2C_ReceiveData(I2C1);
			p_data++;
			len--;
		}
	}
	I2C_AcknowledgeConfig(I2C1, ENABLE);
	return 0;
} 

void sensirion_i2c_init()
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	gpioinit.GPIO_Mode = GPIO_Mode_OUT;
	gpioinit.GPIO_OType = GPIO_OType_PP;
	gpioinit.GPIO_PuPd = GPIO_PuPd_UP;
	gpioinit.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_7;
	gpioinit.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &gpioinit);
	GPIO_SetBits(GPIOB, GPIO_Pin_7);
	GPIO_SetBits(GPIOB, GPIO_Pin_8);
	I2C_SoftwareResetCmd(I2C1, ENABLE);

	gpioinit.GPIO_Mode = GPIO_Mode_AF;
	gpioinit.GPIO_OType = GPIO_OType_OD;
	gpioinit.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_7;
	gpioinit.GPIO_PuPd = GPIO_PuPd_NOPULL;
	gpioinit.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource8,GPIO_AF_I2C1);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);
	GPIO_Init(GPIOB, &gpioinit);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
    hi2c1.I2C_ClockSpeed = 100000;
    hi2c1.I2C_Mode = I2C_Mode_I2C;
    hi2c1.I2C_DutyCycle = I2C_DutyCycle_16_9;
    hi2c1.I2C_OwnAddress1 = 0;
    hi2c1.I2C_Ack = I2C_Ack_Enable;
    hi2c1.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_Cmd(I2C1, ENABLE);
	I2C_Init(I2C1, &hi2c1);
    /* Enable the remapping of Pins 6/7 to 8/9 and the I2C clock before the initialization of the GPIO Pins*/
}

s8 sensirion_i2c_read(u8 address, u8 *data, u16 count)
{
    return (s8) I2C_ReadBlock(address, data, count);
}

s8 sensirion_i2c_write(u8 address, const u8 *data, u16 count)
{
   	return (s8) I2C_WriteBlock(address,data, count);
}

/**
 * Sleep for a given number of microseconds. The function should delay the
 * execution for at least the given time, but may also sleep longer.
 *
 * @param useconds the sleep time in microseconds
 */
void sensirion_sleep_usec(u32 useconds) {
// 没用到
}

  • SDA、SCL引脚根据自己的实际情况配置,这里复用了一个管脚,硬件设计上只剩下这种管脚了。
  • sensirion_sleep_usec()函数不需要用。
  • I2Cx_TIMEOUT_UserCallback()用来应对超时处理的,防止因为I2C总线出现的各种问题卡死在while中,一出问题就重新初始化I2C进行恢复。
  • WAIT_FOR_FLAG()用宏定义封装了超时处理及判断I2C中的EV等事件的发生。见stm32官方手册。
  • I2C_ReadBlock()读函数,规定死了2的bytes的长度,有想用的要自己改下。
  • I2C_WriteBlock()写函数,这个没有规定长度,可以任意发送,w_addr是写入设备的地址。

sht3x.c

#include "sensirion_arch_config.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sht.h"
#include "sht_common.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)
{
    s8 ret = sht_measure();
    if (ret == STATUS_OK) {
        sensirion_sleep_usec(MEASUREMENT_DURATION_USEC);
        ret = sht_read(temperature, humidity);
    }
    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 SHT_DRV_VERSION_STR;
//}

u8 sht_get_configured_sht_address()
{
    return SHT3X_ADDRESS;
}

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

mian.c

while (sht_probe() != STATUS_OK) {
	  LED_D2_D3(GPIO_Pin_6);
        /* printf("SHT sensor probing failed\n"); */
    }
	GPIO_SetBits(GPIOA, GPIO_Pin_6);
	LED_D2_D3(GPIO_Pin_7);
    /*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).
         */
        s8 ret = sht_measure_blocking_read(&temperature, &humidity);
        if (ret == STATUS_OK) {
            /* printf("measured temperature: %0.2f degreeCelsius, "
                      "measured humidity: %0.2f percentRH\n",
                      temperature / 1000.0f,
                      humidity / 1000.0f); */
			LED_D2_D3(GPIO_Pin_6);
        } else {
            //printf("error reading measurement\n");
			LED_D2_D3(GPIO_Pin_7);
        }

        delay_ms(1000);
    }
}

void LED_D2_D3(uint32_t GPIO_Pin_x)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIOInit;
	GPIOInit.GPIO_Mode = GPIO_Mode_OUT;
	GPIOInit.GPIO_OType = GPIO_OType_PP;
	GPIOInit.GPIO_Pin = GPIO_Pin_x;
	GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIOInit);
}

完整读取的时间:16ms(传感器自身测量占了15ms见手册)

猜你喜欢

转载自blog.csdn.net/weixin_38428827/article/details/82690881