STM32
直播中

无人岛

10年用户 911经验值
擅长:嵌入式技术 制造/封装 控制/MCU
私信 关注
[问答]

如何用STM32驱动DS18B20温度传感器实现温度的串口打印显示?

如何用STM32驱动DS18B20温度传感器实现温度的串口打印显示?

回帖(1)

王欢

2021-11-23 11:46:21
  实验摘要

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


/****************************************************************************
函数名: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指令集
  
  
  

  

  
   存储器指令
  
  
  

  

用到的指令
  [tr]指令名称指令代码[/tr]
跳过ROM0xCC
温度变换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;
}

定义三个引脚操作函数。
  
主程序实现功能
用户包含


/* 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!!!rn");
        HAL_Delay(500);
        }
        printf("DS18B20 checked success!!!rn");
  /* USER CODE END 2 */


/* USER CODE BEGIN 3 */
    temperature = DS18B20_Get_Temperature();
                if(temperature < 0)
                printf("temperature = -%.2f degreern",temperature/10);
                else
                printf("temperature = %.2f degreern",temperature/10);
                HAL_Delay(200);}
  /* USER CODE END 3 */


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


#if 1
#include


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温度传感器的使用,主要重点是对单总线器件通信时序的学习,掌握了单总线器件时序的原理可以帮助我们使用其他的器件。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分