蓝桥杯单片机比赛系列4温度传感器DS18B20
- 温度传感器DS18B20原理
- 相关电路
- onewire总线
- 几个需要知道的暂存器和命令
- 代码解释
-
- 实现代码
温度传感器DS18B20原理
相关电路
DS18B20遵循onewire总线协议,只需一根线即可与单片机进行通讯。
onewire总线
onewire总线具体概念等这里不展开叙述,只讲比赛相关的内容。
竞赛中资源包里有头文件onewire.h和源文件oneire.c。只需进行修改即可操作DS18B20传输温度数据。
几个需要知道的暂存器和命令
DS18B20暂存器:
名称 | 说明 |
---|
温度值低八位 | b7-b4四位整数,b3-b0四位小数 |
温度值高八位 | b15-b12四位符号位,b11-b8四位整数,符号位都为1表示零下,0表示零上 |
DS18B20暂存器:
命令 | 说明 |
---|
0xcc | 跳过ROM,寻址所有单线上连接的多个DS18B20 |
0x44 | 启动温度转换,转换结果存放在暂存器0-1个字节,也就是温度值高八位,温度值低八位 |
0xbe | 读取暂存器0-8字节 |
代码解释
修改代码
以第十届(2019)的赛点资源包为例进行说明,之后的赛点资源包大概率不会变,不必过于担心。
因为赛点资源包给的是51的通信程序,而比赛用的是15单片机,在相同晶振条件下15单片机的速度是51单片机的8-12倍,所以将oneire.c里面的延迟函数统统乘以10倍。
这里给出修改完后的代码。同学们可自行进行对比。
#include "reg52.h"
sbit DQ = P1^4;
void Delay_OneWire(unsigned int t)
{
while(t--);
}
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);
}
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;
}
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;
}
自写代码
官方给的赛点资源包只是提供了最基本的通讯协议,而真正的器件要想实现通信还要自己编写相关驱动。废话少说,直接上写好的驱动。
unsigned char read18b20()
{
unsigned char low,high,temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(200);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
temp=(high<<4)|(low>>4);
return temp;
}
这段驱动代码本人习惯直接就写在oneire.c里,在oneire.h中再定义以便,主程序里包含oneire.h就可以调用了。
注意:这段代码只适用于返回温度的整数部分,代码中将连续读了两次温度,第一次读取温度值低八位,第二次读取温度值高八位,在数据处理中将温度值高八位左移四位,也就是把符号位移出去,将低四位的整数左移到高四位,再把温度值低八位右移四位,也就是把小数位移出去,将高四位的整数右移到低四位,最后将两者得到的高低四位连接成八位。这八位都是整数位,其值就是温度值的整数部分。
在主函数里面只需要定义一个8位整数,本人习惯unsigned char 型,就可以接收数据了,后面会给出主函数。
温度值小数实现代码如下
long read18b20()
{
unsigned char low,high;
long temp;
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0x44);
Delay_OneWire(200);
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);
low=Read_DS18B20();
high=Read_DS18B20();
temp=high&0x0f;
temp=temp<<8;
temp=temp|low;
temp=temp*6.25;
return temp;
}
这里定义了16位的长整型,也可以使用unsigned int,代码中将温度值高八位中高四位符号位清零,默认我们温度在零上。将所有有效数字组合得到的值乘以0.0625就可以得到真实的温度值。本人为了后面编写代码方便,直接扩大100倍,在主函数里面调用并将温度显示在数码管上时就可减少不必要的麻烦。举个例子室温23.65°,温度传感器暂存器数据为23.65°,驱动程序返回2365°,在主函数里数码管显示只需要将百位3的数码管点亮小数点即可。可根据自己的需求扩大倍数。
实现代码
最后以实现代码的主函数结束吧,以实现温度小数位为例。
#include <stc15f2k60s2.h>
#include "onewire.h"
unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf,0xc1,0x8e,0xc6,0x89,0x8c};
unsigned char buff[]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
long wendu;
void close()
{
P2=(P2&0x1f)|0xa0;
P0=0;
P2&=0x1f;
P2=(P2&0x1f)|0x80;
P0=0xfb;
P2&=0x1f;
}
void delay(unsigned int ms)
{
unsigned int i,j;
for(i=ms;i>0;i--)
for(j=845;j>0;j--);
}
void display()
{
unsigned char index;
for(index=0;index<8;index++)
{
P2=(P2&0x1f)|0xe0;
P0=0xff;
P2&=0x1f;
P2=(P2&0x1f)|0xc0;
P0=1<<index;
P2&=0x1f;
P2=(P2&0x1f)|0xe0;
P0=buff[index];
P2&=0x1f;
delay(1);
}
P2=(P2&0x1f)|0xe0;
P0=0xff;
P2&=0x1f;
}
void main()
{
close();
while(1)
{
wendu=read18b20();
buff[0]=tab[10];
buff[1]=tab[10];
buff[2]=tab[10];
buff[3]=tab[10];
buff[4]=tab[wendu/1000];
buff[5]=tab[wendu%1000/100]&0x7f;
buff[6]=tab[wendu%100/10];
buff[7]=tab[wendu%10];
display();
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)