完美实现STM32单总线挂多个DS18B20

一般常见的STM32的关于DS18B20的例程都是检测一个传感器,代码一般都是跳过ROM检测,直接获取温度值。这种写法并不适用于单总线上挂载多个DS18B20的情况,Sandeepin的这个代码就是针对这种情况完善的单总线挂多个DS18B20检测,实现获取每个DS18B20的ID和温度。

主要的DS18B20时序代码没变,增加了搜索ROM函数,获取温度时先匹配ID。

核心代码如下:

DS18B20.c文件代码:

#include "DS18B20.h"  

#include "Delay.h"  

#include "stdio.h" // printf用  

  

#define DS18B20_GPIO_NUM                 GPIO_Pin_5  

#define DS18B20_GPIO_X                  GPIOC  

#define RCC_APB2Periph_DS18B20_GPIO_X   RCC_APB2Periph_GPIOC  

  

#define DS18B20_DQ_OUT_Low          GPIO_ResetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM)   

#define DS18B20_DQ_OUT_High         GPIO_SetBits(DS18B20_GPIO_X,DS18B20_GPIO_NUM)   

#define DS18B20_DQ_IN               GPIO_ReadInputDataBit(DS18B20_GPIO_X,DS18B20_GPIO_NUM)   

  

#define MaxSensorNum 8  

unsigned char DS18B20_ID[MaxSensorNum][8];  // 存检测到的传感器DS18B20_ID的数组,前面的维数代表单根线传感器数量上限  

unsigned char DS18B20_SensorNum;            // 检测到的传感器数量(从1开始,例如显示1代表1个,8代表8个)  

  

// 配置DS18B20用到的I/O口  

void DS18B20_GPIO_Config(void)  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_DS18B20_GPIO_X, ENABLE);  

    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);  

    GPIO_SetBits(DS18B20_GPIO_X, DS18B20_GPIO_NUM);  

}  

  

// 引脚输入  

void DS18B20_Mode_IPU(void)  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  

    GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);  

}  

  

// 引脚输出  

void DS18B20_Mode_Out(void)  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_NUM;  

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

    GPIO_Init(DS18B20_GPIO_X, &GPIO_InitStructure);  

  

}  

  

// 复位,主机给从机发送复位脉冲  

void DS18B20_Rst(void)  

{  

    DS18B20_Mode_Out();  

    DS18B20_DQ_OUT_Low;     // 产生至少480us的低电平复位信号  

    Delay_us(480);  

    DS18B20_DQ_OUT_High;    // 在产生复位信号后,需将总线拉高  

    Delay_us(15);  

}  

  

// 检测从机给主机返回的应答脉冲。从机接收到主机的复位信号后,会在15~60us后给主机发一个应答脉冲  

u8 DS18B20_Answer_Check(void)  

{  

    u8 delay = 0;  

    DS18B20_Mode_IPU(); // 主机设置为上拉输入  

    // 等待应答脉冲(一个60~240us的低电平信号 )的到来  

    // 如果100us内,没有应答脉冲,退出函数,注意:从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲  

    while (DS18B20_DQ_IN&&delay < 100)  

    {  

        delay++;  

        Delay_us(1);  

    }  

    // 经过100us后,如果没有应答脉冲,退出函数  

    if (delay >= 100)//Hu200  

        return 1;  

    else  

        delay = 0;  

    // 有应答脉冲,且存在时间不超过240us  

    while (!DS18B20_DQ_IN&&delay < 240)  

    {  

        delay++;  

        Delay_us(1);  

    }  

    if (delay >= 240)  

        return 1;  

    return 0;  

}  

  

// 从DS18B20读取1个位  

u8 DS18B20_Read_Bit(void)  

{  

    u8 data;  

    DS18B20_Mode_Out();  

    DS18B20_DQ_OUT_Low; // 读时间的起始:必须由主机产生 >1us <15us 的低电平信号  

    Delay_us(2);  

    DS18B20_DQ_OUT_High;  

    Delay_us(12);  

    DS18B20_Mode_IPU();// 设置成输入,释放总线,由外部上拉电阻将总线拉高  

    if (DS18B20_DQ_IN)  

        data = 1;  

    else  

        data = 0;  

    Delay_us(50);  

    return data;  

}  

  

// 从DS18B20读取2个位  

u8 DS18B20_Read_2Bit(void)//读二位 子程序  

