嵌入式开发入门-51单片机基础知识(9)- DS18B20

发布时间 2023-05-06 14:21:46作者: 王庚午

一、DS18B20简介

  1、DS18B20是一个数字温度传感器,单总线传输数据,测量温度范围为-45℃-125℃,测量精度可配置为9位,10位,11位,12位,默认配置12位,分别对应0.5℃、0.25℃、0.125℃和0.0625℃。

二、温度测量

  1、由于DS18B20通电后,处于空闲状态,不进行温度测量和转换,所以必须发出转换命令,DS18B20才开始工作。

  2、DS18B20工作后,测量的数据存放自ScrachPad寄存器中,并返回空闲状态。在温度转换过程中,DS18B20发送0响应,转换完成后,发送1响应。

  3、根据18B20 芯片手册中的电路图中看出,在不工作时,DQ拉高。

三、DS18B20操作步骤

第一步:初始化

  1、主机发送复位脉冲;

  2、主机接收DS18B20的存在脉冲;

第二步:ROM指令

  1、SEARCH ROM(F0h)

    搜索:系统刚通电时,如果存在多个从机,则需要使用Search ROM来识别总线中存在的各从机。

  2、READ ROM(33h)

    直接读取:该指令,只有在总线中只存在一个从机时使用。如果有多个存在,则会产生冲突。

  3、MATCH ROM(55h)

    匹配:该指令用于寻找总线中特定的从机。

  4、SKIP ROM(CCh)

    跳过:使用该指令不需要在使用其他ROM指令,可以直接操作总线上所有从机。如配合CONVERT指令,可以直接使能总线上所有从机开始工作转换。

  5、ALARM ROM(ECh)

    报警:该命令用于搜索总线中设置过报警参数的从机,在最近的转换过程中,是否有报警的设备。

第三步:功能指令

  1、CONVERT T(44h)

  转换温度:该指令用于启动单个温度转换,在外部供电时,18B20接收到该指令后,启动转换,转换进行时发送0,转换完成后发送1(DQ拉高,处于空闲状态)。

   2、WRITE SCRATCHPAD(4Eh)

  写指令:该命令用于主机将三字节的数据写入scratchpad寄存器中,第一个数据写入TH寄存器,第二个字节写入TL寄存器,第三个字节写入配置寄存器。

  数据首先传输最低位。三个字节必须在主机发送复位请求前写入,否则数据会丢失。

  3、READ SCRATCHPAD(BEh)

  读指令:该命令用于主机读取scratchpad寄存器的内容。数据首先最低位开始,直到读取第九个字节。如果主机只需要一部分数据,则主机可以在读到自己需要的数据后,就发出复位指令终止读取。

  4、COPY SCARTCHPAD(48h)

  复制指令:该指令用于将scratchpad中TH、TL和配置寄存器的内容复制到主机EEPROM中。如果在寄生电源模式下使用,在发出该指令后的10us内,主机必须强上拉10ms。

  5、RECALL E²

  调用指令:该指令用于从EEPROM中调用报警触发值(TH和TL)和配置数据,并将数据分别放在scratchpad的字节2、3和4中。主机在调用命令之后,发出读取时隙,DS18B20将通过发送0或1来指示调用状态。

  此命令允许主机将3字节的数据写入DS18B20的草稿行。第一个数据字节写入TH寄存器(暂存区的字节2),第二个字节写入TL寄存器(字节3),第三个字节写入配置寄存器(字节4)。数据必须首先传输最低有效位。所有三个字节必须在主机发出重置之前写入,否则数据可能已损坏。

四、初始化

  1、大部分涉及到通信的地方,初始化一般就是握手过程,双方达成共识,我要开始发送数据了,对方准备好数据了等。

  2、初始化步骤:

    (1)主机拉低总线至少480us完成复位脉冲。(类似于先给对方搭个招呼,然后释放总线,看对方如何回复)

    (2)主机释放总线。

    (3)DS18B20检测上升沿(在15us和60us内)(DS18B20等待主机给他打招呼),

    (4)检测到上升沿后,DS18B20将总线拉低60us-240us(DS18B20收到主机的招呼后,给主机回复招呼)

 1 bit Ds18b20_Init(void)
 2 {
 3     bit flag;
 4     DQ = 0;
 5     Delay500us();
 6     DQ = 1;
 7     Delay68us();
 8     flag = DQ;
 9     Delay500us();
10     return flag;
11 }
1 void Ds18b20_Check(void)
2 {
3     Lcd12864_Init();
4     while(Ds18b20_Init())Lcd12864_Show_String(2,2,"NO SENSOR!");    //初始化
5     Lcd12864_Show_String(2,2,"Initial OK");//检测到DS18B20,打印ok,否则打印no sensor
6     Delay500ms();
7 }

五、读写时隙

  5.1写时隙

  写入1时隙和写入0时隙。所有写入时隙持续时间至少为60us,单个写入时隙之间的恢复时间至少为1us,写入时隙都由主机将总线拉低开始。

要生成写入1的时隙,在总线拉低后,主机必须在15us内释放总线。

