本节直接解释代码原理不讲手册理论。
首先知道DS18B20是温度传感器,长得很像三极管的一个东西,里面集成了不少电路。但是呢它只有三个引脚出除了电和地就只剩下一个可以用来通信的引脚了,但是我们知道的许多通信中最少都要两个引脚了,类似于I2c、SPI、485、TTL等等都不是一个引脚通信的,所以只能使用单总线通信,那么单总线是怎么实现通信的呢。单总线就是发送和接收是根据他们不同的时间也就是时序来表示他们的当前状态的,而且还有一个应答,当我收到数据了就应答,或者发送数据应答。就是这样来时现通信时序的。
那么我们看一下这个时序图吧,可以看到这个是初始化的时序图,这个图有是有了,该怎么实现初始化呢。可以看到它是由时间的来进行判断的,但是这个时间单位很小是us微秒级别,这个要根据不同的单片机时钟,写的延时就不同了。但是我们已经知道这个时序了,实现起来还是不难的,首先看到这一个实现整个实序下来是960us,但是如果按照进入60~240us那里的脉冲实现开始算是只有要在480us~960us整个区间的。
下面我们实现一下初始化代码,边实现,边讲实现原理,可以看到我们的这个 oneWire可以就是我们DS18B20的通信引脚,把它定义好就可以了。这个可以自己看自己的原理图的引脚就行。然后看到时序图第一个480us是一个低电平,我们这里先确定把这个置高然后在置底,是为了我们更好理解,然后我们设置500us也是可以的,这个延时是用软件生成的使用stc-isp-15xx-v6.86L.exe这个软件,然后我的单片机时钟是11.0592khz,可以自己设置自己的时钟然后生成延时就好了,接下来到了释放总线的15~60us。我们这里设置了70us
下面到了接收这个的电平了,为什么接收呢,因为这个是DS18B20数据手册写的下面这句话
(总线控制器拉低总线并保持 480us 以发出(TX)一个复位脉
冲,然后释放总线,进入接收状态(RX)。单总线由 5K 上拉电阻拉到高电平。当
DS18B20 探测到 I/O 引脚上的上升沿后,等待 15-60us,然后发出一个由 60-240us
低电平信号构成的存在脉冲。)
就是这个释放完就进入接收状态,所以要读取当前总线的状态。最后我们把后面的480us走完然后就结束初始化了。
sbit oneWire=P3^7;
unsigned char oneWire_Init()
{
unsigned char i,Ack;
EA=0; //防止定时器打断延迟us级别的精度 但是会影响定时器的计时不稳定,
oneWire=1; //初始化时序_该时间的时间范围可以从480到960微妙
oneWire=0;
i = 227;while (--i); //delay()500us;
oneWire=1; //释放总线
//这里是70us 这样就确定进入的是总线拉低。延时15~60us,并进入接收模式。接着DS18B20拉低总线60~240us
i = 29;while (--i);
Ack=oneWire; //读取电平
i = 227;while (--i); //500us 这里是用来后面走完时序
EA=1; //初始化结束了再打开防止定时器打断延迟us级别的精度
return Ack;
}
读时序我们可以看到写发送的时序更短也就是的时序,是60~120us这个区间多了不行,少了也不行。而且是在低电平的状态。
下面是写发送时序的代码,同样的先把定时器中断关闭,免得影响,但是这里有个坏处就是会影响到定时器的使用精准度。
同样的我们这个时序在60us完成,首先是置低ke,走10us,这里有一个过度5us的时间,不然直接15us再开始,可能导致数据丢失问题,但是这个没有试过。有时间可以试一下。然后再这个DS18B20采样的15us时序过后开始采用,也就是把数据位发送到总线中。然后走完这个剩余的时序50us。如果写的时序超过了120us就会变成初始化的时序了,就不是写时序了。不然可以试一下。接着关闭定时器,接释放总线。
void oneWire_SandBit(unsigned char Bit) //位
{
unsigned char i;
EA=0; //防止定时器打断延迟us级别的精度
oneWire=0; //拉低
i = 3;while (--i); //10us
oneWire=Bit; //写样 //写时序至少需要60us
i = 22;while (--i); //50us
EA=1; //初始化结束了再打开防止定时器打断延迟us级别的精度
oneWire=1; //释放
}
写完了,就到了接收时序了,可以看到下面的红线,读时序中。是在15us内完成0到1的变化,然后开始采样数据的,那么我们可以仿照上面的,先之0,然后延时一下置1,接着留一点过度时间,然后接收时序。
代码是这样写的,先0延时5us 到置1,这样就有释放上一个的过度留下时间,然后过5us在留下过度时间,然后开始采样,采样的整个周期在剩下的50us完成,然后释放总线。关闭定时器。然后接收的数据位。
unsigned char oneWire_ReadBit() //位
{
unsigned char Bit,i;
EA=0; //防止定时器打断延迟us级别的精度
oneWire=0; //拉低
i = 1;while (--i); //5us
oneWire=1; //释放
//读时序至少需要60us
i = 1;while (--i); //5us
Bit=oneWire; //采样
i = 20;while (--i); //50us
EA=1; //初始化结束了再打开防止定时器打断延迟us级别的精度
return Bit;
}
上面整个的初始的读写操作已经完成,但是呢。这个只是一个位的操作而已
数据手册可是八个位一个字节啊。是不是我们要写八次初始化,然后每一次都发送0或1然后完成这个16进制嘛?当然不可能,所以我们就用循环。从最低位开始循环,我们&与是判断这个位置有就是1没有就是0.
所以我们就按照这一个位&循环八次。就可以完成这个了操作了。
下面代码这样写先一样关闭定时器,然后循环八次,把我们的位与上低位开始往0000 0001<<1,这样子每次移动移位,当识别到这个位有数据的时候,那么它就是1,,没有就是0这样8个位下来就是0了。
void oneWire_SandByte(unsigned char Byte) //字节
{
unsigned char i;
EA=0; //防止定时器打断延迟us级别的精度
for(i=0;i<8;i++)
{
oneWire_SandBit(Byte&(0x01<<i));
}
EA=1; //初始化结束了再打开防止定时器打断延迟us级别的精度
}
下面同样读操作也是一样的,唯一不一样的是我们要判断这个读的这个位是不是又数据来啊,只要它有数据来,那么哎它就不是0,那么我这个读的操作就可以实现了,就是这么简单。
unsigned char oneWire_ReadByte(void) //字节
{
unsigned char i,Byte=0x00;
EA=0; //防止定时器打断延迟us级别的精度
for(i=0;i<8;i++)
{
if(oneWire_ReadBit()){(Byte|=(0x01<<i)); }
}
EA=1; //初始化结束了再打开防止定时器打断延迟us级别的精度
return Byte;
}
这一整个时序的时现就完成了。就是这么简单。最重要的就是看懂时序图就足够了。