STM32F4硬件IIC读取温湿度传感器SHT31

背景:低速读取温湿度传感器SHT31。(SHT3X系列应该都兼容)

听闻STM32的硬件IIC有些问题,但又听说是在硬件IIC配合使用DMA时才会出现,解决办法是将DMA中断优先级设置为最高即可。我不打算用DMA,所以尝试使用硬件IIC读SHT31。

具体型号为STM32F429,野火的标准库。

SHT31传感器的数据手册不过多介绍,附两个链接:

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

.h文件

#ifndef __BSP_SHT3X_I2C_H
#define __BSP_SHT3X_I2C_H

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

/* STM32F429 I2C 速率最大为400k */
#define        I2C_Speed                                400000

/* STM32 自身的 I2C 地址,这个地址只要与 STM32 外挂的 I2C 器件地址不一样即可 */
#define        I2C_OWN_ADDRESS7                         0x0A

/* SHT3X的设备地址默认为0x44,根据硬件的不同可能为0x45*/
#define        SHT3X_ADDRESS                            0x44

// SHT3X命令,其他可根据手册自行添加
#define        SHT3X_PERIODIC_0_5MPS_M                  0x2024
#define        SHT3X_PERIODIC_1MPS_H                    0x2130
#define        SHT3X_FETCHDATA                          0xE000


/*I2C 接口 */
#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

/* 通讯等待超时时间 */
#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文件

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

float Temperature,Humidity;

uint32_t I2CTimeout;

static void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    
    /* 使能 I2C 引脚的 GPIO 时钟 */
    RCC_AHB1PeriphClockCmd(SHT3X_I2C_SCL_GPIO_CLK|SHT3X_I2C_SDA_GPIO_CLK,ENABLE);
    /* 连接引脚源 PXx 到 I2C_SCL*/
    GPIO_PinAFConfig(SHT3X_I2C_SCL_GPIO_PORT,SHT3X_I2C_SCL_SOURCE,SHT3X_I2C_SCL_AF);
    /* 连接引脚源 PXx 到 to I2C_SDA*/
    GPIO_PinAFConfig(SHT3X_I2C_SDA_GPIO_PORT,SHT3X_I2C_SDA_SOURCE,SHT3X_I2C_SDA_AF);
    
    /* 配置 SCL 引脚 */
    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);
    
    /* 配置 SDA 引脚 */
    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;
    
    /* 使能 I2C 外设时钟 */
    RCC_APB1PeriphClockCmd(SHT3X_I2C_CLK,ENABLE);
    
    /* I2C 模式 */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    /* 占空比 */
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    /*I2C 自身地址 */
    I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDRESS7;
    /* 使能响应 */
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    /* I2C 的寻址模式 */
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    /* 通信速率 */
    I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
    /* 写入配置 */
    I2C_Init(SHT3X_I2C,&I2C_InitStructure);
    /* 使能 I2C */
    I2C_Cmd(SHT3X_I2C,ENABLE);
    
}

void SHT3X_I2C_Init(void)
{
    I2C_GPIO_Config();
    I2C_Mode_Config();
    
    //设置SHT3X为周期数据采集模式
    SHT3X_I2C_WriteCommand(SHT3X_PERIODIC_1MPS_H);
}


/**
 * @brief I2C 等待事件超时的情况下会调用这个函数来处理
 * @param errorCode:错误代码,可以用来定位是哪个环节出错. 
 * @retval 返回 0,表示 IIC 读取失败.
 */
static uint8_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{
    /* 使用串口 printf 输出错误信息,方便调试 */
    printf("I2C Timeout!Code = %d\n",errorCode); //调试完记得把printf注释掉,或换为自己工程的宏开关
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    return errorCode;
}

