温度传感器(DS18B20和PT100)

1.流程图

2.PT100

1)信号采集的基本原理

① PT100的阻值会随温度的变化而成正比变化(温度越高阻值越大),但阻值变化很小,约等于0.385 Ω / 度;

② PT100的测温范围是﹣ 200 ℃ ﹣200℃,且在0℃时,阻值刚好等于100 Ω ;

③ PT100的工作电流要小于5 m A ;

④ PT100的阻值虽然随温度的变化而成正比变化,但在不同温度区间内其变化的速率(也就是K 值 K值K值)不一样。

2)PT100温度阻止变化表

3.PT100驱动电路

1)通过分压法,AD采集PT100电压从而得到阻值求出温度

常温(25 ℃ 25℃25℃)水中的PT100的阻值大概在109.89 Ω左右。

单片机输出3.3v电压,PT100分掉电压约为:

109.89 ∗ 0.005 = 0.54945 V

将其根据AD转换的换算公式换算成AD值大概为:

0.54945 / 3.3 ∗ 4096 = 681.98 ≈ 682

当温度上升一度,假设PT100的阻值刚好上升了0.385 Ω,那么其分掉的电压的变动值约等于:

0.385 ∗ 0.005 = 0.001925 V

将其根据AD转换的换算公式换算成AD值大概为:

0.001925 / 3.3 ∗ 4096 = 2.39 ≈ 2

实验中发现,由于stm32供电3.3v电压不稳定,ADC采集PT100电压波动较大,分压误差较大,优化方案成设计一款恒流源电路,通过采集PT100的电压以及恒流源的电流可以得出PT100的阻值,从而得出温度值。

2)基于LDO稳压器(MD5333)的恒流源电路

网上有很多测试PT100的驱动电路,如直流电桥电路,基于运算放大器的恒流源电路等等,笔者在选择驱动电路时也花费了许多时间,综合考虑打板的难度元器件数量等因素,最后选择了基于LDO稳压器(MD5333)的恒流源电路,其电路图如下所示:

至此硬件部分基本选择完毕,开发板方面用的是正点原子F10ZET6精英板

4.DS18B20模块

为了测试实时温度与PT100温度对比,加入了DS18B20模块进行校准对比测试

1)DS18B20简介

DS18B20是一款单总线的温度传感器,测试温度范围为-55~+125℃,精度为±0.5℃。现场温度直

接以单总线的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根

据实际要求通过简单的编程实现 9~12 位的数字值读数方式。它的工作电压范围为 3~5.5V,采

用多种封装形式,从而使系统设置灵活、方便,设定分辨率以及用户设定的报警温度存储在

EEPROM 中,掉电后依然保存。

2)DS18B20工作时序简介

所有单总线器件要求采用严格的信号时序,以保证数据的完整性。DS18B20 共有 6 种信号 类型:复位脉冲、应答脉冲、写 0、写 1、读 0 和读 1。所有这些信号,除了应答脉冲以外,都 是由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。

① 复位脉冲和应答脉冲

单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少要在 480us,以产生复位脉冲。接着主机释放总线,4.7K 的上拉电阻将单总线拉高,延时时间要在 15~60us,并进入接收模式(Rx)。接着 DS18B20 拉低总线 60~240us,以产生低电平应答脉冲。

② 写时序

写时序包括写 0 时序和写 1 时序。所有写时序至少需要 60us,且在两次独立的写时序之间

至少需要 1us 的恢复时间,两种写时序均起始于主机拉低总线。写 1 时序:主机输出低电平,

延时 2us,然后释放总线,延时 60us。写 0 时序:主机输出低电平,延时 60us,然后释放总线

延时 2us。

③ 读时序

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,

必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要 60us,且在 2 次独立的读

时序之间至少需要 1us 的恢复时间。每个读时序都由主机发起,至少拉低总线 1us。主机在读时

序期间必须释放总线,并且在时序起始后的 15us 之内采样总线状态。典型的读时序过程为:主

机输出低电平延时 2us,然后主机转入输入模式延时 12us,然后读取单总线当前的电平,然后

