历史上的今天
返回首页

历史上的今天

今天是:2024年11月03日(星期日)

2021年11月03日 | stm32实用篇6:HAL库 DS18B20 驱动

2021-11-03 来源:eefocus

DS18B20是很常用的温度传感器,精度很高,测试结果如下:

整个驱动程序基本是根据时序写的。要注意的地方有两个:


1 读数据的时间 < 15us,这个值要实际测试,跟自己实现的延时函数效率有关,我使用的是10us,这个值如果设置的不合理,会直接导致整个数据读取出错;


2 建议使用DS18B20_GetTemp_SkipRom函数,不推荐使用设备号相关的那个读取函数,不太稳定;


 


HAL库驱动程序源码:


bsp_ds18b20.c


/**

  ******************************************************************************

  * @file            bsp_ds18b20.c

  * @author

  * @date

  * @version         v1.0

  * @note            DHT11 driver

  ******************************************************************************

  */

 

#include "bsp_ds18b20.h"

 

/**

  * @brief DS18B20 输出模式

  */

static void DS18B20_Mode_OUT_PP(void)

{

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Pin = BSP_DS18B20_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(BSP_DS18B20_PORT, &GPIO_InitStruct);

}

 

/**

  * @brief DS18B20 输入模式

  */

static void DS18B20_Mode_IN_NP(void)

{

GPIO_InitTypeDef GPIO_InitStruct;

GPIO_InitStruct.Pin = BSP_DS18B20_PIN;

GPIO_InitStruct.Mode = GPIO_MODE_INPUT;

GPIO_InitStruct.Pull = GPIO_NOPULL;

 

HAL_GPIO_Init(BSP_DS18B20_PORT, &GPIO_InitStruct);

}

 

/**

  * @brief 主机给从机发送复位脉冲

  */

static void DS18B20_Reset(void)

{

DS18B20_Mode_OUT_PP(); // 主机输出

DS18B20_OUT_0; // 主机至少产生 480us 的低电平复位信号 

bsp_delay_us(750);

DS18B20_OUT_1; // 主机在产生复位信号后,需将总线拉高 

// 从机接收到主机的复位信号后,会在 15 ~ 60 us 后给主机发一个存在脉冲

bsp_delay_us(15);

}

 

/**

  * @brief  检测从机给主机返回的存在脉冲

  * @return 0:成功 1:失败

*/

static uint8_t DS18B20_Presence(void)

{

uint8_t pulse_time = 0;

DS18B20_Mode_IN_NP(); // 主机设为输入 

// 等待存在脉冲的到来,存在脉冲为一个 60 ~ 240 us 的低电平信号 

// 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在 15 ~ 60 us 后给主机发一个存在脉冲

 

while (DS18B20_IN && (pulse_time < 100)) // 等待低电平响应脉冲

{

pulse_time++;

bsp_delay_us(1);

}

// 经过 100 us 后,存在脉冲都还没有到来

if (pulse_time >= 100)

{

return 1;

}

else

{

pulse_time = 0;

}

// 响应脉冲(低电平)到来,且存在的时间不能超过 240 us 

while(!(DS18B20_IN) && pulse_time < 240)

{

pulse_time++;

bsp_delay_us(1);

}

if(pulse_time >= 240)

{

return 1;

}

else

{

return 0;

}

}

 

 /**

   * @brief  DS18B20 初始化函数

   * @reurn  0:成功 1:失败

   */

uint8_t DS18B20_Init(void)

{

DS18B20_Mode_OUT_PP();

DS18B20_OUT_1;

DS18B20_Reset();

return DS18B20_Presence();

}

 

/**

  * @brief 从DS18B20读取一个bit

  */

static uint8_t DS18B20_ReadBit(void)

{

uint8_t dat;

DS18B20_Mode_OUT_PP(); // 读 0 和读 1 的时间至少要大于 60 us 

DS18B20_OUT_0; // 读时间的起始:必须由主机产生 > 1us < 15us 的低电平信号 

// 这个时间非常重要,设置为 < 15,需要多次尝试;如果设置不合理,数据会直接出错

// 参数建议设为 10 11 12,具体值要根据延时函数的执行效率测试

bsp_delay_us(10);

DS18B20_Mode_IN_NP(); // 设置成输入,释放总线,由外部上拉电阻将总线拉高 

if (DS18B20_IN == 1)

{

dat = 1;

}

else

{

dat = 0;

}

bsp_delay_us(45); // 这个延时参数参考时序图 

return dat;

}

 

/**

  * @brief 从 DS18B20 读一个字节,低位先行

  */

static uint8_t DS18B20_ReadByte(void)

{

uint8_t i, j, dat = 0;

for(i = 0; i < 8; i++) 

{

j = DS18B20_ReadBit();

dat = (dat) | (j << i);

}

return dat;

}

 

/**

  * @brief 写一个字节到 DS18B20,低位先行

  */

static void DS18B20_WriteByte(uint8_t dat)

{

uint8_t i, testb;

DS18B20_Mode_OUT_PP();

for( i = 0; i < 8; i++ )

{

testb = dat & 0x01;

 

dat = dat >> 1;

// 写 0 和写 1 的时间至少要大于60us 

if (testb) // 当前位写 1

{

DS18B20_OUT_0;

bsp_delay_us(5); // 拉低发送写时段信号

DS18B20_OUT_1; // 读取电平时间保持高电平

bsp_delay_us(65);

}

else // 当前位写 0

{

DS18B20_OUT_0; // 拉低发送写时段信号

bsp_delay_us(70); // 读取电平时间保持低电平

DS18B20_OUT_1;

bsp_delay_us(2); // 恢复时间

}

}

}

 

/**

  * @brief  跳过匹配 DS18B20 ROM

  */

static void DS18B20_SkipRom(void)

{

DS18B20_Reset();    

DS18B20_Presence();  

DS18B20_WriteByte(0XCC); /* 跳过 ROM */

}

 

/**

  * @brief  执行匹配 DS18B20 ROM

  */

static void DS18B20_MatchRom(void)

{

DS18B20_Reset();    

DS18B20_Presence();  

DS18B20_WriteByte(0X55); /* 匹配 ROM */

}

 

/**

* 存储的温度是16 位的带符号扩展的二进制补码形式

* 当工作在12位分辨率时,其中5个符号位,7个整数位,4个小数位

*

*         |---------整数----------|-----小数 分辨率 1/(2^4)=0.0625----|

* 低字节  | 2^3 | 2^2 | 2^1 | 2^0 | 2^(-1) | 2^(-2) | 2^(-3) | 2^(-4) |

*

*

*         |-----符号位:0->正  1->负-------|-----------整数-----------|

* 高字节  |  s  |  s  |  s  |  s  |    s   |   2^6  |   2^5  |   2^4  |

*

* 温度 = 符号位 + 整数 + 小数*0.0625

*/

 

/**

  * @brief  在跳过匹配 ROM 情况下获取 DS18B20 温度值 

  * @param  无

  * @retval 温度值

  */

float DS18B20_GetTemp_SkipRom(void)

{

uint8_t tpmsb, tplsb;

int16_t s_tem;

float f_tem;

DS18B20_SkipRom();

DS18B20_WriteByte(0X44); /* 开始转换 */

DS18B20_SkipRom();

  DS18B20_WriteByte(0XBE); /* 读温度值 */

tplsb = DS18B20_ReadByte();  

tpmsb = DS18B20_ReadByte(); 

s_tem = tpmsb << 8;

s_tem = s_tem | tplsb;

if(s_tem < 0) /* 负温度 */

{

f_tem = (~s_tem + 1) * 0.0625f;

}

else

{

f_tem = s_tem * 0.0625f;

}

return f_tem;

}

 

/**

  * @brief  在匹配 ROM 情况下获取 DS18B20 温度值 

  * @param  ds18b20_id:用于存放 DS18B20 序列号的数组的首地址

  */

void DS18B20_ReadId(uint8_t *ds18b20_id)

{

uint8_t uc;

DS18B20_WriteByte(0x33);       //读取序列号

for (uc = 0; uc < 8; uc++)

{

ds18b20_id[uc] = DS18B20_ReadByte();

}

}

 

/**

  * @brief  在匹配 ROM 情况下获取 DS18B20 温度值 

  * @param  ds18b20_id:存放 DS18B20 序列号的数组的首地址

  * @retval 温度值

  */

