第十五届蓝桥杯单片机组——DS18B20模块(带可直接运行的示例代码)

本文详细介绍了DS18B20温度传感器的温度转换与读取步骤,包括官方提供的底层驱动代码,以及在实际应用中遇到的上电异常、无延时读取和中断影响等问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、DS18B20介绍

1.DS18B20的温度测量范围从-55℃到+125℃
2.分辨率在9位到12位之间,可以由用户自己进行设置。
3.在-10℃到+85℃范围内,其精确度可以达到±0.5℃。
4.DS18B20可以通过单个数据线(以及地线)进行通信,也就是one wire。

二、DS18B20的温度转换与读取流程

[1] DS18B20复位。
[2] 写入字节0xCC,跳过ROM指令。
[3] 写入字节0x44,开始温度转换。
[4] 因为温度转换需要时间,所以延时700~900ms。
[5] DS18B20复位。
[6] 写入字节0xCC,跳过ROM指令。
[7] 写入字节0xBE,读取高速暂存器。
[8] 读取暂存器的第0字节,即温度数据的LSB。
[9] 读取暂存器的第1字节,即温度数据的MSB。
[10] DS18B20复位。表示读取数据结束。

[11] 将LSB和MSB整合成为一个16位 数据。
[12] 判断读取结果的符号,进行正负温度的数据处理。

看到这是不是开始害怕了,不用怕,我教大家如何利用蓝桥杯官方提供的资料来完成上面的这些内容。大家需要理解的只有[11]和[12]这两个怎么实现就行。

三、利用手册完成DS18B20的读写

3.1 官方给的底层驱动代码

先看一下官方提供给我们的底层驱动代码中onewire中有哪些函数?

void Write_DS18B20(unsigned char dat);	// 写ds18b20
unsigned char Read_DS18B20(void);		// 读取ds18b20
bit init_ds18b20(void);					// 初始化ds18b20,也就是复位ds18b20

根据这三个函数,再结合官方给的DS18B20手册我们就可以实现代码的编写。

3.2 手册指定的数据流

第一步:初始化,直接调用函数init_ds18b20() 就可以了
第二步:ROM命令,直接跳过ROM指令,也就是发送0x44
第三步:发送要DS18B20干什么,比如让它温度转换或是读取温度。记住每次命令让它干什么之前都要重复前面两步,这是DS18B20的基本流程!!!!!!!!
在这里插入图片描述

3.3 结合ROM 命令流程图和功能命令流程图确认发送什么指令

3.3.1 实现第一个功能(开始温度转换)

记得3.2中所说的固定数据流吗?
初始化、ROM指令、功能指令
在这里插入图片描述在这里插入图片描述

根据ROM流程图和Function流程图,我们完成了第一个功能,开始温度转换

init_ds18b20();			// 初始化
Write_DS18B20(0xcc);	// ROM指令(跳过ROM指令)
Write_DS18B20(0x44);	// 功能指令(开始温度转换)

记住,开始温度转换后一定要延时700ms到900ms

3.3.2 第二个功能(读取温度)

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

init_ds18b20();			// 初始化
Write_DS18B20(0xcc);	// 跳过ROM
Write_DS18B20(0xbe);	// 读取温度
LSB=Read_DS18B20();		// 读取第一个字节,也就是温度数据的低8位
MSB=Read_DS18B20();		// 读取第二个字节,也就是温度数据的高8位
// 后面还有7个字节的数据,我们不需要,复位DS18B20(调用初始化函数就是复位DS18B20)
// init_ds18b20();			// 2024.4.12更新说明:小蜜蜂老师的这行代码是不必要的,甚至会导致你的ds18b20数据不稳定,跳动。

在这里插入图片描述

不同温度对应的不同的数字输出,可以看到高5位用来表示温度的正负!
在这里插入图片描述

3.3.3 数据处理(截取b站小蜜蜂老师的课件)

在这里插入图片描述

3.4 整体代码

float DS18B20_Get_Data()
{
	char LSB,MSB;
	int	 data;
	float temperature;
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	//Delay800ms();	// 可以去掉,也建议去掉,去掉后要确保不能太快的去读取ds18b20,会导致读取的数据不稳定
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	LSB=Read_DS18B20();
	MSB=Read_DS18B20();
	// init_ds18b20();			// 2024.4.12更新说明:小蜜蜂老师的这行代码是不必要的,甚至会导致你的ds18b20数据不稳定,跳动。
	
	data=MSB;				// 保存高8位数据到低8位
	data=(data<<8)|LSB;		// 将高8位的数据向左移8位,再将低8位放到data的低8位中
	
	if(data>0xf800)			// 温度小于0,因为data前5位表示温度的正负
							//所以我们就可以利用前5位是0还是1来判断读取的温度是负的还是正的
							//一般都是正的,所以我就不讲了。
	{
		
	}
	else			// 温度大于0
	{
		temperature=data*0.0625;
	}	
	return 	temperature;			
}

四、DS18B20温度读取异常

4.1 上电出现异常

这是由于DS18B20在上电复位时,温度寄存器中的值位0x0550,即85摄氏度。如果你读取温度有延迟800ms就不会出现这样的问题。

//在main函数初始化部分添加
while((unsigned char)DS18B20_Get_Data() == 85);	// 如果读取温度为85,那么一直卡在这里,除非读取到正常温度

4.2 读取温度没有延时

DS18B20的温度读取需要时间,这个时间是由于AD转换需要时间,所以开启温度转换后一定要延时一段时间。
2024.4.12更新说明:可以不要延时,但是不能读取频率过高。可以配合定时器中断,实现50ms读取一次,但不要放在定时器中断里面啊!

4.3 中断影响了onewire的时序

onewire对时序要求非常严格,所以当中断发生过于频繁或者中断中做的事情太多或者中断用了延时函数,这些都会导致ds18b20的数据异常!其实不止是中断,你代码编写的逻辑不好也会导致ds18b20读取数据异常…

解决办法:因为DS18B20的读取耗时较长,所以我们不能放在中断中解决中断对DS18B20的影响。最好的办法就是放在主函数中运行,然后利用中值滤波滤除异常数据,实践证明效果非常棒,几乎不可能出现数据异常!

五、示例代码

注意代码是通过串口查看读取的数据,请打开串口助手观测读取的数据。

onewire.h

#ifndef __ONEWIRE_H__
#define    __ONEWIRE_H__
#include "stc15f2k60s2.h"
sbit DQ=P1^4;
bit init_ds18b20(void);
unsigned char Read_DS18B20(void);
void Write_DS18B20(unsigned char dat);

#endif

onewire.c

/*	# 	单总线代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/

//
#include "onewire.h"

void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0;i<12;i++);
	}
}

//
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}

//
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;             // 初始化总线为高电平
  	Delay_OneWire(12);  
  	DQ = 0;             // 总线拉低总线480-960us产生复位脉冲
  	Delay_OneWire(80);
  	DQ = 1;             // 主机释放总线,模拟生成一个上升沿,15-60us    
  	Delay_OneWire(10); 
    initflag = DQ;      // ds18b20检测到上升沿后,产生一个60-240us的低电平信号,如果为低电平,说明设备准备就绪
  	Delay_OneWire(5);
  
  	return initflag;
}

main.c

#include "stc15f2k60s2.h"
#include "onewire.h"
#include "intrins.h"
#include <stdio.h>

typedef unsigned char   uint8;
typedef unsigned int    u16;
/**********矩阵键盘IO定义***********/
sbit R1=P3^3;
sbit R2=P3^2;
sbit R3=P3^1;
sbit R4=P3^0;

sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;
/**********矩阵键盘IO定义***********/

uint8 code t_display[]={                       //标准字库
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
    0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black  -     H    J    K    L    N    o   P    U     t    G    Q    r   M    y
    0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
    0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46};    //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1

uint8 display_buffer[]={16,16,16,16,16,16,16,16};

uint8 num=0;
unsigned int timerCount=0;

uint8 ledStatus=0;

void selectHC573(uint8 channel);
void initSystem();
void showBitSeg(uint8 pos,uint8 dat);
void Delay1ms(void);	//@12.000MHz
void scanSeg();
void scanKeyboard();

bit NorP=1;

void Delay700ms(void)	//@12.000MHz
{
    unsigned char data i, j, k;
	_nop_();
	_nop_();
	i = 40;
	j = 236;
	k = 16;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


float DS18B20_Get_Data()
{
    uint8 LSB=0x00,HSB=0x00;
    unsigned int buffer;
    float temperature=0;
    
    init_ds18b20();
    Write_DS18B20(0xcc);
    Write_DS18B20(0x44);
    
//    Delay700ms();       // 延时等待温度转换完成
    
    init_ds18b20();
    Write_DS18B20(0xcc);
    Write_DS18B20(0xbe);
    
    LSB=Read_DS18B20();
    HSB=Read_DS18B20();
    
    buffer=(HSB<<8)|LSB;
    
    
    temperature=buffer*0.0625;
    printf("%f\n",temperature);
    return temperature;
}

char putchar(char c)
{
    SBUF = c;
    while(!TI);
    TI = 0;
    return c;
}
void UartInit(void)		//9600bps@12.000MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	AUXR |= 0x01;		//串口1选择定时器2为波特率发生器
	AUXR &= 0xFB;		//定时器时钟12T模式
	T2L = 0xE6;			//设置定时初始值
	T2H = 0xFF;			//设置定时初始值
	AUXR |= 0x10;		//定时器2开始计时
}

