蓝桥杯_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倍?
解决小数存储问题:
传感器是电子元件,直接存储小数比较麻烦。通过放大16倍,可以把小数转换成整数存储。(比如0.625°C
放大后变成10
,因为0.625 × 16 = 10
)提高精度:
放大后可以保留更多有效数字。比如:
放大前精度是
0.0625°C
(因为1 ÷ 16 = 0.0625
)放大后直接用整数
1
表示0.0625°C
。
四 最后所实现的效果
温度部分,如图所示: