【蓝桥杯——物联网设计与开发】拓展模块3 - 温度传感器模块

发布于:2024-12-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、温度传感器模块

(1)资源介绍

        🔅原理图

        蓝桥杯物联网竞赛实训平台提供了一个拓展接口 CN2,所有拓展模块均可直接安装在 Lora 终端上使用;

图1        拓展接口

        温度传感器模块电路原理图如下所示:

图2        温度传感器模块电路原理图

        通过两张电路图连接可知,引脚资源配置情况为:

表1 引脚资源配置情况
Temperature MCU
SCL PB6
SDA PB7
ALE PB0

        🔅STS30-DIS-B(以下资料来源于STS30-DIS-B数据手册)

        STS3x-DIS是盛思锐最新的高精度数字温度传感器。它依赖于业界成熟的CMOSens®技术,与其前身相比,提供更高的智能,可靠性和更高的精度规格。其功能包括增强的信号处理,两个独特的用户可选择的I2C地址和高达1 MHz的通信速度。DFN封装的占地面积为2.5 x 2.5 mm2,同时保持0.9 mm的高度。这允许将STS3x-DIS集成到各种各样的应用中。此外,2.15 V至5.5 V的宽电源电压范围保证了广泛应用的兼容性。总而言之,STS3x-DIS融合了盛思锐超过15年的数字传感器专业知识。

图3        STS30-DIS 功能框图

        🌙引脚分配

表2 STS30-DIS-B引脚分配
Pin Name Comments
1 SDA 串行数据;输入/输出
2 ADDR

地址引脚;输入;连接到逻辑高或低,不要浮空

3 ALERT 指示警告状态;输出;如果不使用,必须保持浮空
4 SCL 串行时钟;输入/输出
5 VDD 电源电压;输入
6 nRESET 复位引脚,低电平有效;输入;如果不使用,建议保持浮空状态;可通过串联电阻R≥2kΩ与VDD连接
7 R 无电气功能;连接到VSS
8 VSS 接地

        🌙通信

        向传感器发送命令后,在传感器接收另一条命令之前,需要最小的等待时间为1ms

        所有STS30-DIS-B命令和数据都映射到16位地址空间。此外,数据和命令受到CRC校验和的保护。这提高了通信的可靠性。对传感器的16位命令已经包括3位CRC校验和。传感器发送和接收的数据总是经过一个8位的CRC校验。

  • 在写方向上,必须传输校验和因为STS30-DIS-B只接受后面跟着正确校验和的数据
  • 在读方向上,它留给主机来读取和处理校验和;

        🌙时钟拉伸Clock Stretching

  • 当没有时钟拉伸的命令发出时,如果没有数据存在,传感器响应一个带有不确认(NACK)的读报头;
  • 当发出带有时钟拉伸的命令时,传感器响应一个带有ACK的读报头,然后拉下SCL线。将SCL线拉下,直到测量完成。一旦测量完成,传感器释放SCL线并发送测量结果;

        🌙单次触发模式

图4        单次触发模式下的命令

由图4的流程图可知,单次触发模式的数据获取流程如下:

        1. 第一部分

图5        发送流程第一部分

🟠️伪代码如下:

I2C开始;
发送7位I2C地址+写命令位;
等待传感器发送ACK;
发送高8位命令;
等待传感器发送ACK;
发送低8位命令;
等待传感器发送ACK;
I2C结束;

        2. 第二部分

图6        发送流程第二部分

🟠️伪代码如下:

/* 时钟拉伸不使能 */
释放时钟线;(传感器测量中)
I2C开始;
发送7位I2C地址+读命令位;
等待传感器发送NACK;
I2C结束;
释放时钟线;(传感器测量中->传感器测量完成)
I2C开始;
发送7位I2C地址+读命令位;
等待传感器发送ACK;


/* 时钟拉伸使能 */
释放时钟线;(传感器测量中)
I2C开始;
发送7位I2C地址+读命令位;
等待传感器发送ACK;
(传感器将时钟线拉低)

        3. 第三部分

图7        发送流程第三部分

🟠️伪代码如下:

接收高8位温度数据;
发送ACK;
接收低8位温度数据;
发送ACK;
接收CRC校验和;
发送NACK;
I2C结束;

        🌙温度数据转换

T\left [ ^{\circ}C \right ]=-45+175\cdot \frac{S_{T}}{2^{16}-1}

T\left [ ^{\circ}F \right ]=-49+315\cdot \frac{S_{T}}{2^{16}-1}

(2)STM32CubeMX 软件配置


🔅“工程建立、时钟树配置、Debug 串行线配置、代码生成配置” 在下文中有讲解,这里不再赘述❗️

