怎样用stm32驱动ds18b20温度芯片

您所在的位置:网站首页 温度传感器连接 怎样用stm32驱动ds18b20温度芯片

怎样用stm32驱动ds18b20温度芯片

2024-07-14 09:26| 来源: 网络整理| 查看: 265

一、DS18B20简介

DS18B20是一一款常用的数字温度传感器,使用单个引脚完成主从机的双向通信是这款芯片的特点。

物理连接简单,那自然有特殊的时序需要满足,这也是使用这款芯片时麻烦的一点。

由于DS18B20不支持我们熟悉的IIC和SPI协议,那也就没有现成的库函数来支持我们的读写操作,也就是需要我们自行编写驱动。

但本文并不打算从解读数据手册中的时序图开始说太多废话,只给把代码给出,介绍一下驱动编写时可能遇到的一些问题,结合代码给出解决方案。

根据数据手册,这款芯片通常有三种封装:

在这里插入图片描述

三者都是通过DQ这个引脚来进行与主机的通信。所以问题简化为:

控制与DQ连接的引脚的电平变化

实际上,ds18b20支持六个操作指令:

在这里插入图片描述

只需要按照一定电平协议把这些命令发出去,就能够从DQ读取到想要的数据。而这些指令都刚好只有8位(bit),也就是一个字节。于是乎,ds18b20驱动的编写进一步简化为四个小任务:

1、初始化配置 2、通过DQ读取一个字节 3、通过DQ发送一个字节 4、读取温度 二、驱动代码介绍

1、初始化配置

初始化包含两步:

1、`DQ`对应的引脚的GPIO配置 2、通过`DQ`对`ds18b20`芯片的初始化

对应驱动代码的这部分:

/** * @brief 配置DQ并复位DS18B20 * @param 无 * @retval 无 */ void DS18B20_Init(void) { /* 初始化GPIO */ DS18B20_DQGPIOConfig(); /* 复位 */ DS18B20_Rst(); }

DS18B20_DQGPIOConfig()使能APB2总线时钟和GPIO时钟,配置DQ对应的引脚为推挽输出模式即可:

/** * @brief 配置DQ对应的GPIO引脚 * @param 无 * @retval 无 */ void DS18B20_DQGPIOConfig(void) { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启LED相关的GPIO外设时钟*/ RCC_APB2PeriphClockCmd(DQ_GPIO_CLK, ENABLE); /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = PINDQ; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIODQ, &GPIO_InitStructure); }

上面的代码中包含一些宏定义:

/* DQ引脚相关宏定义 */ #define GPIODQ GPIOB #define PINDQ GPIO_Pin_8 /* GPIO端口时钟 */ #define DQ_GPIO_CLK RCC_APB2Periph_GPIOB

注:为了不至于看花眼,不会一次性将所有的宏和函数实现贴出来,说到哪儿就贴到那儿。同时也不用担心不好拼凑成完整的驱动程序,因为文末会放出驱动源代码链接。

重点看DS18B20_Rst()的实现:

/** * @brief 复位DS18B20 */ void DS18B20_Rst(void) { DS18B20_SetDQMode_OUT(); /* 先将数据线置高电平“1” */ DQ_H; /* 延时(该时间要求的不是很严格,但是尽可能的短一点) */ Delay_us(1); /* 数据线拉到低电平“0”。 */ DQ_L; /* 延时750微秒(该时间的时间范围可以从480到960微秒) */ Delay_us(750); /* 数据线拉到高电平“1” */ DQ_H; /* 延时等待(如果初始化成功则在15到60微秒时间之内产生一个由 DS18B20所返回的低电平“0”。据该状态可以来确定它的存在, 但是应注意不能无限的进行等待,不然会使程序进入死循环, 所以要进行超时控制)。 */ /* 超时时间不要设置得太短(500us是足够的), 否则初始化失败,将获取不到环境温度 */ Delay_us(500); uint8_t dqStatus = GPIO_ReadInputDataBit(GPIODQ,PINDQ); /* 将数据线再次拉高到高电平“1”后结束。 */ DQ_H; }

注意看每一步的注释,交代了初始化过程中DQ的电平变化。

其中最后一个延时函数:

Delay_us(500);

尤为关键,如果这里的等待时间不足,将导致初始化失败,出现温度值不变的怪病。

DS18B20_SetDQMode_OUT()是把DQ对应的引脚设置为推挽输出模式:

/** * @brief 设置DQ对应的引脚位推挽输出模式 * @param 无 * @retval 无 */ static void DS18B20_SetDQMode_OUT() { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = PINDQ; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*设置引脚速率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIODQ, &GPIO_InitStructure); }

DQ_H()和DQ_L()分别是将DQ对应的引脚设置为高电平和低电平:

#define DQ_H GPIO_SetBits(GPIODQ,PINDQ) #define DQ_L GPIO_ResetBits(GPIODQ,PINDQ)

Delay_us()的实现如下:

/** * @brief 延时函数 * @param us_cnt 设定的延时微秒数 * @retval 无 */ void Delay_us(uint32_t us_cnt) { TIM3->CNT = us_cnt-1; TIM3->CR1 |= TIM_CR1_CEN; while((TIM3->SR & TIM_FLAG_Update)!=SET); TIM3->SR = (uint16_t)~TIM_FLAG_Update; TIM3->CR1 &= ~TIM_CR1_CEN; }

这个函数需要配置定时器以支持计时:

/** * @brief 配置延时定时器 * @param us_cnt 设定的延时微秒数 * @retval 无 */ void Delay_Timer_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct); TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Down; TIM_TimeBaseInitStruct.TIM_Period = 100-1; TIM_TimeBaseInitStruct.TIM_Prescaler = (84-1); TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct); while((TIM3->SR & TIM_FLAG_Update)!=SET); TIM3->SR = (uint16_t)~TIM_FLAG_Update; }

2、通过DQ读取一个字节

/** * @brief 通过DQ从DS18B20读取一个字节(Byte) * @param 无 * @retval 读取到的数据(uint8_t类型) */ uint8_t DS18B20_ReadByte(void) { uint8_t data = 0x00; for(int i=0;i>=1; /* 将数据线拉高“1” */ DQ_H; DS18B20_SetDQMode_IPU(); /* 延时5微秒 */ Delay_us(5); /* 读数据线的状态得到1个状态位,并进行数据处理 */ if(GPIO_ReadInputDataBit(GPIODQ,PINDQ)) data|=0x80; /* 延时60微秒 */ Delay_us(60); } return data; }

同样的,注意阅读注释中的描述。

DS18B20_SetDQMode_IPU()把DQ对应的引脚设置为输入模式:

/** * @brief 设置DQ对应的引脚为输入模式 * @param 无 * @retval 无 */ static void DS18B20_SetDQMode_IPU() { /*定义一个GPIO_InitTypeDef类型的结构体*/ GPIO_InitTypeDef GPIO_InitStructure; /*选择要控制的GPIO引脚*/ GPIO_InitStructure.GPIO_Pin = PINDQ; /*设置引脚模式为通用推挽输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /*调用库函数,初始化GPIO*/ GPIO_Init(GPIODQ, &GPIO_InitStructure); }

3、通过DQ发送一个字节

/** * @brief 通过DQ向DS18B20发送一个字节(Byte) * @param @arg data 待发送的1字节数据 * @retval 无 */ void DS18B20_WriteByte(uint8_t data) { for(int i=0;i>=1; } }

4、读取温度

/** * @brief 获取温度寄存器的值并转换为温度值返回 * @param 无 * @retval 无 */ float DS18B20_GetTemp(void) { DS18B20_Rst(); DS18B20_WriteByte(SKIPROMCOMMAND); DS18B20_WriteByte(CONVERTT); DS18B20_Rst(); DS18B20_WriteByte(SKIPROMCOMMAND); DS18B20_WriteByte(READSCRATCHPAD); uint8_t tempL = DS18B20_ReadByte(); uint8_t tempH = DS18B20_ReadByte(); /* 反码 */ if(tempH>0x7f) { tempL = ~tempL; tempH = ~tempH+1; } /* 计算温度值 */ float temp = ((tempH4))+ (float)(tempL&0x0f)*0.0625; return temp; }

其中的SKIPROMCOMMAND、CONVERTT、READSCRATCHPAD都是 ds18b20 的控制指令:

在这里插入图片描述 在这里插入图片描述

温度计算的部分需要注意一下:

/* 计算温度值 */ float temp = ((tempH4))+ (float)(tempL&0x0f)*0.0625;

数据手册的表述如下:

在这里插入图片描述

1、黑色-划掉的是多余的符号位 2、红色-对应着整数位 3、蓝色-下划线部分对应小数

首先,(高8位左移4位)或上(低八位右移4位),得到8位的整数部分:

((tempH4))

然后,低8位去掉左边4位:

(tempL&0x0f)

乘2^(-4)化成小数:

(float)(tempL&0x0f)*0.0625

整数部分和小数部分加起来得到温度值:

float temp = ((tempH4))+ (float)(tempL&0x0f)*0.0625;

好啦,ds18b20的驱动就介绍完了,源代码的链接放在下面:

基于stm32F1标准库开发的DS18B20驱动

timer.x提供时序控制支持 ds18b20.x是驱动内容,供以读取温度数据



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3