{  

    u8 i;  

    u8 dat = 0;  

    for (i = 2; i > 0; i--)  

    {  

        dat = dat << 1;  

        DS18B20_Mode_Out();  

        DS18B20_DQ_OUT_Low;  

        Delay_us(2);  

        DS18B20_DQ_OUT_High;  

        DS18B20_Mode_IPU();  

        Delay_us(12);  

        if (DS18B20_DQ_IN)  dat |= 0x01;  

        Delay_us(50);  

    }  

    return dat;  

}  

  

// 从DS18B20读取1个字节  

u8 DS18B20_Read_Byte(void)  // read one byte  

{  

    u8 i, j, dat;  

    dat = 0;  

    for (i = 0; i < 8; i++)  

    {  

        j = DS18B20_Read_Bit();  

        dat = (dat) | (j << i);  

    }  

    return dat;  

}  

  

// 写1位到DS18B20  

void DS18B20_Write_Bit(u8 dat)  

{  

    DS18B20_Mode_Out();  

    if (dat)  

    {  

        DS18B20_DQ_OUT_Low;// Write 1  

        Delay_us(2);  

        DS18B20_DQ_OUT_High;  

        Delay_us(60);  

    }  

    else  

    {  

        DS18B20_DQ_OUT_Low;// Write 0  

        Delay_us(60);  

        DS18B20_DQ_OUT_High;  

        Delay_us(2);  

    }  

}  

  

// 写1字节到DS18B20  

void DS18B20_Write_Byte(u8 dat)  

{  

    u8 j;  

    u8 testb;  

    DS18B20_Mode_Out();  

    for (j = 1; j <= 8; j++)  

    {  

        testb = dat & 0x01;  

        dat = dat >> 1;  

        if (testb)  

        {  

            DS18B20_DQ_OUT_Low;// 写1  

            Delay_us(10);  

            DS18B20_DQ_OUT_High;  

            Delay_us(50);  

        }  

        else  

        {  

            DS18B20_DQ_OUT_Low;// 写0  

            Delay_us(60);  

            DS18B20_DQ_OUT_High;// 释放总线  

            Delay_us(2);  

        }  

    }  

}  

  

//初始化DS18B20的IO口,同时检测DS的存在  

u8 DS18B20_Init(void)  

{  

    DS18B20_GPIO_Config();  

    DS18B20_Rst();  

    return DS18B20_Answer_Check();  

}  

  

// 从ds18b20得到温度值,精度:0.1C,返回温度值(-550~1250),Temperature1返回浮点实际温度  

float DS18B20_Get_Temp(u8 i)  

{  

    //u8 flag;  

    u8 j;//匹配的字节  

    u8 TL, TH;  

    short Temperature;  

    float Temperature1;  

    DS18B20_Rst();  

    DS18B20_Answer_Check();  

    DS18B20_Write_Byte(0xcc);// skip rom  

    DS18B20_Write_Byte(0x44);// convert  

    DS18B20_Rst();  

    DS18B20_Answer_Check();  

  

    // DS18B20_Write_Byte(0xcc);// skip rom  

    //匹配ID,i为形参  

    DS18B20_Write_Byte(0x55);  

    for (j = 0; j < 8; j++)  

    {  

        DS18B20_Write_Byte(DS18B20_ID[i][j]);  

    }  

  

    DS18B20_Write_Byte(0xbe);// convert  

    TL = DS18B20_Read_Byte(); // LSB     

    TH = DS18B20_Read_Byte(); // MSB    

    if (TH & 0xfc)  

    {  

        //flag=1;  

        Temperature = (TH << 8) | TL;  

        Temperature1 = (~Temperature) + 1;  

        Temperature1 *= 0.0625;  

    }  

    else  

    {  

        //flag=0;  

        Temperature1 = ((TH << 8) | TL)*0.0625;  

    }  

    return Temperature1;  

}  

  

// 自动搜索ROM  

void DS18B20_Search_Rom(void)  