/******************************** WriteCommand ************************************/
uint8_t SHT3X_I2C_WriteCommand(uint16_t command)
{
    /* 产生 I2C 起始信号 */
    I2C_GenerateSTART(SHT3X_I2C,ENABLE);
    /* 设置超时等待时间 */
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV5 事件并清除标志 */
    while(!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0)  return I2C_TIMEOUT_UserCallback(1);
    }
    /* 发送 EEPROM 设备地址 */
    I2C_Send7bitAddress(SHT3X_I2C,SHT3X_ADDRESS << 1,I2C_Direction_Transmitter);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV6 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
    }

    /* 发送要写入的 COMMAND */
    I2C_SendData(SHT3X_I2C,command >> 8);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV8 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
    }
    
    /* 发送一字节*/
    I2C_SendData(SHT3X_I2C, command & 0xff);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV8 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
    }    
    
    /* 发送停止信号 */
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    
    return 0;
}

/********************* ReadData **********************/
uint8_t SHT3X_I2C_ReadData(uint16_t* temperature, uint16_t* humidity)
{
    /* 产生 I2C 起始信号 */
    I2C_GenerateSTART(SHT3X_I2C,ENABLE);
    /* 设置超时等待时间 */
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV5 事件并清除标志 */
    while(!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_MODE_SELECT))
    {
        if((I2CTimeout--) == 0)    return I2C_TIMEOUT_UserCallback(5);
    }
    /* 发送 EEPROM 设备地址 */
    I2C_Send7bitAddress(SHT3X_I2C,SHT3X_ADDRESS << 1,I2C_Direction_Transmitter);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV6 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
    }

    /* 发送要写入的 COMMAND */
    I2C_SendData(SHT3X_I2C,SHT3X_FETCHDATA >> 8);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV8 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
    }
    
    /* 发送一字节 */
    I2C_SendData(SHT3X_I2C, SHT3X_FETCHDATA & 0xFF);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV8 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
    }    
    
    /* 产生第二次 I2C 起始信号 */ //!!!!
    I2C_GenerateSTART(SHT3X_I2C, ENABLE);
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV5 事件并清除标志 */
    while (!I2C_CheckEvent(SHT3X_I2C, I2C_EVENT_MASTER_MODE_SELECT))
    {
        if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
    }
    
    /* 发送 EEPROM 设备地址 */
    I2C_Send7bitAddress(SHT3X_I2C, (SHT3X_ADDRESS << 1) | 1, I2C_Direction_Receiver);

    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /* 检测 EV6 事件并清除标志 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    *temperature = I2C_ReceiveData(SHT3X_I2C) << 8;
    /* 使能应答,方便下一次 I2C 传输 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    *temperature |= I2C_ReceiveData(SHT3X_I2C);
    /* 使能应答,方便下一次 I2C 传输 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    I2C_ReceiveData(SHT3X_I2C);
    /* 使能应答,方便下一次 I2C 传输 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    *humidity = I2C_ReceiveData(SHT3X_I2C) << 8;
    /* 使能应答,方便下一次 I2C 传输 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    *humidity |= I2C_ReceiveData(SHT3X_I2C);
    /* 使能应答,方便下一次 I2C 传输 */
    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);
    }
    /* 通过 I2C,从设备中读取一个字节的数据 */
    I2C_ReceiveData(SHT3X_I2C);

    /* 发送非应答信号 */
    I2C_AcknowledgeConfig(SHT3X_I2C, DISABLE);
    /* 发送停止信号 */
    I2C_GenerateSTOP(SHT3X_I2C, ENABLE);
    
    return 0;
}


/*******************************数据读取接口*********************************/
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\n", Temperature,Humidity);
    }
    else
    {
//       printf("ERROR!");
    }
}

SHT3X_I2C_WriteCommand函数和SHT3X_I2C_ReadData函数是根据芯片手册提供的配置流程添加。

写命令:

读数据:

main函数:

int main(void)
{
    SysTick_Init();
    /* 初始化 USART 配置模式为 115200 8-N-1 */
    USARTx_Config();
    SHT3X_I2C_Init();
    
    while(1)
    {
        Delay_ms(2000);
        SHT3X_Get_T_H(); //延时读取温湿度即可
    }
}

目前发现一个问题:这个方法读温湿度传感器会成功一次,失败一次,非常稳定。每次都是I2C_TIMEOUT_UserCallback(12)这里超时退出。

原因未知,但我只需要慢速读,不影响我的使用。如有大佬知道原因,希望可以分享。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值