目录
一、相关知识
1.关于DS18B20(这里只是简述,更详细专业的介绍请看芯片手册)
2.DS18B20的数据处理
3.DS18B20的分辨率
4.注意事项
5.整数、一位小数、两位小数、三位小数、四位小数的实现
①整数
②一位小数
③两位小数
④三位小数
⑤四位小数
6.DS18B20的温度转换与读取流程(在使用时直接套用该框架)
二、所要实现的功能
三、代码实现
1.参数定义
2.138译码器通道选择函数
3.初始化系统
4.延时函数
5.单个数码管显示函数
6.数码管动态显示
7.新延时函数
8.读DS18B20数据
9.完整代码
四、运行图片
关于共阳数码管等部分已在之前的文章中介绍过了,此处不再介绍了。
需要用到的芯片手册和驱动文件都已上传资源可以自行免费下载使用!
【免费】蓝桥杯最新驱动文件包括onewire、iic、ds1302资源-CSDN文库
【免费】蓝桥杯单片机开发及应用的中英两版芯片手册资源-CSDN文库
一、相关知识
1.关于DS18B20(这里只是简述,更详细专业的介绍请看芯片手册)
DS18B20是测温范围在-55℃~+125℃的可编程分辨率的单总线数字温度计。(可编程分辨率,也就是说在软件编程时可以实现读取温度保留整数、一位小数、两位小数抑或是三位四位小数。单总线,也就是说DS18B20是连在单根总线上的,需要使用到提供的onewire库。如何操作将在后文中说到)
2.DS18B20的数据处理
DS18B20是以16位带符号位扩展的二进制补码形式读出,高五位为符号位(全部为0就是整数,全部为1就是负数),中间7位为整数部分,低四位为小数部分。以+85℃为例按照高中低位断开:0x0550 = 00000 (+)1010101(85) 0000(.0)。如下图所示,
3.DS18B20的分辨率
DS18B20的分辨率为0.0625,那么为什么是0.0625呢? 这就对应了上面说的最多到小数点后四位,在二进制转为十进制中就是2^(-4)=1/16=0.0625。
当读出的数据为正温度时,将LSB和MSB(低八位和高八位)整合成16位数,直接乘以0.0625即可。(乘以0.0625=1/16就相当于把二进制的数右移四位去掉低四位表示的小数部分)
当读出的数据为负温度时,需要将LSB和MSB整合成的16位整数,取反后再加1,再乘以0.0625。(所涉及到的就是数电的二进制正负问题的知识)
4.注意事项
①使用DS18B20需要用到onewire的库,比赛时会提供onewire.c和onewire.h。
②非常重要的一点!!!就是组委会可能会在提供的文件中动一些手脚,以增加难度,这时读取的温度要么刷新很慢要么读出来的数就是错的。那么我们在使用之前就要认真分辨,哪里动了手脚,我们再改回来(onewire、iic、ds1302文件都已上传资源,需要的可自行下载);
③比赛时会提供onewire的库,但是有时候提供的是12T的,而蓝桥杯比赛的板子是1T的。可以把onewire文件中的延时都扩大10倍或12倍。至于这个倍数看情况,自己可以多试几个倍数,哪个倍数读取温度时更好用就用哪个,没有绝对情况。
5.整数、一位小数、两位小数、三位小数、四位小数的实现
保留三位、四位小数基本上不会出现,常见的就是整数、一位小数、两位小数。
①整数
直接乘以分辨率0.0625把小数部分去掉。
temp *= 0.0625;
②一位小数
相当于是乘以0.0625之后再乘以10,把整数扩大10倍(乘的数越大,右移的越少,这里就是相当于给小数留一个位置),然后对于( LSB & 0x0f )*0.625,相当于加上低八位中的低四位的第一位,那么就相当于是保留了一位小数。
temp *= 0.625;
temp += ( LSB & 0x0f ) * 0.625;
③两位小数
相当于是乘以0.0625之后再乘以100,把整数扩大100倍,然后对于( LSB & 0x0f )*6.25,相当于加上低八位中的低四位的前两位,那么就相当于是保留了两位小数。
temp *= 6.25;
temp += ( LSB & 0x0f ) * 6.25;
④三位小数
相当于是乘以0.0625之后再乘以1000,把整数扩大1000倍,然后对于( LSB & 0x0f )*62.5,相当于加上低八位中的低四位的前三位,那么就相当于是保留了三位小数。
temp *= 62.5;
temp += ( LSB & 0x0f ) * 62.5;
⑤四位小数
相当于是乘以0.0625之后再乘以10000,把整数扩大10000倍,然后对于( LSB & 0x0f )*625,相当于加上低八位中的低四位,那么就相当于是保留了四位小数。
temp *= 625;
temp += ( LSB & 0x0f ) * 625;
6.DS18B20的温度转换与读取流程(在使用时直接套用该框架)
Ⅰ.DS18B20复位;
Ⅱ.写入字节0xcc,跳过ROM指令;(一般的单总线上可以接很多设备,每个DS18B20都有一个独特的64位序列号以示区分,而蓝桥杯单片机上只有一个DS18B20,因此可以直接跳过ROM的匹配ID)
Ⅲ.写入字节0x44,开始温度转换;
Ⅳ.延时一段时间;
Ⅴ.DS18B20再次复位;
Ⅵ.写入字节0xcc,跳过ROM指令;
Ⅶ.写入字节0xbe,读取高速暂存器;
Ⅷ.读取暂存器的第0字节,即温度数据的LSB(低八位);
Ⅸ.读取暂存器的第1字节,即温度数据的MSB(高八位);
Ⅹ.将LSB和MSB整合成为一个16位的二进制数;
Ⅺ.判断读取数据的正负号,进行正负温度处理。
代码示例:
unsigned char LSB,MSB; // 定义低八位和高八位
// 复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0x44,开始转化温度
Write_DS18B20( 0x44 );
// 延时一段时间
Delay_New( 100 );
// 再复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0xbe,读取高速暂存器
Write_DS18B20( 0xbe );
// 读取数据,先读取低八位再读取高八位,因为低八位在第0字节,高八位在第1字节
LSB = Read_DS18B20();
MSB = Read_DS18B20();
temp = MSB; // 先存高八位
temp <<= 8; // 高八位左移八位,给低八位留位置
temp = temp | LSB; // 或上低八位,相当于是传入低八位数据,形成一个16位的数据
// 判断为正数还是负数
if( ( temp & 0xf800 ) == 0x0000 ) // 正数
{
// 判断后看要求是保留整数还是一位小数、两位小数、三位小数、四位小数
}
二、所要实现的功能
使用DS18B20读取温度。具体操作为用手捏住DS18B20使温度升高再放开使温度降低,重复操作,使得DS18B20读取温度正确、灵敏。读取的温度示数显示在数码管上,分五种情况为:保留至整数、一位小数、两位小数、三位小数、四位小数。
三、代码实现
1.参数定义
#include <STC15F2K60S2.H>
#include "Delay_ms.h"
#include "onewire.h"
unsigned int temp = 0; // 记录温度
// 不加“code”会影响单片机的运行速度,会改变RAM,加了code之后内容就固定不变了,不会多占用RAM空间
unsigned char code SMG_Duanma[19] =
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f,0x00
}; // 0~F - . 全部
// 带小数点的数
unsigned char code SMG_Dian[10] =
{
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10
}; // 0.~9.
2.138译码器通道选择函数
// 通道选择函数
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
3.初始化系统
// 初始化系统
void System_Init(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
// 关闭通道选择
HC138_Init( 0 );
}
4.延时函数
void Delay_tms( unsigned int t )
{
int i;
while( t-- )
{
for( i=115 ; i>0 ; i-- )
{}
}
}
5.单个数码管显示函数
// 数码管显示
void SMG_Light( unsigned char pos , unsigned char dat )
{
HC138_Init( 6 );
P0 = 0x01 << pos;
HC138_Init( 7 );
P0 = dat;
}
6.数码管动态显示
// 数码管动态显示
void SMG_Show(void)
{
// // 显示整数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 4 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 5 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// // 显示一位小数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 4 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Dian[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// 显示两位小数
SMG_Light( 2 , 0xff );
Delay_tms( 10 );
SMG_Light( 3 , 0xff );
Delay_tms( 10 );
SMG_Light( 4 , SMG_Duanma[ temp / 1000 ] );
Delay_tms( 10 );
SMG_Light( 5 , SMG_Dian[ temp / 100 % 10 ] );
Delay_tms( 10 );
SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
Delay_tms( 10 );
SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
Delay_tms( 10 );
// // 显示三位小数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , SMG_Duanma[ temp / 10000 ] );
// Delay_tms( 10 );
// SMG_Light( 4 , SMG_Dian[ temp / 1000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// // 显示四位小数
// SMG_Light( 2 , SMG_Duanma[ temp / 100000 ] );
// Delay_tms( 10 );
// SMG_Light( 3 , SMG_Dian[ temp / 10000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 4 , SMG_Duanma[ temp / 1000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
}
7.新延时函数
用于DS18B20的延时,其中包含数码管动态显示函数,因为如果不包含,那么在每一次到DS18B20延时的时候数码管就是呈现闪烁状态的,这种情况不允许。
// 延时函数
void Delay_New( unsigned int t )
{
while( t-- )
{
SMG_Show(); // 必须这样写,把数码管显示放在延时函数中,避免温度读取过程中影响数码管显示
}
}
8.读DS18B20数据
// 读DS18B20数据
void Read_Temp(void)
{
unsigned char LSB,MSB; // 定义低八位和高八位
// 复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0x44,开始转化温度
Write_DS18B20( 0x44 );
// 延时一段时间
Delay_New( 100 );
// 再复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0xbe,读取高速暂存器
Write_DS18B20( 0xbe );
// 读取数据,先读取低八位再读取高八位,因为低八位在第0字节,高八位在第1字节
LSB = Read_DS18B20();
MSB = Read_DS18B20();
temp = MSB; // 先存高八位
temp <<= 8; // 高八位左移八位,给低八位留位置
temp = temp | LSB; // 或上低八位,相当于是传入低八位数据,形成一个16位的数据
// 判断为正数还是负数
if( ( temp & 0xf800 ) == 0x0000 ) // 正数
{
// 整数
// temp *= 0.0625;
// 一位小数
// temp *= 0.625;
// temp += ( LSB & 0x0f ) * 0.625;
// 两位小数
temp *= 6.25;
temp += ( LSB & 0x0f ) * 6.25;
// 三位小数
// temp *= 62.5;
// temp += ( LSB & 0x0f ) * 62.5;
// 四位小数
// temp *= 625;
// temp += ( LSB & 0x0f ) * 625;
}
}
9.完整代码
#include <STC15F2K60S2.H>
#include "Delay_ms.h"
#include "onewire.h"
unsigned int temp = 0; // 记录温度
// 不加“code”会影响单片机的运行速度,会改变RAM,加了code之后内容就固定不变了,不会多占用RAM空间
unsigned char code SMG_Duanma[19] =
{
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f,0x00
}; // 0~F - . 全部
// 带小数点的数
unsigned char code SMG_Dian[10] =
{
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10
}; // 0.~9.
// 通道选择函数
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
// 初始化系统
void System_Init(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
// 关闭通道选择
HC138_Init( 0 );
}
// 数码管显示
void SMG_Light( unsigned char pos , unsigned char dat )
{
HC138_Init( 6 );
P0 = 0x01 << pos;
HC138_Init( 7 );
P0 = dat;
}
// 数码管动态显示
void SMG_Show(void)
{
// // 显示整数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 4 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 5 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// // 显示一位小数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 4 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Dian[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// 显示两位小数
SMG_Light( 2 , 0xff );
Delay_tms( 10 );
SMG_Light( 3 , 0xff );
Delay_tms( 10 );
SMG_Light( 4 , SMG_Duanma[ temp / 1000 ] );
Delay_tms( 10 );
SMG_Light( 5 , SMG_Dian[ temp / 100 % 10 ] );
Delay_tms( 10 );
SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
Delay_tms( 10 );
SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
Delay_tms( 10 );
// // 显示三位小数
// SMG_Light( 2 , 0xff );
// Delay_tms( 10 );
// SMG_Light( 3 , SMG_Duanma[ temp / 10000 ] );
// Delay_tms( 10 );
// SMG_Light( 4 , SMG_Dian[ temp / 1000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
// // 显示四位小数
// SMG_Light( 2 , SMG_Duanma[ temp / 100000 ] );
// Delay_tms( 10 );
// SMG_Light( 3 , SMG_Dian[ temp / 10000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 4 , SMG_Duanma[ temp / 1000 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 5 , SMG_Duanma[ temp / 100 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 6 , SMG_Duanma[ temp / 10 % 10 ] );
// Delay_tms( 10 );
// SMG_Light( 7 , SMG_Duanma[ temp % 10 ] );
// Delay_tms( 10 );
}
// 延时函数
void Delay_New( unsigned int t )
{
while( t-- )
{
SMG_Show(); // 必须这样写,把数码管显示放在延时函数中,避免温度读取过程中影响数码管显示
}
}
// 读DS18B20数据
void Read_Temp(void)
{
unsigned char LSB,MSB; // 定义低八位和高八位
// 复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0x44,开始转化温度
Write_DS18B20( 0x44 );
// 延时一段时间
Delay_New( 100 );
// 再复位
init_ds18b20();
// 写入0xcc,跳过ROM指令
Write_DS18B20( 0xcc );
// 写入0xbe,读取高速暂存器
Write_DS18B20( 0xbe );
// 读取数据,先读取低八位再读取高八位,因为低八位在第0字节,高八位在第1字节
LSB = Read_DS18B20();
MSB = Read_DS18B20();
temp = MSB; // 先存高八位
temp <<= 8; // 高八位左移八位,给低八位留位置
temp = temp | LSB; // 或上低八位,相当于是传入低八位数据,形成一个16位的数据
// 判断为正数还是负数
if( ( temp & 0xf800 ) == 0x0000 ) // 正数
{
// 整数
// temp *= 0.0625;
// 一位小数
// temp *= 0.625;
// temp += ( LSB & 0x0f ) * 0.625;
// 两位小数
temp *= 6.25;
temp += ( LSB & 0x0f ) * 6.25;
// 三位小数
// temp *= 62.5;
// temp += ( LSB & 0x0f ) * 62.5;
// 四位小数
// temp *= 625;
// temp += ( LSB & 0x0f ) * 625;
}
}
int main(void)
{
System_Init(); // 系统初始化
while(1)
{
Read_Temp();
SMG_Show();
}
}
四、运行图片