【蓝桥杯——物联网设计与开发】基础模块1- GPIO输出icon-default.png?t=O83Ahttps://blog.csdn.net/m0_63116406/article/details/135604705?spm=1001.2014.3001.5502

1️⃣点击引脚 PB6 → 选择 GPIO_Output 模式(此处默认为推挽输出);

     点击引脚 PB7 → 选择 GPIO_Output 模式(此处配置为开漏输出);

图8        引脚配置



2️⃣初始化 OLED;(配置步骤在下文中有讲解,这里不再赘述);

【蓝桥杯——物联网设计与开发】基础模块6 - OLED_蓝桥杯物联网oled-CSDN博客


3️⃣生成代码即可;

(3)代码编写

🟢️main 函数

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t H_VALUE, L_VALUE, CRC_VALUE;    // 高8位数据、低8位数据、CRC校验数据
float temp;                            // 温度数据
uint16_t ui_dat_temp;                    // 显示数据
uint8_t puc_oled[17];                    // OLED显示存储区
/* USER CODE END PV */
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C3_Init();
  /* USER CODE BEGIN 2 */
	OLED_Init();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* 第一部分 */
	I2CStart2();
	I2CSendByte2(0x94);
	I2CWaitAck2();
	I2CSendByte2(0x24);
	I2CWaitAck2();
	I2CSendByte2(0x0B);
	I2CWaitAck2();
	I2CStop2();
	/* 第二部分 */
	HAL_Delay(2);
	I2CStart2();
	I2CSendByte2(0x95);
	I2CWaitAck2();
	I2CStop2();
	HAL_Delay(2);
	I2CStart2();
	I2CSendByte2(0x95);
	I2CWaitAck2();
    /* 第三部分 */
	H_VALUE = I2CReceiveByte2();
	I2CSendAck2();
	L_VALUE = I2CReceiveByte2();
	I2CSendAck2();
	CRC_VALUE = I2CReceiveByte2();
	I2CSendNotAck2();
	I2CStop2();
	if(CRC_VALUE)
	{
		;
	}
	temp = ((H_VALUE << 8) | L_VALUE) * 175.0 / 65535 - 45;
    ui_dat_temp = (uint16_t)(Temp_Read() * 10);
	sprintf((char*)puc_oled, "   Temperature  ");
	OLED_ShowString(0, puc_oled);
	sprintf((char*)puc_oled, "      %.1f  ", ui_dat_temp / 10.0);
	OLED_ShowString(2, puc_oled);
    HAL_Delay(500);    //延时一会儿
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

(4)实验现象

        能够实时测量环境温度。


二、温度采集接口函数封装

🟡️软件i2c.c

#include "i2c_2.h"

#define DELAY_TIME 20

//I2C总线内部延时函数
static void delay1(unsigned int n)
{
    uint32_t i;
    for ( i = 0; i < n; ++i);
}

