• 基于STM32_DS18B20单总线传感器驱动


    基于STM32_DS18B20单总线传感器驱动


    前言

    本文以一款典型的单总线传感器及其驱动——DS18B20为例,简单对1-Wire总线接口的传感器做个示例讲解,该项目基于硬件平台STM32F407,使用标准库本完成。


    一、BS18B20?

    DS18B20数字温度计提供9至12位(可配置)温度读数,指示设备的温度。信息通过1-Wire总线接口发送到/从DS18B20,因此只需要从中央微处理器连接到DS18B20的一根线(和接地)。读取、写入和执行温度转换的电源可以从数据线本身获得,而不需要外部电源。每个DS18B20都包含一个唯一的硅序列号,所以多个DS18B20可以存在于同一个1-Wire总线上。这允许在许多不同的地方放置温度传感器。此功能有用的应用包括暖通空调环境控制,感应建筑物,设备或机械内部的温度,以及过程监控和控制。

    示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
    特点:
    1.唯一的1线接口只需要一个端口引脚进行通信
    2.多功能简化了分布式温度传感应用
    3.不需要外部组件,可从数据线供电。
    4.供电范围3.0V至5.5V 零待机电源
    5.测量温度范围为-55°C至+125°C。相当于-67°F到+257°F ±0.5°C精度从-10°C到+85°C
    6.温度计分辨率可编程从9到12位

    二、原理

    1.复位与检验

    主机发送(TX)复位脉冲(低信号,至少480µs),然后主机释放线路并进入接收模式(RX),线总线通过5k上拉电阻拉到高状态。在检测到DQ引脚上的上升沿后,DS18B20等待15-60µs,然后发送存在脉冲(60-240µs的低电平信号),主机在该时段检测到DQ的低电平信号,表示DS18B20设备存在,否则设备不存在。参见驱动中:void DS18B20_Reset(void)和uint8_t DS18B20_Check(void)函数。
    在这里插入图片描述

    2.基本命令

    DS18B20常见的命令及含义如表所示:

    名称命令码功能
    Read ROM33h该命令允许总线主机读取DS18B20的8位族码、唯一的48位序列号和8位CRC。该命令只能在总线上有单个DS18B20时使用。如果总线上有多个从站,当所有从站试图同时传输时,就会发生数据冲突(open drain将产生有线AND结果)。
    Match ROM55hmatch ROM命令,后跟64位ROM序列,允许总线主机在多路总线上寻址特定的DS18B20。只有与64位ROM序列完全匹配的DS18B20才会响应以下内存功能命令。所有与64位ROM序列不匹配的从存储器将等待复位脉冲。该命令可用于总线上的单个或多个设备。
    Skip ROMCCh该命令允许总线主机在不提供64位ROM代码的情况下访问内存功能,从而在单丢总线系统中节省时间。如果总线上有一个以上的从站,并且在Skip ROM命令之后发出了Read命令,那么当多个从站同时传输时,总线上就会发生数据冲突(open drain下拉将产生一个有线and结果)。相当于跳过了识别码查验,直接读取温度
    Search ROMF0h当系统最初启动时,总线主机可能不知道1-Wire总线上的设备数量或它们的64位ROM代码。搜索ROM命令允许总线主人使用消除过程来识别总线上所有从设备的64位ROM代码。
    Read ScratchpadBEh该命令读取刮记本的内容。读取将从字节0开始,并将继续通过刮擦板,直到读取第九个(字节8,CRC)字节。如果不是所有的位置都要读取,主机可以在任何时候发出复位以终止读取。
    Search ROM44h该命令开始温度转换。无需进一步收集数据。温度转换将被执行,然后DS18B20将保持空闲。如果总线主机按照该命令发出读时隙,只要DS18B20忙于进行温度转换,它就会在总线上输出0;当温度转换完成时,它将返回一个1。如果parasitepowered,总线主必须在发出该命令后立即启用一个大于tconv的强上拉。

    基本操作过程参见官方手册给出的如下流程图
    在这里插入图片描述

    3.唯一ROM识别码

    每个DS18B20包含一个64位长的唯一ROM代码。发送0x33命令后可读取改64位识别码,前8位是1-Wire族代码(DS18B20代码为28h)。接下来的48位是唯一的序列号。最后8位是前56位的CRC(CRC = X8 + X5 + X4 + 1),(参见图4)。当有多个DS18B20设备同时挂在1-Wire总线下时,可通过发送0x55匹配命令后发送识别码来匹配ROM识别码来确定控制或读取哪一个DS18B20设备数据。以前的方式是先准确无误得读取每个DS18B20设备唯一ROM识别码并提前写入代码中,如果某个传感器设备损坏,更换传感器设备的同时要更改代码或识别码配置文件。后来手册中给出一个Search ROM命令(暂时没试过)。
    在这里插入图片描述

    4.温度转换

    DS18B20的核心功能是其直接数字温度传感器。DS18B20的分辨率是可配置的(9,10,11或12位),12位读数为出厂默认状态。这相当于0.5°C, 0.25°C, 0.125°C或0.0625°C的温度分辨率。在发出Convert T [44h]命令后,执行温度转换,热数据以16位扩展符号的二进制补码格式存储在刮板存储器中。一旦转换完成,可以通过发出Read Scratchpad [BEh]命令在1-Wire接口上检索温度信息。数据通过1-Wire总线传输,首先是LSB总线。温度寄存器的MSB包含“符号”(S)位,表示温度是正的还是负的。表2描述了输出数据与测量温度的确切关系。该表采用12位分辨率。转换方式参见驱动void DS18B20_GetTemp_Main(void)函数。
    在这里插入图片描述

    三、驱动代码

    .h文件:

    #ifndef __DS18B20_H
    #define __DS18B20_H
    
    #include "stdio.h"
    #include "Config.h" 
    #include "SysTick.h"
    
    /*******************************************
     *DS18B20 devier
     ********************************************/
    #define DS18B20_IS_READY          (1u)   //设备存在      
    #define DS18B20_NOT_READY         (0u)   //设备不存在
     
    #define DS18B20_DQ_IN()           {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}   //PG9 输入模式
    #define DS18B20_DQ_OUT()          {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;}   //PG9 输出模式
    
    #define DS18B20_DQ_PORT           GPIOG
    #define DS18B20_DQ_PIN            GPIO_Pin_9
    
    #define DS18B20_WAIT_TIMEOUT      (uint8_t)240	  //等待时间
    #define DS18B20_DELAY             (uint8_t)5	  //延时时间
    
    #define DS18B20_DQ_LOW            GPIO_ResetBits(DS18B20_DQ_PORT,DS18B20_DQ_PIN)    //为设置低电平
    #define DS18B20_DQ_HIGH           GPIO_SetBits(DS18B20_DQ_PORT,DS18B20_DQ_PIN)      //设置为高电平 
    
    #define DS18B20_DQ_STATUS         GPIO_ReadInputDataBit(DS18B20_DQ_PORT,DS18B20_DQ_PIN)		//读取DQ状态
     
    #define DS18B20_delay_us(a)       SysCtlDelayus(a)			//延时函数us
    
    typedef enum
    {
      DS18B20_1 = 0,
      DS18B20_2,
      DS18B20_3,
      DS18B20_4,
      DS18B20_Num_Counter
    }DS18B20_Num;
    
    
    /*DS18B20_SerialNumber
    ------------------------------------------------------------------------------------
    |  8-BIT CRC CODE      |     48-BIT SERIAL NUMBER      |    8-BIT FAMILY CODE(28h) |
    ------------------------------------------------------------------------------------
    MSB                 LSB MSB                         LSB  MSB                       LSB
    */
    typedef struct
    {
      uint8_t DS18B20_IndexNumber;          //设备编号
      uint8_t DS18B20_SerialNumber[8];      //ROM唯一识别码
      short   DS18B20_Temperature;          //温度数据
    }DS18B20_ATTRIB_Type;
    
    extern uint8_t DS18B20_Init(void);
    extern void DS18B20_Reset(void);
    extern short DS18B20_Get_Temperature(uint8_t Index_Num);
    extern void DS18B20_GetTemp_Main(void);
    
    #endif
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    .c文件:

     #include "DS18B20_Dev.h"
     
    //#define DS18B20_MORE_THAN_ONE 
    
    static DS18B20_ATTRIB_Type DS18B20_Temp[DS18B20_Num_Counter] = 
      {
        {DS18B20_1,{0x08,0x22,0x70,0xB0,0x9C,0x87,0x28},0u},
        {DS18B20_2,{0x00,0x00,0x00,0x00,0x00,0x00,0x00},0u},
        {DS18B20_3,{0x00,0x00,0x00,0x00,0x00,0x00,0x00},0u},
        {DS18B20_4,{0x00,0x00,0x00,0x00,0x00,0x00,0x00},0u},   
      };
    
    static uint8_t DS18B20_Check(void);
    static uint8_t DS18B20_Read_Bit(void);
    static uint8_t DS18B20_Read_Byte(void);
    static void DS18B20_Write_Byte(uint8_t aByte);
    static uint8_t DS18B20_Get_RomID(uint8_t *ID_Buffer);
    
    /************************************************************************************
    *@fuction	:DS18B20_Init
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    extern uint8_t DS18B20_Init(void)
    {
        DS18B20_Reset();
        if(DS18B20_Check() == DS18B20_IS_READY)
        {
          #ifdef DS18B20_MORE_THAN_ONE
            DS18B20_Get_RomID(DS18B20_Temp[0].DS18B20_SerialNumber);
          #else
            return DS18B20_IS_READY;
          #endif
        }
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Start
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    void DS18B20_Start(void)
    {
        DS18B20_Reset();	   
    	  if(DS18B20_Check() == DS18B20_IS_READY)	 
        {
          DS18B20_Write_Byte(0xCC);   //skip rom
          DS18B20_Write_Byte(0x44);   //convert
        }
        else
        {
           //error
        }
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Reset
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    void DS18B20_Reset(void)
    {
        //DS18B20 复位时序:DQ输出模式 DQ = 0(750us), DQ = 0(20us.
        DS18B20_DQ_OUT();
        DS18B20_DQ_LOW;
        DS18B20_delay_us(750);
        DS18B20_DQ_HIGH;
        DS18B20_delay_us(15);
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Check
    *@brief		:
    *@param		:--
    *@return	:1-device ok/0-device error
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    uint8_t DS18B20_Check(void)
    {
        uint8_t wait_time = 0;
        uint8_t Ready_Dev = 0;
    		
        //DQ输入模式
        DS18B20_DQ_IN();
        //等待DQ脚被DS18B20拉低
        while((DS18B20_DQ_STATUS) && (wait_time < DS18B20_WAIT_TIMEOUT))
        {
            wait_time++;
            DS18B20_delay_us(1);
        }
        if(wait_time >= DS18B20_WAIT_TIMEOUT)
        {
             //如果等待时间超时,则退出等待
             return (uint8_t)DS18B20_NOT_READY;
        }
        else
        {
             //等待DQ脚被DS18B20抬高
             wait_time = 0;
             while((!DS18B20_DQ_STATUS) && (wait_time < DS18B20_WAIT_TIMEOUT))
             {
                  wait_time++;
                  DS18B20_delay_us(1);
             }
             if(wait_time >= DS18B20_WAIT_TIMEOUT)
             {
                  //如果等待时间超时,则退出等待
                  return (uint8_t)DS18B20_NOT_READY;
             }
             else
             {
                  //如果未超时,则说明设备存在
                  return (uint8_t)DS18B20_IS_READY;              
             }
        }
    }
    
    
    
    /************************************************************************************
    *@fuction	:DS18B20_Write_Byte
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    static void DS18B20_Write_Byte(uint8_t aByte)
    {		
        
        uint8_t i = 0;
        uint8_t temp = 0;
        //将DQ设置为输出模式
        DS18B20_DQ_OUT();
        for(i = 0;i < 8;i++)
        {
            temp = aByte&0x01;
            aByte = aByte>>1;
            if(temp)
            {	
                //最低位为1
                DS18B20_DQ_LOW;
                DS18B20_delay_us(2);
                DS18B20_DQ_HIGH;
                DS18B20_delay_us(60);          
            }
            else
            {	  //最低位为0
                DS18B20_DQ_LOW;
                DS18B20_delay_us(60);
                DS18B20_DQ_HIGH;
                DS18B20_delay_us(2);
            }       
        }
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Read_Bit
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    static uint8_t DS18B20_Read_Bit(void) 			 //read one bit
    {
      uint8_t Bit_Status;
      
      DS18B20_DQ_OUT();       //SET PG9 OUTPUT
      DS18B20_DQ_LOW; 
      DS18B20_delay_us(2);
      DS18B20_DQ_HIGH; 
      DS18B20_DQ_IN();        //SET PG9 INPUT
      DS18B20_delay_us(12); 
      if(DS18B20_DQ_STATUS)
      {
        Bit_Status = 1;
      }
      else
      {    
        Bit_Status = 0;
      }    
      DS18B20_delay_us(50);
    
      return Bit_Status;
    }
    /************************************************************************************
    *@fuction	:DS18B20_Read_Byte
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    static uint8_t DS18B20_Read_Byte(void)
    {
        uint8_t i = 0 ,Bit_Status = 0,aByte = 0;
        //DQ为输入模式
        DS18B20_DQ_IN();
        for (i = 0; i < 8; i++)
        {
            //低位先出
            Bit_Status = DS18B20_Read_Bit();
            aByte = (Bit_Status << 7) | (aByte >> 1);
        }
        
        return aByte;
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Read_Byte
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    static uint8_t DS18B20_Get_RomID(uint8_t *ID_Buffer)
    {
        //uint8_t RomID[8];
        uint8_t i = 0;
        
        //DS18B20_Start();                    // ds1820 start convert
        DS18B20_Reset();
        DS18B20_Check();	 
        DS18B20_Write_Byte(0x33);             // skip rom
        
        //SDA为输入模式
        DS18B20_DQ_IN();
        for (i = 0; i < 8; i++)
        {
            ID_Buffer[i] = DS18B20_Read_Byte();
        }
        
        //return aByte;
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_GetTemp_Main
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    void DS18B20_GetTemp_Main(void)
    {
        uint8_t temp;
        uint8_t TL,TH;
    	short Temperature;
      
        DS18B20_Start();                    // ds1820 start convert
        DS18B20_Reset();
        DS18B20_Check();	 
        DS18B20_Write_Byte(0xCC);           // skip rom
        DS18B20_Write_Byte(0xBE);           // read	    
        TL = DS18B20_Read_Byte();           // LSB   
        TH = DS18B20_Read_Byte();           // MSB   
        if(TH > 7)
        {
            TH = ~TH;
            TL = ~TL; 
            temp = 0;     //温度为负  
        }
        else
        {      
          temp = 1;      //温度为正
        }      	  	  
        Temperature = TH;                           //获得高八位
        Temperature <<= 8;    
        Temperature += TL;                          //获得低八位
        Temperature = (double)Temperature * 0.625;  //转换     
    	if(temp)
        {
          DS18B20_Temp[0].DS18B20_Temperature = Temperature;                       //返回温度值
        }
        else 
        {
          DS18B20_Temp[0].DS18B20_Temperature = -Temperature;
        }      
    }
    
    /************************************************************************************
    *@fuction	:DS18B20_Get_Temperature
    *@brief		:
    *@param		:--
    *@return	:void
    *@author	:_Awen
    *@date		:2022-12-04
    ************************************************************************************/
    extern short DS18B20_Get_Temperature(uint8_t Index)
    {
        return DS18B20_Temp[0].DS18B20_Temperature;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305

    四、注意事项

    1.硬件电路中DQ脚外部会加上拉电阻,主机释放总线会被上拉电阻自动上拉,但为保险器件我们将主机释放中线写为主动上拉为高电平
    2. .h文件中对延时函数DS18B20_delay_us(a)的定义,关于 SysCtlDelayus(a)实际是ARM汇编的一种延时函数的写法,参见另一篇关于延时函数的博客(汇编延时)https://blog.csdn.net/Yin_w/article/details/130036593?spm=1001.2014.3001.5501

  • 相关阅读:
    【分布式-1】分布式理论
    python基于轻量级卷积神经网络模型开发构建眼疾识别系统
    Python语言学习实战-内置函数sorted()的使用(附源码和实现效果)
    深入探究 C++ 编程中的资源泄漏问题
    qemu-kvm:虚拟网络丢包实例UDP:virtionet-tun-bridge-tun-virtionet
    带你读论文丨S&P21 Survivalism: Living-Off-The-Land 经典离地攻击
    MySQL——Centos7下环境安装
    detect.py和train.py的参数解释
    【深度学习入门】- 用电路思想解释感知机
    信源编码 | 无线通信基础知识
  • 原文地址:https://blog.csdn.net/Yin_w/article/details/133324608