{  

    u8 k, l, chongtuwei, m, n, num;  

    u8 zhan[5];  

    u8 ss[64];  

    u8 tempp;  

    l = 0;  

    num = 0;  

    do  

    {  

        DS18B20_Rst(); //注意:复位的延时不够  

        Delay_us(480); //480、720  

        DS18B20_Write_Byte(0xf0);  

        for (m = 0; m < 8; m++)  

        {  

            u8 s = 0;  

            for (n = 0; n < 8; n++)  

            {  

                k = DS18B20_Read_2Bit();//读两位数据  

  

                k = k & 0x03;  

                s >>= 1;  

                if (k == 0x01)//01读到的数据为0 写0 此位为0的器件响应  

                {  

                    DS18B20_Write_Bit(0);  

                    ss[(m * 8 + n)] = 0;  

                }  

                else if (k == 0x02)//读到的数据为1 写1 此位为1的器件响应  

                {  

                    s = s | 0x80;  

                    DS18B20_Write_Bit(1);  

                    ss[(m * 8 + n)] = 1;  

                }  

                else if (k == 0x00)//读到的数据为00 有冲突位 判断冲突位  

                {  

                    //如果冲突位大于栈顶写0 小于栈顶写以前数据 等于栈顶写1  

                    chongtuwei = m * 8 + n + 1;  

                    if (chongtuwei > zhan[l])  

                    {  

                        DS18B20_Write_Bit(0);  

                        ss[(m * 8 + n)] = 0;  

                        zhan[++l] = chongtuwei;  

                    }  

                    else if (chongtuwei < zhan[l])  

                    {  

                        s = s | ((ss[(m * 8 + n)] & 0x01) << 7);  

                        DS18B20_Write_Bit(ss[(m * 8 + n)]);  

                    }  

                    else if (chongtuwei == zhan[l])  

                    {  

                        s = s | 0x80;  

                        DS18B20_Write_Bit(1);  

                        ss[(m * 8 + n)] = 1;  

                        l = l - 1;  

                    }  

                }  

                else  

                {  

                    //没有搜索到  

                }  

            }  

            tempp = s;  

            DS18B20_ID[num][m] = tempp; // 保存搜索到的ID  

        }  

        num = num + 1;// 保存搜索到的个数  

    } while (zhan[l] != 0 && (num < MaxSensorNum));  

    DS18B20_SensorNum = num;  

    //printf("DS18B20_SensorNum=%d\r\n",DS18B20_SensorNum);  

}  

DS18B20.h文件代码:

#ifndef __DS18B20_H  

#define __DS18B20_H   

    

#include "stm32f10x.h"  

  

u8 DS18B20_Init(void);  

u8 DS18B20_Read_Byte(void);  

u8 DS18B20_Read_Bit(void);  

u8 DS18B20_Answer_Check(void);  

void  DS18B20_GPIO_Config(void);  

void  DS18B20_Mode_IPU(void);  

void  DS18B20_Mode_Out(void);  

void  DS18B20_Rst(void);  

void  DS18B20_Search_Rom(void);  

void  DS18B20_Write_Byte(u8 dat);  

float DS18B20_Get_Temp(u8 i);  

  

#endif  

main.c文件代码:

#include "stm32f10x.h"  

#include "stdio.h"  

#include "string.h"//strlen、memset用到  

#include "USART.h"  

#include "Delay.h"  

#include "DS18B20.h"  

  

extern unsigned char DS18B20_ID[8][8];//检测到的传感器ID存数组  

extern unsigned char DS18B20_SensorNum;  

  

int main(void)  

{  

    u8 num=0;  

    USART1_init(9600);  

    while(DS18B20_Init())//初始化DS18B20,兼检测18B20  

    {  

        printf("DS18B20 Check Failed!\r\n");    

    }  

    printf("DS18B20 Ready!\r\n");  

    while(1)  

    {       

        DS18B20_Search_Rom();  

        printf("DS18B20_SensorNum:%d\r\n",DS18B20_SensorNum);  

      for(num=0;num<DS18B20_SensorNum;num++)  

        {  

            printf("ID:%02x%02x%02x%02x%02x%02x%02x%02x TM:%.2f\r\n",DS18B20_ID[num][0],DS18B20_ID[num][1],DS18B20_ID[num][2],DS18B20_ID[num][3],DS18B20_ID[num][4],DS18B20_ID[num][5],DS18B20_ID[num][6],DS18B20_ID[num][7],DS18B20_Get_Temp(num));  

        }  

        printf("\r\n");  

        Delay_s(2);  

    }     

}

运行结果如图:

帮严博士出本科题的时候,出了一个DS18B20的分布式温度检测系统,要求肯定不仅仅是这篇文章的简略例子了。不仅单总线,一块单片机还要挂多总线,实现更多传感器数据采集,最好还配上上位机,反正把自己能想到的东西都加进来了,把一个简单的DS18B20包装得高大上。