STM32F4 hardware IIC reads temperature and humidity sensor SHT31

Background: read temperature and humidity sensor SHT31 at low speed. (SHT3X series should be compatible)

I heard that there are some problems with the hardware IIC of STM32, but I also heard that it will only appear when the hardware IIC is used in conjunction with DMA. The solution is to set the DMA interrupt priority to the highest. I don’t plan to use DMA, so try to use hardware IIC to read SHT31.

The specific model is STM32F429, the standard library of Wildfire.

The data sheet of the SHT31 sensor is not too much to introduce, and two links are attached:

https://blog.csdn.net/qq_43957122/article/details/98713072

https://blog.csdn.net/Lushengshi/article/details/129254086

.h file

#ifndef __BSP_SHT3X_I2C_H
#define __BSP_SHT3X_I2C_H

#include "stm32f4xx_gpio.h"
#include "stm32f4xx_i2c.h"

/* STM32F429 I2C rate up to 400k */
#define I2C_Speed 400000

/* STM32's own I2C address, as long as this address is different from the address of the STM32 external I2C device */
#define I2C_OWN_ADDRESS7 0x0A

/* The device address of SHT3X is 0x44 by default, it may be 0x45 depending on the hardware*/
#define SHT3X_ADDRESS 0x44

// SHT3X command, others can be added according to the manual
#define SHT3X_PERIODIC_0_5MPS_M 0x2024
#define SHT3X_PERIODIC_1MPS_H 0x2130
#define SHT3X_FETCHDATA 0xE000


/*I2C interface */
#define SHT3X_I2C I2C1
#define SHT3X_I2C_CLK RCC_APB1Periph_I2C1

#define SHT3X_I2C_SCL_PIN GPIO_Pin_6
#define SHT3X_I2C_SCL_GPIO_PORT GPIOB
#define SHT3X_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB
#define SHT3X_I2C_SCL_SOURCE GPIO_PinSource6
#define SHT3X_I2C_SCL_AF GPIO_AF_I2C1

#define SHT3X_I2C_SDA_PIN GPIO_Pin_7
#define SHT3X_I2C_SDA_GPIO_PORT GPIOB
#define SHT3X_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB
#define SHT3X_I2C_SDA_SOURCE GPIO_PinSource7
#define SHT3X_I2C_SDA_AF GPIO_AF_I2C1

/* Communication waiting timeout */
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))
 
void SHT3X_I2C_Init(void);
uint8_t SHT3X_I2C_WriteCommand(uint16_t command);
uint8_t SHT3X_I2C_ReadData(uint16_t* temperature, uint16_t* humidity);
void SHT3X_Get_T_H(void);
#endif /* __BSP_SHT3X_I2C_H */

.c file

#include <stdio.h>
#include "bsp_i2c_sht3x.h"

float Temperature, Humidity;

uint32_t I2CTimeout;

static void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* Enable GPIO clock for I2C pins */
    RCC_AHB1PeriphClockCmd(SHT3X_I2C_SCL_GPIO_CLK|SHT3X_I2C_SDA_GPIO_CLK,ENABLE);
    /* Connect pin source PXx to I2C_SCL*/
    GPIO_PinAFConfig(SHT3X_I2C_SCL_GPIO_PORT,SHT3X_I2C_SCL_SOURCE,SHT3X_I2C_SCL_AF);
    /* connect pin source PXx to I2C_SDA*/
    GPIO_PinAFConfig(SHT3X_I2C_SDA_GPIO_PORT,SHT3X_I2C_SDA_SOURCE,SHT3X_I2C_SDA_AF);
    
    /* Configure the SCL pin */
    GPIO_InitStructure.GPIO_Pin = SHT3X_I2C_SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(SHT3X_I2C_SCL_GPIO_PORT, & GPIO_InitStructure);
    
    /* configure SDA pin */
    GPIO_InitStructure.GPIO_Pin = SHT3X_I2C_SDA_PIN;
    GPIO_Init(SHT3X_I2C_SDA_GPIO_PORT, & GPIO_InitStructure);
}

static void I2C_Mode_Config(void)
{
    I2C_InitTypeDef I2C_InitStructure;
    
    /* Enable I2C peripheral clock */
    RCC_APB1PeriphClockCmd(SHT3X_I2C_CLK,ENABLE);
    
    /* I2C mode */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    /* duty cycle */
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    /*I2C own address */
    I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDRESS7;
    /* enable response */
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    /* I2C addressing mode */
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    /* communication rate */
    I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
    /* Write configuration */
    I2C_Init(SHT3X_I2C, &I2C_InitStructure);
    /* enable I2C */
    I2C_Cmd(SHT3X_I2C,ENABLE);
    
}

void SHT3X_I2C_Init(void)
{
    I2C_GPIO_Config();
    I2C_Mode_Config();
    
    //Set SHT3X to periodic data acquisition mode
    SHT3X_I2C_WriteCommand(SHT3X_PERIODIC_1MPS_H);
}


/**
 * @brief I2C will call this function when waiting for event timeout
 * @param errorCode: Error code, which can be used to locate which link is wrong.
 * @retval returns 0, indicating that the IIC read failed.
 */
static uint8_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
    /* Use the serial port printf to output error information, which is convenient for debugging */
    printf("I2C Timeout!Code = %d\
",errorCode); //Remember to comment out printf after debugging, or replace it with the macro switch of your own project
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    return errorCode;
}