延时 50us。

在了解单总线时序之后,我们来看一下 DS18B20 的典型温度读取过程,DS18B20 的典型

温度读取过程为:复位→发 SKIPROM(0xCC)→发开始转换命令(0x44)→延时→复位→发

送 SKIPROM 命令(0xCC)→发送存储器命令(0xBE)→连续读取两个字节数据(即温度)→

结束。

3)原理图及CUBEMAX配置

由原理图可知,DS18B20是由PG11口提供使能

打开串口打印温度信息

4)代码部分

代码部分移植正点原子的ds18b20库,稍作修改

#ifndef __DS18B20_H
#define __DS18B20_H

#include "tim.h"


/******************************************************************************************/
/* DS18B20引脚 定义 */

#define DS18B20_DQ_GPIO_PORT                GPIOG
#define DS18B20_DQ_GPIO_PIN                 GPIO_PIN_11
#define DS18B20_DQ_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)   /* PG口时钟使能 */

/******************************************************************************************/

/* IO操作函数 */
#define DS18B20_DQ_OUT(x)   do{ x ? \
                                HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN, GPIO_PIN_SET) : \
                                HAL_GPIO_WritePin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN, GPIO_PIN_RESET); \
                            }while(0)                                                       /* 数据端口输出 */
#define DS18B20_DQ_IN       HAL_GPIO_ReadPin(DS18B20_DQ_GPIO_PORT, DS18B20_DQ_GPIO_PIN)     /* 数据端口输入 */


uint8_t ds18b20_init(void);         /* 初始化DS18B20 */
uint8_t ds18b20_check(void);        /* 检测是否存在DS18B20 */
short ds18b20_get_temperature(void);/* 获取温度 */

#endif
#include "tim.h"
#include "ds18b20.h"
#include "usart.h"


/**
 * @brief       复位DS18B20
 * @param       data: 要写入的数据
 * @retval      无
 */
static void ds18b20_reset(void)
{
    DS18B20_DQ_OUT(0);  /* 拉低DQ,复位 */
    delay_us(750);      /* 拉低750us */
    DS18B20_DQ_OUT(1);  /* DQ=1, 释放复位 */
    delay_us(15);       /* 延迟15US */
}

/**
 * @brief       等待DS18B20的回应
 * @param       无
 * @retval      0, DS18B20正常
 *              1, DS18B20异常/不存在
 */
uint8_t ds18b20_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;

    while (DS18B20_DQ_IN && retry < 200)    /* 等待DQ变低, 等待200us */
    {
        retry++;
        delay_us(1);
    }

    if (retry >= 200)
    {
        rval = 1;
    }
    else
    {
        retry = 0;

        while (!DS18B20_DQ_IN && retry < 240)   /* 等待DQ变高, 等待240us */
        {
            retry++;
            delay_us(1);
        }

        if (retry >= 240) rval = 1;
    }

    return rval;
}

/**
 * @brief       从DS18B20读取一个位
 * @param       无
 * @retval      读取到的位值: 0 / 1
 */
static uint8_t ds18b20_read_bit(void)
{
    uint8_t data = 0;
    DS18B20_DQ_OUT(0);
    delay_us(2);
    DS18B20_DQ_OUT(1);
    delay_us(12);

    if (DS18B20_DQ_IN)
    {
        data = 1;
    }

    delay_us(50);
    return data;
}

/**
 * @brief       从DS18B20读取一个字节
 * @param       无
 * @retval      读到的数据
 */
static uint8_t ds18b20_read_byte(void)
{
    uint8_t i, b, data = 0;

    for (i = 0; i < 8; i++)
    {
        b = ds18b20_read_bit(); /* DS18B20先输出低位数据 ,高位数据后输出 */
        
        data |= b << i;         /* 填充data的每一位 */ 
    }

    return data;
}

/**
 * @brief       写一个字节到DS18B20
 * @param       data: 要写入的字节
 * @retval      无
 */
