STM32 DS18B20温度传感器实验(HAL库)

实验摘要

本文主要关于如何用STM32系列单片机驱动DS18B20温度传感器实现温度的串口打印显示,本实验中STM32基于HAL库开发。本文全程记录实验过程,手把手教大家基于STM32的DS18B20温度采集实验。

DS18B20简述

首先,我们拿到DS18B20之后进行观察。
DS18B20
该器件只有3个引脚,分别为电源端VCC,地GND和数据线DQ。只有一条数据线,说明该器件是单总线器件。单总线器件好处是只占用一个单片机的一个I/O口。
DS18B20通信协议通过查阅DS18B20中文资料获得,该部分将在驱动文件编写部分详细讲解。

STMCubeMX引脚配置

本文使用STMCubeMX软件进行引脚和时钟的配置

芯片选型

芯片选型
本文选择STM32L431RC系列,根据自己的芯片选择。

引脚和时钟配置

引脚配置
引脚主要配置外部时钟输入输出口,串口通信USART1用于打印温度数据,传感器数据DQ接口(此处使用PA5)。时钟配置
将HCLK配置为80MHz。
工程管理
工程管理,设置项目名称和保存路径,选择MDK-ARM V5。
代码生成
Code Generator中建议勾选Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral,为每个外设生成独立的.c文件。

生成ARM-MDK工程

点击GENERATE CODE生成ARM-MDK工程。

编写DS18B20驱动文件

新建工程项目组

