当前位置: 首页 > news >正文

蓝桥杯_DS18B20温度传感器

目录

前言

一关于DS18B20的介绍

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

三 利用官方提供的底层驱动代码进行代码编写

onewire.c

比赛提供的文件代码:

在比赛文件的最后,要添加的代码:

大家可能产生的疑问:

(1) 为什么是返回unsigned int值?

(2) 为什么要把读到的数据先给low,再给high?

 (3) 为什么返回值,要将高八位向左移8位?

onewire.h

main.c

 这里要特别说明一个部分:

 (1)为什么这里需要将读取的温度数据要除以16?

 (2)为什么要放大16倍?

四 最后所实现的效果


前言

关于DS18B20,他是一个CMOS器件,也就证明了他是一个低功耗的设备,在正式进入这个模块学习之前,我们要知道一个课外小知识:

VDD和VCC有什么区别?

答:VDD强调用于低功耗及多种电压,VCC通常表示5v,但是他们俩并没有本质的区别,只是使用习惯上有不同而已,大家不必纠结这两个符号的区别

一关于DS18B20的介绍

我会介绍相关比较重要的点,具体的功能和器件请搜寻其他博主的博客,自行查阅

1.DS18B20的温度测量范围从-55℃到+125℃
2.分辨率在9位到12位之间,可以由用户自己进行设置。
3.在-10℃到+85℃范围内,其精确度可以达到±0.5℃。(一般来说仅需配置大于0的即可)
4.DS18B20可以通过单个数据线(以及地线)进行通信,也就是onewire。(比赛时,官方会提供的文件就是onewire)

在这里要介绍一下onewire,DS18B20 温度传感器只需要用一根数据线(和一根地线)就能和主控设备(比如单片机)通信,这种通信方式叫做「单总线」(OneWire)。这里的onewire和后续要学的IIC不一样,是另一种更简单的通信协议

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

由于比赛的时候,官方会给提供onewire相关的底层驱动代码,所以我会根据比赛所提供的材料进行代码教学,并不会涉及太多底层的讲解,只要大家缕清相关思路,记住一些特殊的值(例如:0xcc、0x44等对应的功能),相信大家肯定是可以编写出来这个温度测量的功能的

[1] 初始化总线
[2] 写入字节0xcc,跳过rom。
[3] 写入字节0x44,进行温度转换。
[4] 初始化总线
[5] 写入字节0xcc,跳过rom。
[6] 写入字节0xbe,读取高速暂存器。(将后面的high和low的值存放到这里面)
[7] 读取暂存器的第0字节,即温度数据的LSB(low)。
[8] 读取暂存器的第1字节,即温度数据的MSB(high)。
[9] 返回high+low的值

三 利用官方提供的底层驱动代码进行代码编写

我们先看一下 官方给的底层驱动代码的相关函数

void Write_DS18B20(unsigned char dat);	// 写ds18b20
unsigned char Read_DS18B20(void);		// 读取ds18b20
bit init_ds18b20(void);					// 初始化ds18b20,也就是复位ds18b20
void Delay_OneWire(unsigned int t);     //延时函数

我们只需要结合上面这四个函数和第二部分(DS18B20的温度转换与读取流程的九个步骤),就可以实现测量温度啦,请大家耐心看下去,并理解每一句话。

onewire.c

首先,我们需要将比赛所提供的onewire.c文件插入到项目当中,我们所要编写的测量温度的函数

rd_temperature是要写在官方提供的文件最后面的。

比赛提供的文件代码:

/*
  程序说明: 单总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机
  日    期: 2011-8-9
*/
#include "onewire.h"
#include <STC15F2K60S2.H>


//单总线延时函数
void Delay_OneWire(unsigned int t)
{
  while(t--);
}

//DS18B20芯片初始化
bit Init_DS18B20(void)
{
	bit initflag = 0;
	DQ = 1;
	Delay_OneWire(120);
	DQ = 0;
	Delay_OneWire(800); 
	DQ = 1;
	Delay_OneWire(100); 
	initflag = DQ;    
	Delay_OneWire(50);
  
	return initflag;
}

//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(50);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(50);
}

//从DS18B20读取一个字节
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(50);
	}
	return dat;
}

在比赛文件的最后,要添加的代码:

大家一定要注意,初始化ds18b20,初始化了两次,如果要让DS18B20干什么,例如让它进行温度转换或者读取暂存器的数值。记住!!!每次让它干什么之前,都要重复前面两步(初始化和跳过rom),这是他的的基本流程!!!

//读取温度函数,完成温度转换,并且将温度转换之后的温度数据
//返回值是温度的高低八位
unsigned int rd_temperature(void)
{
	
	unsigned char low,high;//温度返回值的高低八位
	
	init_ds18b20();
	Write_DS18B20(0xcc);//跳过rom
	Write_DS18B20(0x44);//进行温度转化
	
	init_ds18b20();
	Write_DS18B20(0xcc);//跳过rom
	Write_DS18B20(0xbe);//读取温度
	
	
	low = Read_DS18B20();
	high = Read_DS18B20();
	
	return (high << 8)+low;//16位,所以是unsigned_int
	
}

大家可能产生的疑问:

(1) 为什么是返回unsigned int值?

当DS18B20读取温度时,它会返回两个字节的数据,也就是16位。是由高八位和低八位组成,高八位是整数部分,低八位是小数部分

(2) 为什么要把读到的数据先给low,再给high?

因为ds18b20中,是先读低位再读高位

 (3) 为什么返回值,要将高八位向左移8位?

high左移8位是为了正确对齐两个字节的二进制位,确保高字节占据16位整数的高8位,低字节占据低8位。这是DS18B20数据格式的强制要求

onewire.h

只需要将在.c文件中的函数名字全部复制到.h之中即可

#ifndef __ONEWIRE_H
#define __ONEWIRE_H
#include <STC15F2K60S2.H>

sbit DQ = P1^4;  //单总线接口

void Delay_OneWire(unsigned int t);  //STC89C52RC
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);
unsigned int rd_temperature(void);

#endif

main.c

在该函数中,也是建立在这个专栏前面的模板,大家在main.c文件中,看不懂的话,请移步到我前面有关蓝桥杯的相关文件中慢慢观看,我把代码复制到这里,大家可以复制运行试一试,特别说明:我最后实现的效果是利用按按键的次数,改变用数码管显示温度、时钟和计数器

#include <STC15F2K60S2.H>
#include "Timer.h"
#include "dsp_init.h"
#include "dsp_seg.h"
#include "stdio.h"
#include "key.h"
#include "ds1302.h"
#include "dsp_led.h"
#include "onewire.h"



//函数声明,三个东西主体循环
void Key_Process(void);		//按键处理,底层的数据
void Seg_Process(void);		//显示处理,显示信息的生成
void Led_Process(void);		//LED状态信息


//全局变量
//显示专用
unsigned char seg_buf[8];		//放置字符串转换后的段码到数组
unsigned char seg_string[10];	//放置字符串
unsigned char pos = 0;	//中断显示专用


//按键专用
unsigned char Key_Val;	//按键的数值存储变量
unsigned char Key_Down;	
unsigned char Key_Old;


//按键和显示函数减速专用,基本不变,我不写试一下
unsigned char Key_Slow_Down;
unsigned char Seg_Slow_Down;


//可能会改变
//unsigned_char Seg_Show_Num;	//准备显示出来的数值

//灯的函数
unsigned char ucled;

//运行时间
unsigned int ms_count = 0;
unsigned int s_count = 0;

//状态
unsigned char Running_State = 0;

//DS1302专用
unsigned char ucRTC[3] = {22,10,32};

void main()
{
	Cls_Peripheral();		//清除外设
	Timer1Init();		//定时器1,1ms进入一次
	Set_RTC(ucRTC);//设置初始化12.59.55
	init_ds18b20();
	
	while(1)
	{
		Key_Process();		//按键处理,底层的数据
		Seg_Process();		//显示处理,显示信息的生成
		Led_Process();

	}

}


//定时器初始化
//-----------------------------------------------

/* Timer1_interrupt routine */
void tm1_isr() interrupt 3 
{
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
	
	if(++ms_count == 1000)//记录运行时间
	{
		s_count++;
		ms_count = 0;
	}
	
	
  Seg_Disp( seg_buf,pos);//数码管显示
	if(++pos == 8)
	{
		pos = 0;
	}
	
	Led_Disp(ucled);//灯切换亮

	
}