//配置SDA引脚为输入模式
void SDA_Input_Mode2(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.Pin = SDA2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//配置SDA引脚为输出模式
void SDA_Output_Mode2(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	GPIO_InitStruct.Pin = SDA2;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//SDA引脚输出
void SDA_Output2(uint16_t val )
{
	if(val)
		GPIOB->BSRR |= SDA2;
	else
		GPIOB->BRR |= SDA2;
}
//SCL引脚输出
void SCL_Output2( uint16_t val )
{
	if(val)
		GPIOB->BSRR |= SCL2;
	else
		GPIOB->BRR |= SCL2;
}
//读取SDA引脚状态
uint8_t SDA_Input2(void)
{
	return HAL_GPIO_ReadPin(GPIOB, SDA2);
}

//I2C总线启动信号
void I2CStart2(void)
{
    SDA_Output2(1);
    delay1(DELAY_TIME);
    SCL_Output2(1);
    delay1(DELAY_TIME);
    SDA_Output2(0);
    delay1(DELAY_TIME);
    SCL_Output2(0);
    delay1(DELAY_TIME);
}

//I2C总线停止信号
void I2CStop2(void)
{
    SCL_Output2(0);
    delay1(DELAY_TIME);
    SDA_Output2(0);
    delay1(DELAY_TIME);
    SCL_Output2(1);
    delay1(DELAY_TIME);
    SDA_Output2(1);
    delay1(DELAY_TIME);

}

//等待应答
unsigned char I2CWaitAck2(void)
{
    unsigned short cErrTime = 5;
    SDA_Input_Mode2();
    delay1(DELAY_TIME);
    SCL_Output2(1);
    delay1(DELAY_TIME);
    while(SDA_Input2())
    {
        cErrTime--;
        delay1(DELAY_TIME);
        if (0 == cErrTime)
        {
            SDA_Output_Mode2();
            I2CStop2();
            return ERROR;
        }
    }
    SCL_Output2(0);
    SDA_Output_Mode2();
    delay1(DELAY_TIME);
    return SUCCESS;
}

//发送应答
void I2CSendAck2(void)
{
    SDA_Output2(0);
    delay1(DELAY_TIME);
    delay1(DELAY_TIME);
    SCL_Output2(1);
    delay1(DELAY_TIME);
    SCL_Output2(0);
    delay1(DELAY_TIME);

}

//发送非应答
void I2CSendNotAck2(void)
{
    SDA_Output2(1);
    delay1(DELAY_TIME);
    delay1(DELAY_TIME);
    SCL_Output2(1);
    delay1(DELAY_TIME);
    SCL_Output2(0);
    delay1(DELAY_TIME);

}

//发送一个字节数据
void I2CSendByte2(unsigned char cSendByte)
{
    unsigned char  i = 8;
    while (i--)
    {
        SCL_Output2(0);
        delay1(DELAY_TIME);
        SDA_Output2(cSendByte & 0x80);
        delay1(DELAY_TIME);
        cSendByte += cSendByte;
        delay1(DELAY_TIME);
        SCL_Output2(1);
        delay1(DELAY_TIME);
    }
    SCL_Output2(0);
    delay1(DELAY_TIME);
}

//接收一个字节数据
unsigned char I2CReceiveByte2(void)
{
    unsigned char i = 8;
    unsigned char cR_Byte = 0;
    SDA_Input_Mode2();
    while (i--)
    {
        cR_Byte += cR_Byte;
        SCL_Output2(0);
        delay1(DELAY_TIME);
        delay1(DELAY_TIME);
        SCL_Output2(1);
        delay1(DELAY_TIME);
        cR_Byte |=  SDA_Input2();
    }
    SCL_Output2(0);
    delay1(DELAY_TIME);
    SDA_Output_Mode2();
    return cR_Byte;
}

🟡️软件i2c.h

#ifndef __I2C_2_H
#define __I2C_2_H

#include "main.h"
//I2C总线引脚定义
#define SCL2	GPIO_PIN_6
#define SDA2	GPIO_PIN_7

//接口函数
void I2CStart2(void);
void I2CStop2(void);
unsigned char I2CWaitAck2(void);
void I2CSendAck2(void);
void I2CSendNotAck2(void);
void I2CSendByte2(unsigned char cSendByte);
unsigned char I2CReceiveByte2(void);

#endif

🟡️温度采集函数

/* 温度采集函数,中等可重复性+不使能时钟拉伸 */
float Temp_Read(void)
{
	uint8_t H_VALUE, L_VALUE, CRC_VALUE;
	float temp;
	I2CStart2();
	I2CSendByte2(0x94);
	I2CWaitAck2();
	I2CSendByte2(0x24);
	I2CWaitAck2();
	I2CSendByte2(0x0B);
	I2CWaitAck2();
	I2CStop2();
	HAL_Delay(2);
	
	I2CStart2();
	I2CSendByte2(0x95);
	I2CWaitAck2();
	I2CStop2();
	HAL_Delay(2);
	
	I2CStart2();
	I2CSendByte2(0x95);
	I2CWaitAck2();
	H_VALUE = I2CReceiveByte2();
	I2CSendAck2();
	L_VALUE = I2CReceiveByte2();
	I2CSendAck2();
	CRC_VALUE = I2CReceiveByte2();
	I2CSendNotAck2();
	I2CStop2();
	if(CRC_VALUE)
	{
		;
	}
	temp = ((H_VALUE << 8) | L_VALUE) * 175.0 / 65535 - 45;
	return temp;
}

🔴温度采集接口函数调用实例

/* 采集任务函数 */
void Task_Colt(void)
{
    /* 200ms进入一次 */
	if(cnt_colt < 200)	return;
	cnt_colt = 0;
    /* 温度采集与转换 */
	ui_dat_temp = (uint16_t)(Temp_Read() * 10);
}

三、踩坑日记

(1)引脚配置问题

  1. 推荐使用软件模拟I2C(硬件I2C,在4ti测试出来有问题);
  2. PB7(SDA)配置为开漏输出、速度快;PB6(SCL)配置为推挽输出,速度快;

(2)底层驱动延时问题

        🔅注意编写底层驱动时存在两次HAL_Delay(2),释放时钟线作用,根据芯片手册说明,此处需要≥1ms;

(3)ALERT引脚

        🔅报警引脚Alert一般用于连接MCU的中断引脚。引脚的输出取决于可编程限制的温度读数值。当满足报警条件时,引脚拉高;

        🔅根据手册说明,不使用时必须保持浮空。