float DS18B20_GetTemp_MatchRom(uint8_t * ds18b20_id)

{

uint8_t tpmsb, tplsb, i;

int16_t s_tem;

float f_tem;

DS18B20_MatchRom();            /* 匹配ROM */

  for(i = 0;i < 8; i++)

{

DS18B20_WriteByte(ds18b20_id[i]);

}

DS18B20_WriteByte(0X44); /* 开始转换 */

 

DS18B20_MatchRom();            /* 匹配ROM */

for(i = 0; i < 8; i++)

{

DS18B20_WriteByte(ds18b20_id[i]);

}

DS18B20_WriteByte(0XBE); /* 读温度值 */

tplsb = DS18B20_ReadByte();  

tpmsb = DS18B20_ReadByte(); 

s_tem = tpmsb << 8;

s_tem = s_tem | tplsb;

if (s_tem < 0) /* 负温度 */

{

f_tem = (~s_tem + 1) * 0.0625f;

}

else

{

f_tem = s_tem * 0.0625f;

}

return f_tem;

}

 

// 测试程序

 

#if 0

 

uint8_t uc, ucDs18b20Id[8];

while (DS18B20_Init())

{

printf("rn no ds18b20 exit rn");

}

 

printf("rn ds18b20 exit rn");

 

DS18B20_ReadId(ucDs18b20Id);            // 读取 DS18B20 的序列号

printf("rnDS18B20的序列号是: 0x");

 

for (uc = 0; uc < 8; uc++)          // 打印 DS18B20 的序列号

{

printf("%.2x", ucDs18b20Id[uc]);

}

 

while (1)

{

printf("rn温度: %.1frn", DS18B20_GetTemp_SkipRom());

 

HAL_Delay(1000); /* 1s 读取一次温度值 */

}

 

#endif

 

bsp_ds18b20.h


/**

  ******************************************************************************

  * @file bsp_ds18b20.h

  * @author

  * @date

  * @version     v1.0

  * @note DS18B20 driver

  ******************************************************************************

  */

 

#ifndef __BSP_DS18B20_H

#define __BSP_DS18B20_H

 

#include "bsp_conf.h"

 

#define BSP_DS18B20_PORT             TEMP_SENSOR_GPIO_Port

#define BSP_DS18B20_PIN                TEMP_SENSOR_Pin

 

#define DS18B20_OUT_1                HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_SET)

#define DS18B20_OUT_0                HAL_GPIO_WritePin(BSP_DS18B20_PORT, BSP_DS18B20_PIN, GPIO_PIN_RESET)

 

#define DS18B20_IN         HAL_GPIO_ReadPin(BSP_DS18B20_PORT, BSP_DS18B20_PIN)

 

uint8_t DS18B20_Init(void);

void DS18B20_ReadId(uint8_t *ds18b20_id);

float DS18B20_GetTemp_SkipRom(void);

float DS18B20_GetTemp_MatchRom(uint8_t * ds18b20_id);

 

#endif /* __BSP_DS18B20_H */

推荐阅读

史海拾趣

Fedco_Batteries公司的发展小趣事

为了拓展国际市场,Fedco_Batteries积极寻求与国内外企业的合作。他们与多家知名企业建立了战略合作关系,共同研发新型电池技术,开拓新市场。同时,公司还积极参加国际展会和论坛,与国际同行交流学习,不断提升自身的竞争力。这种合作共赢的发展模式,让Fedco_Batteries在国际市场上取得了显著的成绩。

协顺电子(Finecables)公司的发展小趣事

近年来,随着电子行业技术的不断革新和市场环境的变化,协顺电子也面临着前所未有的挑战。为了应对这些挑战,公司开始进行转型升级。一方面,公司加大研发投入,推动产品创新和技术升级;另一方面,公司也积极探索新的业务领域和市场机会。同时,协顺电子还加强了与供应商和客户的合作关系,共同应对市场变化和挑战。这些努力使得公司在激烈的市场竞争中保持了良好的发展态势。

Altech公司的发展小趣事

在电子行业的发展历程中,Altech公司凭借其敏锐的市场洞察力,率先投入研发铝合金汽车线束导体。随着新能源汽车市场的崛起,对高效、轻量化的线束导体需求日益增长。Altech公司成功研发出铝合金汽车线束导体,不仅满足了市场对轻量化的需求,还提高了导电性能,为新能源汽车行业带来了革命性的变革。