/******************************** WriteCommand ***************** *********************/
uint8_t SHT3X_I2C_WriteCommand(uint16_t command)
{
    /* Generate I2C start signal */
    I2C_GenerateSTART(SHT3X_I2C,ENABLE);
    /* Set the timeout waiting time */
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV5 event and clear flag */
    while(!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);
    }
    /* Send EEPROM device address */
    I2C_Send7bitAddress(SHT3X_I2C,SHT3X_ADDRESS << 1,I2C_Direction_Transmitter);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV6 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
    }

    /* Send COMMAND to write */
    I2C_SendData(SHT3X_I2C, command >> 8);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV8 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
    }
    
    /* send a byte */
    I2C_SendData(SHT3X_I2C, command & 0xff);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV8 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
    }
    
    /* Send stop signal */
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    
    return 0;
}

/******************** ReadData ***********************/
uint8_t SHT3X_I2C_ReadData(uint16_t* temperature, uint16_t* humidity)
{
    /* Generate I2C start signal */
    I2C_GenerateSTART(SHT3X_I2C,ENABLE);
    /* Set the timeout waiting time */
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV5 event and clear flag */
    while(!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
    }
    /* Send EEPROM device address */
    I2C_Send7bitAddress(SHT3X_I2C,SHT3X_ADDRESS << 1,I2C_Direction_Transmitter);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV6 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
    }

    /* Send COMMAND to write */
    I2C_SendData(SHT3X_I2C,SHT3X_FETCHDATA >> 8);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV8 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
    }
    
    /* Send a byte */
    I2C_SendData(SHT3X_I2C, SHT3X_FETCHDATA & 0xFF);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV8 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
    }
    
    /* Generate the second I2C start signal */ //!!!!
    I2C_GenerateSTART(SHT3X_I2C, ENABLE);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV5 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
    }
    
    /* Send EEPROM device address */
    I2C_Send7bitAddress(SHT3X_I2C, (SHT3X_ADDRESS << 1) | 1, I2C_Direction_Receiver);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* Detect EV6 event and clear flag */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
    }
    
    /**** Read First Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
    }
    /* Read a byte of data from the device via I2C */
    *temperature = I2C_ReceiveData(SHT3X_I2C) << 8;
    /* Enable acknowledgment for next I2C transmission */
    I2C_AcknowledgeConfig(SHT3X_I2C, ENABLE);
    
    /**** Read Second Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
    }
    /* Read a byte of data from the device via I2C */
    *temperature |= I2C_ReceiveData(SHT3X_I2C);
    /* Enable acknowledgment for next I2C transmission */
    I2C_AcknowledgeConfig(SHT3X_I2C, ENABLE);
    
    /**** Read Third Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
    }
    /* Read a byte of data from the device via I2C */
    I2C_ReceiveData(SHT3X_I2C);
    /* Enable acknowledgment for next I2C transmission */
    I2C_AcknowledgeConfig(SHT3X_I2C, ENABLE);
    
    /**** Read Fourth Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
    }
    /* Read a byte of data from the device via I2C */
    *humidity = I2C_ReceiveData(SHT3X_I2C) << 8;
    /* Enable acknowledgment for next I2C transmission */
    I2C_AcknowledgeConfig(SHT3X_I2C, ENABLE);
    
    /**** Read Fifth Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(15);
    }
    /* Read a byte of data from the device via I2C */
    *humidity |= I2C_ReceiveData(SHT3X_I2C);
    /* Enable acknowledgment for next I2C transmission */
    I2C_AcknowledgeConfig(SHT3X_I2C, ENABLE);
    
    /**** Read Sixth Byte ****/
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(16);
    }
    /* Read a byte of data from the device via I2C */
    I2C_ReceiveData(SHT3X_I2C);

    /* Send a non-acknowledgment signal */
    I2C_AcknowledgeConfig(SHT3X_I2C, DISABLE);
    /* Send stop signal */
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    
    return 0;
}


/********************************Data Read Interface************** *******************/
void SHT3X_Get_T_H(void)
{
    uint16_t tem = 0, hum = 0;
    if(!SHT3X_I2C_ReadData( &tem, &hum))
    {
        Temperature = 175.0f * (float)tem / 65535.0f - 45.0f;
        Humidity = 100.0f * (float)hum / 65535.0f;
        printf("tem: %f, hum = %f\
", Temperature, Humidity);
    }
    else
    {
// printf("ERROR!");
    }
}

The SHT3X_I2C_WriteCommand function and SHT3X_I2C_ReadData function are added according to the configuration process provided by the chip manual.

write command:

Read data:

main function:

int main(void)
{
    SysTick_Init();
    /* Initialize USART configuration mode to 115200 8-N-1 */
    USARTx_Config();
    SHT3X_I2C_Init();
    
    while(1)
    {
        Delay_ms(2000);
        SHT3X_Get_T_H(); // Delayed reading of temperature and humidity
    }
}

At present, a problem has been found: this method will succeed once in reading the temperature and humidity sensor, and fail once, which is very stable. Every time it is I2C_TIMEOUT_UserCallback (12) timeout exit here.

The reason is unknown, but I just need to read slowly and it doesn’t affect my usage. If anyone knows the reason, please share.