static void ds18b20_write_byte(uint8_t data)
{
    uint8_t j;

    for (j = 1; j <= 8; j++)
    {
        if (data & 0x01)
        {
            DS18B20_DQ_OUT(0);  /*  Write 1 */
            delay_us(2);
            DS18B20_DQ_OUT(1);
            delay_us(60);
        }
        else
        {
            DS18B20_DQ_OUT(0);  /*  Write 0 */
            delay_us(60);
            DS18B20_DQ_OUT(1);
            delay_us(2);
        }

        data >>= 1;             /* 右移,获取高一位数据 */
    }
}

/**
 * @brief       开始温度转换
 * @param       无
 * @retval      无
 */
static void ds18b20_start(void)
{
    ds18b20_reset();
    ds18b20_check();
    ds18b20_write_byte(0xcc);   /*  skip rom */
    ds18b20_write_byte(0x44);   /*  convert */
}

/**
 * @brief       初始化DS18B20的IO口 DQ 同时检测DS18B20的存在
 * @param       无
 * @retval      0, 正常
 *              1, 不存在/不正常
 */
uint8_t ds18b20_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    
    DS18B20_DQ_GPIO_CLK_ENABLE();   /* 开启DQ引脚时钟 */

    gpio_init_struct.Pin = DS18B20_DQ_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;            /* 开漏输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
    HAL_GPIO_Init(DS18B20_DQ_GPIO_PORT, &gpio_init_struct); /* 初始化DS18B20_DQ引脚 */
    /* DS18B20_DQ引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    ds18b20_reset();
    return ds18b20_check();
}

/**
 * @brief       从ds18b20得到温度值(精度:0.1C)
 * @param       无
 * @retval      温度值 (-550~1250)
 *   @note      返回的温度值放大了10倍.
 *              实际使用的时候,要除以10才是实际温度.
 */
short ds18b20_get_temperature(void)
{
    uint8_t flag = 1;           /* 默认温度为正数 */
    uint8_t TL, TH;
    short temp;
    
    ds18b20_start();            /*  ds1820 start convert */
    ds18b20_reset();
    ds18b20_check();
    ds18b20_write_byte(0xcc);   /*  skip rom */
    ds18b20_write_byte(0xbe);   /*  convert */
    TL = ds18b20_read_byte();   /*  LSB */
    TH = ds18b20_read_byte();   /*  MSB */
    if (TH > 7)
    {/* 温度为负,查看DS18B20的温度表示法与计算机存储正负数据的原理一致:
        正数补码为寄存器存储的数据自身,负数补码为寄存器存储值按位取反后+1
        所以我们直接取它实际的负数部分,但负数的补码为取反后加一,但考虑到低位可能+1后有进位和代码冗余,
        我们这里先暂时没有作+1的处理,这里需要留意 */
        TH = ~TH; 
        TL = ~TL;
        flag = 0;   /* 温度为负 */
    }
    temp = TH;      /* 获得高八位 */
    temp <<= 8;
    temp += TL;     /* 获得底八位 */
    /* 转换成实际温度 */
    if (flag == 0)
    {   /* 将温度转换成负温度,这里的+1参考前面的说明 */
        temp = (double)(temp+1) * 0.625;
        temp = -temp;   
    }
    else
    {
        temp = (double)temp * 0.625;
    }
    
    return temp;
}

main.c

5) 效果展示

5.红外遥控模块

1)无线模块编码协议

红外遥控的编码方式目前广泛使用的是:PWM(脉冲宽度调制)的 NEC 协议和 Philips PPM

(脉冲位置调制)的 RC-5 协议的。开发板配套的遥控器使用的是 NEC 协议,其特征如下:

1、8 位地址和 8 位指令长度;

2、地址和命令 2 次传输(确保可靠性);

3、PWM 脉冲位置调制,以发射红外载波的占空比代表“0”和“1”;

4、载波频率为 38Khz;

5、位时间为 1.125ms 或 2.25ms;

在 NEC 协议中,如何为协议中的数据‘0’或者‘1’?这里分开红外接收器和红外发射

器。