要生成写入0时隙,在总线拉低后,必须持续最少60us保持总线为低电平,然后在释放总线。

因为DS18B20在主机启动写入时隙后,在15us和60US之间采集主机数据,在这个期间为高,则向DS18B20写入1,如果为低,则写0。

   

 1 void Ds18b20_Write_Low(void)
 2 {
 3     DQ = 0;
 4     Delay68us();
 5     DQ = 1;
 6     _nop_();
 7     _nop_();
 8 }
 9 
10 
11 /******************************************************************************/
12 // 函数名称:Ds18b20_Write_High 
13 // 输入参数:无
14 // 输出参数:无
15 // 函数功能:DS18B20写1
16 /******************************************************************************/
17 void Ds18b20_Write_High(void)
18 {
19     DQ = 0;
20     _nop_();
21     _nop_();
22     DQ = 1;
23     Delay68us();
24 }
25 
26 
27 /******************************************************************************/
28 // 函数名称:Ds18b20_Write_Cmd 
29 // 输入参数:myCmd-命令
30 // 输出参数:无
31 // 函数功能:DS18B20写命令
32 /******************************************************************************/
33 void Ds18b20_Write_Cmd(uint8 cmd)
34 {
35     uint8 i;
36     for(i=0;i<8;i++)
37        {
38         if(cmd&0x01)Ds18b20_Write_High();
39         else Ds18b20_Write_Low();
40         cmd >>= 1;    
41     }    
42 }    

  5.2读时隙

  DS18B20只能在主机发出读取时隙时向主机发送数据。因此主机必须在发出readScratchpad[BEh]或readPower[B4h]命令后,立即生成读取时隙,以便DS18B20提供所请求的数据。

  读取时隙的持续时间至少为60us,时隙之间的恢复时间至少为1us,然后释放总线,从而启动读取时隙。

  在主机启动读取时隙后,DS18B20将在总线上传输1或0。

  DS18B20通过使总线保持高电平来发送1,将总线拉低来发送0。

  当发送0时,DS18B20将在时隙结束时释放总线,DS18B20发送的数据在启动读取时隙的下降沿之后15us内有效,因此,主机必须释放总线。然后从开始的15us内对总线状态进行采样。

uint8 Ds18b20_Read_Data(void)
{
    uint8 tmpRead=0x00;
    uint8 i;
    for(i=0;i<8;i++)
      {
        tmpRead>>=1;
        DQ = 0;
        _nop_();
        _nop_();
        DQ = 1;
        _nop_();
        _nop_();
        if(DQ)tmpRead|=0x80;//读1
        //else tmpRead|=0x00;//读0
        Delay68us();    
    }
    return tmpRead;
}

  5.3忙信号检测

void Ds18b20_Busy_Check(void)
{
    bit flag; //0-busy 1-done
    do{
        DQ = 0;
        _nop_();
        _nop_();
        DQ = 1;
        _nop_();
        _nop_();
        flag = DQ;
        Delay68us();        
    }while(!flag);
}

  5.4获取温度值

void Ds18b20_Get_Temp(uint8 *tempValue){
    uint8 valueLow,valueHigh;
    uint16 resultLow;    //范围超过了255

    Ds18b20_Init();
    Ds18b20_Write_Cmd(0xcc);//skip rom
    Ds18b20_Write_Cmd(0x44);//convert T
    //Delay750ms();//busycheck
    Ds18b20_Busy_Check();
    Ds18b20_Init();
    Ds18b20_Write_Cmd(0xcc);//skip rom
    Ds18b20_Write_Cmd(0xbe);//read scratchpad
    valueLow  = Ds18b20_Read_Data();//read byte0
    valueHigh = Ds18b20_Read_Data();//read byte1        
    resultLow = (valueLow & 0x0f) * 625;        
    valueLow >>= 4;
    *(tempValue+0) = (valueHigh *16 + valueLow)/100 + 0x30;   //温度值的百位
    *(tempValue+1) = (valueHigh *16 + valueLow)%100/10 + 0x30;//温度值的十位
    *(tempValue+2) = (valueHigh *16 + valueLow)%100%10 + 0x30;//温度值的个位
    *(tempValue+4) = resultLow/1000 + 0x30;                   //小数点后第一位
    *(tempValue+5) = resultLow%1000/100 + 0x30;               //小数点后第二位
    *(tempValue+6) = resultLow%1000%100/10 + 0x30;            //小数点后第三位
    *(tempValue+7) = resultLow%1000%100%10 + 0x30;            //小数点后第四位
}

  5.5主函数

int main(void){    
    Ds18b20_Check();                                     //检测总线是否存在DS18B20
    Lcd12864_Screen_Init();                              //LCD12864界面初始化设置    
    while(1){
        Ds18b20_Get_Temp(sampleResult);                  //读取温度
        if (sampleResult[0] == '0')sampleResult[0] = ' ';//首位如果为0,则不显示
        Lcd12864_Show_String(2, 2, sampleResult);        //显示温度
    }
}