//key_process的函数内部
void Key_Process(void)		//按键处理,底层的数据
{
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;
	
	//读取按键按下的编号数值
	Key_Val = Key_Read();	
	
	Key_Down = Key_Val & (Key_Old ^ Key_Val);
	Key_Old = Key_Val;
	//以上这五行,就是检验下降沿
	
	
	//根据代码改变
	if(Key_Down)	//如果捕捉到下降沿跳变
	{
		if(++Running_State == 3) Running_State = 0;//保证Running_State在01翻滚
	}
	
}


//seg_process的函数内部
void Seg_Process(void)		//显示处理,显示信息的生成
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	

	switch(Running_State)
	{
		case 0:
		sprintf(seg_string,"    %04.2f",rd_temperature() / 16.0);
		break;		
		
		case 1:
		Read_RTC(ucRTC);
		sprintf(seg_string,"%2d-%2d-%2d",(unsigned int)ucRTC[0],(unsigned int)ucRTC[1],(unsigned int)ucRTC[2]);
		break;
		case 2:		
		sprintf(seg_string,"-----%03d",(unsigned int)s_count);
		break;
	}
	
	
	
	
	//永远不变
  Seg_Tran(seg_string, seg_buf);

}


//Led_Process的函数内部
void Led_Process(void)		
{
	switch(Running_State)
	{
		case 0:
			ucled = 0x03;
 			break;
		case 1:		
			ucled = 0x0c;
			break;
		case 2:		
			ucled = 0x30;
			break;
	}
	
	
	
}

 这里要特别说明一个部分:

sprintf(seg_string,"    %04.2f",rd_temperature() / 16.0);
 (1)为什么这里需要将读取的温度数据要除以16?

假设温度传感器(比如DS18B20)的测量范围是 -55°C 到 +125°C,但它内部存储温度数据时,把温度值放大了16倍,相当于把温度的小数部分用整数来记录。

 (2)为什么要放大16倍?
  1. 解决小数存储问题
    传感器是电子元件,直接存储小数比较麻烦。通过放大16倍,可以把小数转换成整数存储。(比如 0.625°C 放大后变成 10,因为 0.625 × 16 = 10

  2. 提高精度
    放大后可以保留更多有效数字。比如:

    • 放大前精度是 0.0625°C(因为 1 ÷ 16 = 0.0625

    • 放大后直接用整数 1 表示 0.0625°C

四 最后所实现的效果

温度部分,如图所示:

http://www.dtcms.com/a/99895.html

相关文章:

  • 【Java】Java核心知识点与相应面试技巧(六)——类与对象(一)
  • 什么是CMS?常用CMS有哪些?
  • Oracle数据库数据编程SQL<2.3 DML增、删、改及merge into>
  • 【学Rust写CAD】15 定点数实现(fixed.rs)
  • CSS中的em,rem,vm,vh详解
  • PipeWire 音频设计与实现分析一——介绍
  • C# 字符串(String)
  • 前端路由守卫与后端权限验证,仅使用路由守卫是否安全?
  • docker日志大小和保存管理
  • 常用的排序算法
  • 浅析Android Jetpack ACC之ViewModel
  • vector之内存分配详解
  • 23 种设计模式中的迭代器模式
  • Three.js 快速入门教程【十九】CSS2DRenderer(CSS2D渲染器)介绍,实现场景中物体或设备标注标签信息
  • QML中刷新图片的三种方法对比分析
  • [ComfyUI] 如何升级自定义节点(Custom Nodes)
  • 计算机网络和因特网
  • AGI 的概念、意义与未来展望
  • 【AI论文】挑战推理的边界:大型语言模型的数学基准测试
  • Keepass恢复明文主密码漏洞(CVE-2023-3278)复现与hashcat爆破学习
  • Array数组常用方法总结(javascript版)
  • SpringBoot的自动装配原理
  • Redis-常用命令
  • Spring 过滤器(Filter)和过滤器链(Filter Chain)完整示例,包含多个过滤器和Filter 生命周期
  • 简单介绍一下Unity中的material和sharedMaterial
  • PipeWire 音频设计与实现分析三——日志子系统
  • vxe-table 设置单元格可编辑无效问题解决
  • 网络传输优化之多路复用与解复用
  • 流动的梦境:GPT-4o 的自回归图像生成深度解析
  • 聚焦应用常用功能,提升用户体验与分发效率