红外发射器:发送协议数据‘0’ = 发射载波信号 560us + 不发射载波信号 560us

发送协议数据‘1’ = 发射载波信号 560us + 不发射载波信号 1680us

红外发射器的位定义如下图所示

红外接收器:接收到协议数据‘0’ = 560us 低电平 + 560us 高电平

接收到协议数据‘1’ = 560us 低电平 + 1680us 高电平

NEC 遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步

码由一个 9ms 的低电平和一个 4.5ms 的高电平组成,地址码、地址反码、控制码、控制反码

均是 8 位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性 。

因此可以利用输入捕获来测量高电平的脉宽,从而实现遥控的解码。

2) 原理图及CUBEMAX配置

由原理图可知无线模块通过PB9管脚使能并通过TIM4的4通道进行采集:

TIM4_CH4的默认管脚不是PB9要手动就行设置,同时打开中断设置

3)代码部分

通过tim回调函数捕获上升沿

此时就可以得到解码信号:

此时数据较为复杂,可以对数据稍加处理:

效果如下:

后两位就是解码及其反码,此时就可以定义成为宏加以使用来调整温度阈值:

效果如下:

红外部分代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
#define MAXUP 157
#define MAXDOWN 87
#define MINUP 221
#define MINDOWN 61
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

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

/* USER CODE BEGIN PV */
uint32_t upCount=0;
uint16_t ValueUp=0;
uint16_t ValueDown=0;
uint8_t isUpCapt=1;
uint16_t width=0;
uint16_t buffer[128]={0};
uint16_t bufferId=0;
uint8_t rcvFalg=0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	upCount++;
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(isUpCapt)//如果是上升沿捕获
	{
		ValueUp=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_4);
		isUpCapt=0;
		__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_4,TIM_ICPOLARITY_FALLING);
		upCount=0;
	}
	else{
		ValueDown=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_4);
		isUpCapt=1;
		__HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_4,TIM_ICPOLARITY_RISING);
		width=ValueDown+upCount*65536-ValueUp;
		if(width>4400&&width<4600)
		{
			bufferId=0;
			buffer[bufferId++]=width;
		}
		else if(bufferId>0)
		{
			buffer[bufferId++]=width;
			if(bufferId>32)
			{
				rcvFalg=1;
				bufferId=0;
			}
		}
	}
}
void bitBuffer2num(char num[])
{
	num[0]=0;
	num[1]=0;
	num[2]=0;
	num[3]=0;
	for(int i=0;i<32;i++)
	{
		if(buffer[i+1]<1000)
		{
			num[i/8]=num[i/8]<<1;
		}
		else
		{
			num[i/8]=num[i/8]<<1;
			num[i/8]|=0x01;
		}
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	char printbuff[128]={0};
	char num[4]={0};
	char key=0;
  /* 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_TIM4_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
	HAL_TIM_Base_Start_IT(&htim4);//定时器更新产生中断
	HAL_TIM_IC_Start_IT(&htim4,TIM_CHANNEL_4);//
  while (1)
  {
		if(rcvFalg)
		{
			for(int i=0;i<4;i++)
			{
				bitBuffer2num(num);
				sprintf(printbuff,"0x%02x ",num[i]);
				HAL_UART_Transmit(&huart1,printbuff,strlen(printbuff),HAL_MAX_DELAY);
			}
//				sprintf(printbuff,"%u ",buffer[i]);
//				HAL_UART_Transmit(&huart1,printbuff,strlen(printbuff),HAL_MAX_DELAY);
//			}
			HAL_UART_Transmit(&huart1,"\r\n",2,HAL_MAX_DELAY);
			rcvFalg=0;
		}
		printf("%d\r\n",num[3]);
			if(num[3]==157)
			{
				printf("111111\r\n");
			}
			HAL_Delay(1000);
    /* USER CODE END WHILE */

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

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

6.报警模块

如果温度超过设定的阈值,led红灯闪烁,蜂鸣器报警

CUBEMAX和代码源码:

链接:百度网盘 请输入提取码

提取码:cwrq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值