CML公司的发展小趣事

随着电子行业的快速发展,CML意识到只有不断创新才能在激烈的市场竞争中立于不败之地。于是,公司加大了对研发的投入,积极引进先进的技术和设备,不断提升产品的性能和质量。经过多年的努力,CML成功开发出了一系列具有自主知识产权的半导体产品,不仅在国内市场占据了领先地位,还成功打入国际市场,赢得了全球客户的信赖和好评。

General Industrial Controls ( GIC )公司的发展小趣事
在必要时,可以增加额外的保护电路来提高系统的稳定性和可靠性。例如,在输入端增加过压保护电路,在输出端增加过流保护电路等。
DESIGNERSYSTEMS公司的发展小趣事

DESIGNERSYSTEMS公司的创始人在电子行业有着深厚的背景和独特的见解。他们发现市场上缺乏一种能够集成多种功能、高度定制化的电子设备设计解决方案。于是,他们创立了DESIGNERSYSTEMS,专注于提供从概念到原型再到量产的全方位设计服务。通过不断的技术创新和对市场需求的精准把握,DESIGNERSYSTEMS迅速在电子行业树立了自己的品牌形象,赢得了客户的信赖。

问答坊 | AI 解惑

15075018luerdu

老师,我做的是简易数字频率计的,输入信号要进行放大的 ,输入信号的频率是0.1HZ到1MHZ. 我想要一个放大器集成块的,要求的频率达到1MHZ以上的。 老师听说你有这种放大器呀,是不是呀? 老师,下面是我找到的一个高频放大器集成块,你帮我看看 ...…

查看全部问答>

冰天雪地,,着急,,哪个帮我看个经典的汇编程序

帮我写下程序流程   我的邮箱 puyiyue1980@126.com 人总有困难时候,,请高人帮我看下,,,…

查看全部问答>

同一个坑里的战友

前些天听说一个同事要辞职,还以为是开玩笑呢,今天确认了是真的!毕竟人还可以,平日关系也不错!心里有点不是滋味!嗨!!!…

查看全部问答>

Pads(Power PCB)与Protel 哪个好?

AD6(protel)与PADS基本功能比较一、 原理图部分1、 库⑴ DxDesigner 的原理图库与PCB的库是相互独立的,而且每个原理图符号库都是一个文件,很难实现统一管理;AD6可以使用集成库来统一管理,不仅是原理图符号库与PCB封装库,还能把混合电路仿真库 ...…

查看全部问答>

求128128液晶中文资料or驱动代码

求128128液晶中文资料或者驱动代码 358915781 zoujun224@qq.com…

查看全部问答>

高分悬赏求个单片机的问题

我想用单片机做个定时测温控制系统,请高手指导一下…

查看全部问答>

YAFFS能否移植到其他嵌入式操作系统?

YAFFS移植到嵌入式Linux的案例非常多,但移植到其他操作系统却从来没见过。 所以想问下各位大虾,YAFFS能否移植到其他嵌入式操作系统?比如Nucleus Plus?…

查看全部问答>

再问一个比较菜的问题

怎么样去理解内部电源层,和内部地层的意思? 如果一个双面板加了一个内部电源层或者地层是不是就变成多面板了?(意思就是内部电源层也是信号层呗,就是为了图个稳定加上的?) 指导一下.…

查看全部问答>

STM32调试,ram下正常,flash下错误

一个在中断中读写USART1的小程序。在不擦除flash下原有程序的情况下,在ram中调试,虽然NVIC中USART1已经enable,USART1寄存器已经有中断使能且收/发事件发生,但无法进入中断。并不是flash中有任意程序都会发生此问题,目前暂未找到规律。而 ...…

查看全部问答>

iPhone 风力发电机充电器?!我去!太拉风了简直……

本帖最后由 jameswangsynnex 于 2015-3-3 19:59 编辑 iPhone 实在太牛了个叉了!!用 iPhone 的童鞋不是整天喊没电吗?于是人们不断发明一些解决方案,这两款小型风力发电机就是专门为 iPhone 充电的。一起来看看有多拉风吧…… 第一款:直充小风 ...…

查看全部问答>