DS18B20是一种半双工单总线通信方式,它共有6中信号:复位脉冲,应答脉冲,写0,写1,读0和读1。所有这些信号,除了应答脉冲以外,都是由主机发出同步信号,并且发送所有的命令和数据都是字节的低位在前。
复位脉冲
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少480us,以产生复位脉冲,接着主机释放总线,4.7K的上拉电阻将总线拉高,延时15~16us,并进入接收模式。
//复位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //设置为输出模式
DS18B20_DQ_OUT=0; //拉低DQ
delay_us(750); //拉低750us(至少480us)
DS18B20_DQ_OUT=1; //DQ=1拉高释放总线
delay_us(15); //15US
}
此时进入接收模式,等待应答信号。
应答信号
将DS18B20总线拉低60~240us,产生低电平应答脉冲。
等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN();//SET PA0INPUT
while(DS18B20_DQ_IN&&retry<200)// DS18B20_DQ_IN的作用为读引脚,观察其是否为低电平
{
retry++;//如果为高电平,则retry++
delay_us(1);
}
if(retry>=200)//未检测到18B20
{
return 1;
}
else
{
retry=0;
}
while(!DS18B20_DQ_IN&&retry<240)
{
retry++;//低电平时retry++
delay_us(1);
}
if(retry>=240)return1;//由于拉低总线的时间在60~240us,大于240则没检测到18B20
return 0;
}
写时序
写时序包括写0时序和写1时序。所有写时序至少需要60us,且在两次独立的写时序之间需要1us的恢复时间,两种写时序均起始于主机拉低总线。
写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。
写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT();//设置IO口为输出
for (j=1;j<=8;j++) //八位数据,一位一位写
{
testb=dat&0x01;//取第一位数据
dat=dat>>1;//移位
if (testb) //输出高
{
DS18B20_DQ_OUT=0;//主机输出低电平
delay_us(2);//延时2us
DS18B20_DQ_OUT=1;
delay_us(60); //延时60us
}
else//输出低
{
DS18B20_DQ_OUT=0;//主机输出低电平
delay_us(60);
DS18B20_DQ_OUT=1;//释放总线
delay_us(2);
}
}
}
读时序
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能传输数据。
所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。
典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void)
{
u8 data;
DS18B20_IO_OUT();//设置IO口为输出
DS18B20_DQ_OUT=0; //输出低电平2us
delay_us(2);
DS18B20_DQ_OUT=1; //拉高释放总线
DS18B20_IO_IN();//设置IO口为输入
delay_us(12);//延时12us
if(DS18B20_DQ_IN)data=1;//读取总线数据(当前电平)
else data=0;
delay_us(50);//延时50us
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)
{
u8 i,j,dat=0;
for (i=1;i<=8;i++)
{
j=DS18B20_Read_Bit();
dat=(j<<7)|(dat>>1);
}
return dat;
}
DS18B20是如何读取
现在我们来看看DS18B20是怎么读取温度的,DS18B20的典型温度读取过程为:复位->发SKIP ROM命令(0XCC)->发开始转换命令(0X44)->延时->复位->发送SKIP ROM命令(0XCC)->发读存储器命令(0XBE)->连续读出两个字节数据(即温度)->结束。
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值(-550~1250)
short
DS18B20_Get_Temp(void)
{
u8 temp;
u8 TL,TH;
short tem;
DS18B20_Start (); // ds1820 start convert
DS18B20_Rst();//复位
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);//convert
TL=DS18B20_Read_Byte();// LSB
TH=DS18B20_Read_Byte();// MSB
if(TH>7)//(TH>xxxxx111)温度为负
{
TH=~TH;TL=~TL;
temp=0;
}
else temp=1;//温度为正
tem=TH;//获得高八位
tem<<=8;
tem+=TL;//获得底八位
tem=(float)tem*0.625;//转换
if(temp) return tem; //返回温度值
else return -tem;
}
其中
void
DS18B20_Start(void)// ds1820 start convert
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
紧接着我们来讲后面有关温度计算的问题,首先,我们用DS18B20_Read_Byte()函数读取两个字节的温度,先低后高。
DS18B20 能够提供两个字节的温度数据,共16位数。但是因为18B20是单总线的,数据都是一位一位传输的。每次读出的操作,DS18B20 仅仅送出8位数。必须读出两次,才能完整的读出16位数。两次读出时,DS18B20 先输出的是低八位,后输出的是高八位。这是器件本身的特性。
转化后得到的12位数据,存储在18B20的两个8比特的RAM中,二进制中的前面5位是符号位,如果测得的温度大于0,这5位为0,只要将测得的数值乘以0.0625即可得到实际温度;如果温度小于0,这五位为1,检测到的数值需要取反加1再乘以0.0625即可得到实际温度。(源码中0.625是因为要保证精度,在主函数中温度有除以10,所以相当于0.0625)
由上图可以看出前五位为符号位。
主函数中初始化DS18B20的函数如下:
//初始化DS18B20的IO口DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能PORTA时钟
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;//PORTA0 推挽输出
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出1
DS18B20_Rst();
return DS18B20_Check();//未检测到DS18B20则返回1
}
既然学到了这里,想必大家对这个都十分熟悉,这里我就不再多说。
以上,DS18B20读取温度的方式就讲完了。学会使用这些函数,就可以在主函数中调用他们来读取外界温度。