#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
bit flag;
sbit ds=P2^2;      //DS18B20的数据引脚位定义
sbit dula=P2^6;
sbit wela=P2^7;
uchar code table_du[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,
0x7c,0x39,0x5e,0x79,0x71};
uint temp;

delay(uchar x)    //延时函数
{
	uchar a,b;
	for(a=x;a>0;a--)
		for(b=200;b>0;b--);
}

void delayus(uint t)   //微秒级的延时函数
{
	while(t--);
}

void ds_reset()    //单总线初始化函数
{
	ds=1;           //总线先置高，让ds等于1
	delayus(5);      //稍延时
	ds=0;          //主机发送复位脉冲 
	delayus(80);    //延时（在480~960ms之间）
	ds=1;          //释放总线，让ds等于1
	delayus(14);    //等待（15~60ms）
	if(ds==0)      //判断总线ds是否等于0
		flag=1;    //flag等于1表示DS18B20存在
	else
		flag=0;    //flag等于0表示DS18B20不存在。
	delayus(20);
}

bit ds_read_bit()    //读时隙（即位读取）函数
{
	bit dat;        //定义一个位的变量dat
	ds=0;         //主机把总线拉低
	_nop_();       //延时一个机器周期，一个机器周期大约一个微秒
	_nop_();       //再延时一个机器周期，保证延时大于1微秒
	ds=1;         //释放总线，把总线交给了DS18B20进行控制
	_nop_();      //延时约一个微秒，让总线上的数据保持稳定
	dat=ds;       //把总线上的数据赋给dat
	delayus(10);   //延时，让整个读时序至少会持续60个微秒
	return dat;    //把读到的值dat返回
}

uchar ds_read_byte()   //读一个字节函数
{
	uchar i,j,k;
	for(i=0;i<8;i++)   //循环8次
	{
		j=ds_read_bit();   //读到的位值赋给j
		k=(j<<7)|(k>>1);  //由于DS18B20在传输数据的时候，都是低位在先。那么我们读到
                         	  //的第一位，就是最低位。那么我们把j读到的数据先左移7位后，
                        	  //原来最低位就变成了最高位了。打个比方，比如刚开始最低位j的
                        	  //值是1，那么左移7位之后，就变成了1000 0000。k在没有赋值时，
                        	  //默认值是0，那么右移一位还是0000 0000，那么j和k进行或运算，
                       		  //结果是1000 0000。那么当第二位的值过来时，比如j还是个1，
                      		  //那么左移7位之后就变成了1000 0000，但这个时候K的值再右移
                        	  //的时候，就变成了0100 0000，j和k进行或运算之后结果就是1100 
                        	  //0000，这样依次循环8次。k就读到了一个字节。
	}
	return k;  //把读到的值k返回
}

void ds_write_byte(uchar dat)   //写一个字节函数
{
	uchar i;
	for(i=0;i<8;i++)      //循环8次
	{
		ds=0;          //把总线拉为低电平
		_nop_();        //延时一机器周期，约1微秒
		ds=dat&0x01;    //dat写0x01按位与，目的是先传送dat的最低位
		delayus(6);      //延时，让整个读时序持续60~120微秒
		ds=1;          //把总线释放，让ds等于1
		dat=dat>>1;     //让dat右移一位，准备下一位的写入
	}
		delayus(6);     //延时，让每个函数之间都有一定的间隔停顿
}

uint read_temperature()   //读取温度函数
{
	uchar a,b;
	ds_reset();            //单总线初始化
	ds_write_byte(0xcc);   //发送跳过ROM指令，板上只接了一个DS18B20，不需要匹配
	ds_write_byte(0xbe);   //读取温度值，即读暂存器（代码为BEH）
	a=ds_read_byte();    //调用读一个字节的函数,把读到的低8位赋值给a
	b=ds_read_byte();    //把读到的低8位赋值给b
	temp=b;
	temp=temp<<8;
	temp=temp|a;      //把高8位b和低8位a，组成16位数据赋给temp保存
	temp=temp*0.0625*10+0.5;  //把temp乘以0.0625就是它的实际温度值，我们平常的温度
                                      //一般都是正温度来的，也不会超过100度，那么我们现在就
                                      //把它当成一个几十度的正温度值来处理，我们在数码管上就
                                      //显示3位数：2位整数，一位小数。在这里呢，我们为了方
                                      //便写显示函数，我先把这个数放大10倍，那么在显示的时候，
                                      //就当成百位、十位、个位来处理。到时在十位加个小数点就
                                      //可以。后面的小数部分，我们就四舍五入，给它加个0.5。如
                                      //果后面的小数大于0.5，就向个位进1，如果小于0.5就不要。
	return temp;  //把temp值返回
}

display(uint temp)  //数码管显示函数
{
	P0=table_du[temp/100];
	dula=1;
	dula=0;
	P0=0xfe;
	wela=1;
	wela=0;
	delay(10);

	P0=table_du[temp%100/10]|0x80;  //或上0x80，目的是在第2个数码管显示出小数点
	dula=1;
	dula=0;
	P0=0xfd;
	wela=1;
	wela=0;	
	delay(10);

	P0=table_du[temp%100%10];
	dula=1;
	dula=0;
	P0=0xfb;
	wela=1;
	wela=0;
	delay(10);	
}

void main()   //主函数
{
	while(1)
	{
		ds_reset();       //单总线初始化
		ds_write_byte(0xcc);   //跳线ROM
		ds_write_byte(0x44);   //启动温度传换
		display(read_temperature());   //显示当前温度
	}
}