项目组
单击红圈出现项目组
这里可以自行添加文件管理项目结构,清晰明了。这里我们可以在Group新建一个文件夹Hardware用于存放DS18B20的驱动文件。(DS18B20.CDS18B20.h
接着在文件夹Hardware中添加DS18B20.CDS18B20.h

编写DS18B20.c

DS18B20中文资料
链接: https://pan.baidu.com/s/1k060r4s_5XI1R9r00jDP2g 提取码: nmr4 .
首先写一个粗略的微秒延时函数,用于单线协议中的延时。

/****************************************************************************
函数名:delay_us
功能:微秒级延时
输入:延时数据
输出:无
返回值:无
备注:
****************************************************************************/
void delay_us(uint32_t time)
{
    
    
  time *= 10;
	while(time)
		time--;
}

初始化

主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。

做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。

我们需要配置引脚为输入或输出模式,需要两个函数。
发送复位信号和检测存在脉冲两个函数。
引脚输入输出配置函数参考gpio.c中的void MX_GPIO_Init(void)函数编写。

/****************************************************************************
函数名:DS18B20_IO_IN
功能:使DS18B20_DQ引脚变为输入模式
输入:无
输出:无
返回值:无
备注:DQ引脚为PA5
****************************************************************************/
void DS18B20_IO_IN(void){
    
    
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin = GPIO_PIN_5;
	GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}


/****************************************************************************
函数名:DS18B20_IO_OUT
功能:使DS18B20_DQ引脚变为推挽输出模式
输入:无
输出:无
返回值:无
备注:DQ引脚为PA5
****************************************************************************/
void DS18B20_IO_OUT(void){
    
    
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin = GPIO_PIN_5;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
}


/***************************************************************************
函数名:DS18B20_Rst
功  能:发送复位信号
输  入: 无
输  出:无
返回值:无
备  注:
***************************************************************************/
void DS18B20_Rst(void){
    
    
	DS18B20_IO_OUT();//引脚输出模式
	
	//拉低总线并延时750us
	DS18B20_DQ_OUT_LOW;
	delay_us(750);     
	
	//释放总线为高电平并延时等待15~60us
	DS18B20_DQ_OUT_HIGH;
	delay_us(15);
}
	

/***************************************************************************
函数名:DS18B20_Check
功  能:检测DS18B20返回的存在脉冲
输  入: 无
输  出:无
返回值:0:成功  1:失败   2:释放总线失败
备  注:
***************************************************************************/
uint8_t DS18B20_Check(void){
    
    
	//定义一个脉冲持续时间
	uint8_t retry = 0;
	//引脚设为输入模式
	DS18B20_IO_IN();
	while(DS18B20_DQ_IN && retry < 200){
    
    
		retry++;
		delay_us(1);
	}
	
	if(retry >= 200)
		return 1;
	else
		retry = 0;
	
	//判断DS18B20是否释放总线
	while(!DS18B20_DQ_IN && retry < 240){
    
    
		retry++;
		delay_us(1);
	}
	
	if(retry >= 240)
		return 2;
	
	return 0;
}

写时序

写时序

写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。

/***************************************************************************
函数名:DS18B20_Write_Byte
功  能:向DS18B20写一个字节
输  入: 要写入的字节
输  出:无
返回值:无
备  注:
***************************************************************************/
void DS18B20_Write_Byte(uint8_t data){
    
    
	uint8_t j;
	uint8_t databit;
	DS18B20_IO_OUT();
	for(j=1;j<=8;j++){
    
    
		databit=data&0x01;//取数据最低位
		data=data>>1;     //右移一位
		if(databit){
    
          //当前位写1
			DS18B20_DQ_OUT_LOW;
			delay_us(2);
			DS18B20_DQ_OUT_HIGH;
			delay_us(60);
		}else{
    
              //当前位写0
			DS18B20_DQ_OUT_LOW;
			delay_us(60);
			DS18B20_DQ_OUT_HIGH;
			delay_us(2);
		}
	}
}

读时序

读时序

/***************************************************************************
函数名:DS18B20_Read_Bit
功  能:向DS18B20读一个位
输  入: 无
输  出:无
返回值:读入数据
备  注:
***************************************************************************/
uint8_t DS18B20_Read_Bit(void){
    
    
	uint8_t data;
	DS18B20_IO_OUT();
	DS18B20_DQ_OUT_LOW;
	delay_us(2);
	DS18B20_DQ_OUT_HIGH;
	DS18B20_IO_IN();
	delay_us(12);
	
	if(DS18B20_DQ_IN)
		data = 1;
	else
		data = 0;
	
	delay_us(50);
	return data;
}


/***************************************************************************
函数名:DS18B20_Read_Byte
功  能:向DS18B20读一个字节
输  入: 无
输  出:无
返回值:读入数据
备  注:
***************************************************************************/
uint8_t DS18B20_Read_Byte(void){
    
    
	uint8_t i,j,data;
	data = 0;
	for(i=1;i<=8;i++){
    
    
		j = DS18B20_Read_Bit();
		data = (j<<7)|(data>>1);
		/*j=0或1,j<<7=0x00或0x80,和data右移一位相或,即把1/0写入最高位,下次再往右移位*/

	}
	return data;
}

读取温度

基本时序操作已经完成,再根据资料中的ROM命令编写DS18B20的启动函数。由于我们只使用一个DS18B20,所以可以直接跳过ROM。

DS18B20的ROM指令集

在这里插入图片描述

存储器指令

在这里插入图片描述
用到的指令

指令名称 指令代码
跳过ROM 0xCC
温度变换 0x44
读暂存器 0xBE
/***************************************************************************
函数名:DS18B20_Start
功  能:DS18B20开启
输  入: 无
输  出:无
返回值:无
备  注:
***************************************************************************/
void DS18B20_Start(void){
    
    
	DS18B20_Rst();
	DS18B20_Check();
	DS18B20_Write_Byte(0xcc);//跳过ROM
	DS18B20_Write_Byte(0x44);//温度变换命令
}


/***************************************************************************
函数名:DS18B20_Init
功  能:DS18B20初始化
输  入: 无
输  出:无
返回值:无
备  注:
***************************************************************************/
uint8_t DS18B20_Init(void){
    
    
	//引脚初始化
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin = GPIO_PIN_5;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Pull = GPIO_PULLUP;
	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	DS18B20_Rst();
	return DS18B20_Check();
}

/***************************************************************************
函数名:DS18B20_Read_Temperature
功  能:读取一次温度
输  入: 无
输  出:无
返回值:读取到的温度数据
备  注:适用于总线上只有一个DS18B20的情况
***************************************************************************/
short DS18B20_Get_Temperature(uint8_t a){
    
    
	uint8_t temp;
	uint8_t TL,TH;
	short temperature;
	
	DS18B20_Start();
	DS18B20_Rst();
	DS18B20_Check();
  DS18B20_Write_Byte(0xcc);//跳过ROM
	DS18B20_Write_Byte(0xbe);//读暂存器
	TL = DS18B20_Read_Byte();//低八位
	TH = DS18B20_Read_Byte();//高八位
	
	//判断温度值是否为负数
	if(TH>0x70){
    
    
		TH = ~TH;
		TL = ~TL;
		temp = 0;
	}else
		temp = 1;
	
	temperature = TH;
	temperature <<= 8;
	temperature += TL;
	temperature = (float)temperature*0.625;
	if(temperature)
		return temperature;
	else
		return -temperature;
}

在DS18B20.c开头包含头文件#include "DS18B20.h"

#include "DS18B20.h"

编写DS18B20.h

#include "stm32l4xx_hal.h"
#include "main.h"
#define  DS18B20_DQ_OUT_HIGH       HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_SET)
#define  DS18B20_DQ_OUT_LOW        HAL_GPIO_WritePin(DQ_GPIO_Port, DQ_Pin, GPIO_PIN_RESET)
#define  DS18B20_DQ_IN             HAL_GPIO_ReadPin(DQ_GPIO_Port, DQ_Pin)

头文件中要包含hal库头文件#include "stm32l4xx_hal.h"#include "main.h"
定义三个引脚操作函数。

主程序实现功能

用户包含

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "DS18B20.h"
/* USER CODE END Includes */

函数声明

/* USER CODE BEGIN PFP */
uint8_t DS18B20_Init(void);
short DS18B20_Get_Temperature(void);
/* USER CODE END PFP */
/* USER CODE BEGIN 1 */
 float temperature;
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
  while(DS18B20_Init()){
    
    
	printf("DS18B20 checked failed!!!\r\n");
	HAL_Delay(500);
	}
	printf("DS18B20 checked success!!!\r\n");
  /* USER CODE END 2 */
/* USER CODE BEGIN 3 */
    temperature = DS18B20_Get_Temperature();
		if(temperature < 0)
		printf("temperature = -%.2f degree\r\n",temperature/10);
		else
		printf("temperature = %.2f degree\r\n",temperature/10);
		HAL_Delay(200);}
  /* USER CODE END 3 */

重定向printf函数

在usart.c中添加printf重定向函数,该函数在不同信号芯片可能有不同。

#if 1
#include <stdio.h>

int fputc(int ch, FILE *stream)
{
    
    
    /* 堵塞判断串口是否发送完成 */
    while((USART1->ISR & 0X40) == 0);

    /* 串口发送完成,将该字符发送 */
    USART1->TDR = (uint8_t) ch;

    return ch;
}
#endif

包含头文件路径

单击魔法棒图标在这里插入图片描述
选择C/C++,在include path中添加文件路径。在这里插入图片描述
>>New(Insert)>>
在这里插入图片描述
选择DS18B20.c和DS18B20.h的路径。
在这里插入图片描述

编译项目并下载到硬件中

连接硬件线路

在这里插入图片描述
三条线路,连接之后DS18B20模块电源指示灯亮起。

检验结果

烧录程序,并打开串口助手,这里推荐win10应用市场的串口调试助手,可以免费下载。
在这里插入图片描述
我们看到温度数据可以被读取出来,可以用手捂一下传感器看温度是否有变化。实验完成!

总结

该实验完成了对DS18B20温度传感器的使用,主要重点是对单总线器件通信时序的学习,掌握了单总线器件时序的原理可以帮助我们使用其他的器件。

猜你喜欢

转载自blog.csdn.net/liuyy_2000/article/details/113754150