int main()
{   
    
    uint8 t=0;
    float temp=0;
    initSystem();
    UartInit();	
    while((unsigned char)DS18B20_Get_Data()==85)
    while(1)
    {
        DS18B20_Get_Data();
        Delay700ms();
    }
}



void selectHC573(uint8 channel)
{
    P2&=0x1f; //清空P2
    switch(channel)
    {
        case 4:{P2|=(0x04<<5); break;}  // LED
        case 5:{P2|=(0x05<<5); break;}  // Beep、Relay、Motor
        case 6:{P2|=(0x06<<5); break;}  // 数码管位码
        case 7:{P2|=(0x07<<5); break;}  // 数码管段码
        default:{break;}                 // 关闭所有通道
    }
}

void initSystem()
{
    selectHC573(5);
    P0=0x0f;
    
    selectHC573(0);
    P0=0xff;    //因为下一步要操作LED,在开启LED的573前,应该把P0的数据清零
    
    selectHC573(4);
    P0=~0x00;   // 设置LED
    

}

void showBitSeg(uint8 pos,uint8 dat)
{
    selectHC573(7);
    P0=0xff;
    
    selectHC573(6);
    P0=(0x80>>pos); 
    
    selectHC573(7);
    P0=~dat;
}


void Delay1ms(void)	//@12.000MHz
{
	unsigned char data i, j;

	i = 12;
	j = 169;
	do
	{
		while (--j);
	} while (--i);
}


void scanSeg()
{
    uint8 i=0;
    for(i=0;i<8;i++)
    {
        showBitSeg(i,t_display[ display_buffer[i] ]);
        Delay1ms();
    }
}

void Delay20ms(void)	//@12.000MHz
{
	unsigned char data i, j;

	i = 234;
	j = 115;
	do
	{
		while (--j);
	} while (--i);
}


void scanKeyboard()
{
    R1=R2=R3=R4=1;
    C1=0; C2=C3=C4=1;
    if(R1==0)
    {
        Delay20ms();    // 软件消抖  
        if(R1==0)
        {
//            while(R1==0)
            {
                num=0;
            }
        }
    }
    
    R1=R2=R3=R4=1;
    C3=0; C2=C1=C4=1;
    if(R2==0)
    {
        Delay20ms();    // 软件消抖  
        if(R2==0)
        {
//            while(R1==0)
            {
                num=13;
            }
        }
    }
    
    
}
### 蓝桥杯 DS18B20 温度传感器编程题解决方案 #### 题目背景介绍 蓝桥杯竞赛中的DS18B20温度传感器题目通常涉及读取并处理来自该传感器的数据。这类题目旨在考察参赛者对于硬件接口的理解以及数据采集和处理的能力。 #### 硬件连接说明 为了实现对DS18B20温度传感器的操作,需要将其正确地连接到微控制器上。一般情况下,这涉及到三根线:电源(VCC),接地(GND) 和 数据(DQ)[^1]。具体接法如下: - VCC 连接到单片机的VCC引脚; - GND 接至单片机的地线端口; - DQ 则通过一个4.7kΩ左右大小的上拉电阻连回到VCC,并且此引脚也应接入单片机的一个I/O口中用于通信。 #### 初始化配置 初始化过程中要设置好相应的寄存器参数来准备后续操作。这里给出一段基于C语言环境下的Arduino平台上的简单示例代码来进行初始化工作[^2]: ```c++ #include <OneWire.h> #include <DallasTemperature.h> // Data wire is plugged into pin 2 on the Arduino #define ONE_WIRE_BUS 2 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); ``` 这段程序定义了一个`OneWire`对象实例化为`oneWire`, 并指定了与之相连的数据总线编号;接着创建了另一个名为`sensors`的对象用来管理所有的温度传感设备。 #### 获取温度值 当完成上述准备工作之后就可以调用相应的方法获取当前测量得到的摄氏度数值了。下面展示如何请求一次新的温度转换过程并且读回结果: ```c++ void loop() { // Call sensors.requestTemperatures() to issue a global temperature request to all devices on bus float temperatureC = sensors.getTempCByIndex(0); // Get temperature of first device found Serial.print("Current temperature: "); Serial.println(temperatureC); } ``` 以上循环函数会不断打印出由第一个检测到的DS18B20所报告出来的最新室温信息给串行监视器查看[^3]。 #### 错误处理机制 考虑到实际应用环境中可能出现的各种异常情况,在编写此类程序时还应该加入必要的错误捕捉逻辑以提高系统的健壮性和可靠性。比如可以增加超时判断、重试次数限制等功能模块确保即使遇到突发状况也能平稳运行而不至于崩溃退出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值