清翔零基础教你学51单片机_个人学习笔记(14)_数字温度传感器DS18B20(理论+实践)

说明

本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。

|

本次笔记对应视频教程的第40,,41集 数字温度传感器DS18B20(理论+实践)

如果笔记之中有任何错误,请在评论区指出,谢谢

目录

一、DS18B20 

1.1 总览

1.2 两种供电方式 

1.3 温度寄存器的格式​编辑

 1.4 内部存储器结构

配置报警 

配置精度 

 1.5 访问DS18B20的顺序

1.6  ROM指令

 1.7 功能指令

 1.8 功能指令总结表

二、 单总线信号

2.1 初始化时序

2.2 写时序

2.3 读时序

2.4 操作举例

三、 开发板原理图上的DS18B20

四、编程

4.1 创建工程

4.2 main.c

4.3 main.h

4.4 修改后的代码

 4.5 现象


 

一、DS18B20 

1.1 总览

注意中文数据手册有些许错误,看的时候需要留心。

 

1.2 两种供电方式 

DS18B20可以不接VCC而从数据引脚吸取电源,这样最少只需要数据脚和接地两根线就行了。

寄生供电
外部电源供电

 

1.3 温度寄存器的格式

S为0表示正的温度,S为1表示负的温度

 

 注:上电默认温度85℃

 1.4 内部存储器结构

 设置完数据之后可以再发送拷贝命令把数据复制到EEPROM中,上电后会自动把EEPROM的数据复制到TH,TL,和配置寄存器中,实现掉电不丢失数据。

配置报警 

 

 位0和位1是测得的温度,位2位3是用户设置的高低温度报警数据,位4是配置寄存器

配置精度 

存储器位4是配置精度的

 

 

 1.5 访问DS18B20的顺序

通过单线总线端口访问 DS18B20 的协议如下:

  1. 步骤1. 初始化
  2. 步骤2. ROM 操作指令必须先发出一条ROM指令表明了顺序很重要
  3. 步骤3. DS18B20 功能指令

 每一次 DS18B20 的操作都必须满足以上步骤,若是缺少步骤或是顺序混乱,器件将不会返回值。

1.6  ROM指令

Search ROM [F0h] ( 搜索 ROM 指令)

READ ROM [33h] ( 读取 ROM 指令 ) 

 MATH ROM [55h] ( 匹配 ROM 指令 )

 SKIP ROM [CCh] ( 忽略 ROM 指令)

ALARM SEARCH [ECH] 报警 搜索指令 

 1.7 功能指令

 1.8 功能指令总结表

清翔PPT总结ROM指令和功能指令

 

二、 单总线信号

2.1 初始化时序

 主机先拉低总线480us以上,构成复位脉冲,然后释放总线,DS18B20会在探测到上升沿后,等待15到60us,再拉低总线60到240us构成存在脉冲。

2.2 写时序

要产生写时序,先把总线拉低至少1us,如果写0,总线必须拉低60到120us,然后释放总线,如果写1,在写时序之后15us内把总线拉高,总时长要大于60us。注意两次写周期之间至少要间隔1us

2.3 读时序

 要产生读时序,必须把总线拉低至少1us,然后释放总线,在读信号开始后15us内总线控制器采样总线数据,读一位数据至少保持在60us以上。两次读周期之间至少间隔1us

2.4 操作举例

 

三、 开发板原理图上的DS18B20

 由于开发板上只挂载了1个DS18B20,所以可以直接发送忽略ROM指令,不需要发送64位地址,节省时间。

四、编程

4.1 创建工程

复制上一份工程文件夹,修改名称为“14.数字温度传感器DS18B20”,进入项目文件夹,打开工程文件,删除main.c函数的内容。

4.2 main.c

void main()
{
    int TEM;
    uchar TEML;
    uchar TEMH;
    while(1)
    {
        DSinit();       //初始化DS18B20
        DSWriteByte(0xCC);  //发送忽略ROM指令
        DSWriteByte(0x44);  //发送温度转换功能指令
        //上面3个步骤已经完成,再次操作需要再次进行3步
        DSinit();       //初始化DS18B20
        DSWriteByte(0xCC);  //发送忽略ROM指令
        DSWriteByte(0xBE);  //读取暂存器功能指令
        TEML = DSReadByte();    //读取1个字节(温度低位)
        TEMH = DSReadByte();    //读取1个字节(温度高位)
        DSinit();       //停止读取数据
        TEM = TEMH;
        TEM <<= 8;
        TEM |= TEML;
        TEM = TEM * 0.0625*10 + 0.5;    //扩大十倍并四舍五入
        SEG_DIS3(TEM);
        SEG_DIS(2,22);
    }
}

/**********************************************************************************/
/*************************  DS18B20  *********************************************/
/**********************************************************************************/

    //DS18B20初始化
bit DSinit()
{
    bit i;
    DS = 1;
    _nop_();
    DS = 0;
    delay_us(73);  //11.95 + 6.5*x = 480,得x ≈ 73
    DS = 1;     //释放总线
    delay_us(8);  //等待60us, 11.95 + 6.5*x = 60,得x=8
    i = DS;
    delay_us(29);  //等待DS发送完存在信号,200us, 11.95+6.5*x=200,得x=29
    DS = 1;
    return (i);
}

    //DS18B20写时序,写一个字节
void DSWriteByte(uchar DAT)
{
    uchar i;
    DS = 1;
    for (i = 0; i < 8; i++)
    {
        DS = 0;
        _nop_();
        DS = DAT & 0x01;
        DAT >>= 1;
        delay_us(11);  //写0和写1都要求时长超过60us,11.95+6.5*x=80,->x=11
        DS = 1;     //释放总线,等待下一次传输
        _nop_();
    }
    DS = 1;
}

    //DS18B20读一个字节
uchar DSReadByte()
{
    uchar i;
    uchar j;
    uchar dat;
    DS = 1;
    for (i = 0; i < 8; i++)
    {
        DS = 0;
        _nop_();    //产生读时序
        DS = 1;     //释放总线
        delay5us(); //延时一会儿
        j = DS;
        delay_us(10);   //延时等待这次数据DS18B20发送完全
        DS = 1;
        _nop_();
        dat = (j<<7)|(dat>>1);	//接收到的数据是从低位到高位,顺序调换一下
    }
    return (dat);     
}

void delay_us(uchar us)
{
    while(us--);    //进入需要11.95us,执行一次us--需要6.5us
}

delay_us的时间可以用debug功能查看 。本代码并未考虑温度为负的情况。根据温度寄存器的格式,高字节前5位都是符号位,所以可以判断高字节前5位的任何1位是否为1,为1则表示温度为负。再处理一下数码管显示部分,就可以实现负温度显示了。

注意负数是以补码的形式存储在内存中的,所以还要处理一下,还原温度值。关于负数如何以补码的形式存储,请自行查阅资料。为了验证自己的负数代码是否正确,可以注释掉从DS18B20读取数据的代码,然后自己手动给对应的变量赋值,编译下载看数码管是显示正常。

对于显示位数,也可以通过处理显示函数来实现,温度整数部分最大是3位,小数部分最大是4位,可以先判断温度是否为负,来决定是否显示负号,再判断百位,十位,个位是不是0,如果是0则不显示,位置可以用一个变量来判断,某个位有数字显示(非0),就显示完之后让这个变量自加,如果某个位是0,就不运行这个位的代码,从而能实现自动向左靠齐。个位判断完后,在个位显示代码里面要加上小数点,然后再处理剩下的小数部分。小数部分最多4位,如果想实现不显示小数最后的0,可以判断最后1位是否为0,为0则不显示,如果最后一位为0,再判断倒数第2位是否为0,如此连续判断4位小数,就可以实现去掉小数末尾的0

在显示全部整数和小数中,就没必要进行四舍五入了,直接显示原转换精度,把转换后的TEM乘以0.0625再乘以10000,这样就能去掉全部的小数,把小数转换成了一个整数,再送入数码管显示处理。

注意,由于DS18B20测温范围最大125℃,日常测温在10到40度之间,但是第二位的小数点是手动打上去的,实际变量的值要乘以10倍,也就是100到400,超过了uchar类型的范围,因此我修改了一下SEG_DIS3为

void SEG_DIS3(uint i) 
{
    SEG_DIS(1, i / 100);
    SEG_DIS(2, i % 100 / 10);
    SEG_DIS(3, i % 10);
}

只需要修改一下参数由uchar编程uint就行了。

如果想要修改成其他精度,可以参考

  • 1.5 访问DS18B20的顺序
  • 1.6  ROM指令
  • 1.7 功能指令

这三个小节的内容。

	DSinit();//初始化DS18B20
	DSWriteByte(0xcc);//发送跳跃ROM指令
	DSWriteByte(0x4e);//写暂存器指令
	DSWriteByte(0x7f);
	DSWriteByte(0xf7);
	DSWriteByte(0x1f);//配置工作在9位模式下
	DSinit();//初始化DS18B20
	DSWriteByte(0xcc);//发送跳跃ROM指令 
	DSWriteByte(0x48);//把温度上下限和精度设置存储到EEPROM中,下次上电自动读取

这些内容放在while循环的前面就行了,因为这些内容只需要执行一次,不需要一直在while循环里面重复执行,而且EEPROM有写入次数限制

4.3 main.h

写入函数声明和变量定义

/**************  DS18B20函数  *************************/
bit DSinit();                //DS18B20初始化
void DSWriteByte(uchar DAT);    //DS18B20写时序,写一个字节
uchar DSReadByte();         //DS18B20读一个字节

sbit DS = P2^2;

4.4 修改后的代码

void SEG_DIS(uchar position, uchar number)
{
    //P0 = 0xFF;
    DU = 0;
    P0 = we[position - 1];
    WE = 1;
    WE = 0;
    
    //P0 = 0x00;
    P0 = du[number];
    DU = 1;
    DU  = 0;
}

bit s;  //标志温度是否为负的变量

void main()
{
    //这些内容只需要执行一次即可,执行完可以注释掉
    unsigned long TEM;
    uchar TEML;
    uchar TEMH;
    uchar i;
//    
//    DSinit();       //初始化DS18B20
//    DSWriteByte(0xCC);  //发送忽略ROM指令
//    DSWriteByte(0x4e);  //写暂存器指令
//    DSWriteByte(0x1e);   //设置报警温度上限:30
//    DSWriteByte(0x1d);   //设置报警温度下限:29
//    DSWriteByte(0x7f);  //设置12位精度
//    
//    DSinit();       //初始化DS18B20
//    DSWriteByte(0xCC);  //发送忽略ROM指令
//    DSWriteByte(0x48);  //拷贝暂存器数据到EEPROM实现掉电不丢失
    //循环部分
    while(1)
    {
        
        DSinit();       //初始化DS18B20
        DSWriteByte(0xCC);  //发送忽略ROM指令
        DSWriteByte(0x44);  //发送温度转换功能指令
        //上面3个步骤已经完成,再次操作需要再次进行3步
        DSinit();       //初始化DS18B20
        DSWriteByte(0xCC);  //发送忽略ROM指令
        DSWriteByte(0xBE);  //读取暂存器功能指令
        TEML = DSReadByte();    //读取1个字节(温度低位)
        TEMH = DSReadByte();    //读取1个字节(温度高位)
        DSinit();       //停止读取数据
        TEM = TEMH;
        TEM <<= 8;
        TEM |= TEML;
        if (TEMH & 0x80)    //符号位为1,说明温度小于0,需要根据补码求原码,显示负号
        {
            TEM = ~TEM + 1;
            s = 1;
        }
        else
            s = 0;  //温度为正数,不显示负号,标志位为0
        TEM = TEM * 0.0625*10000;    //扩大10000倍,4位小数全部变成整数
        i = 0;
        //符号位
        if (s) SEG_DIS(++i, 20);
        //百位
        if (TEM < 1000000);
        else SEG_DIS(++i,TEM/1000000%10);
        //十位
        if (TEM < 100000);
        else SEG_DIS(++i,TEM/100000%10);
        //个位
        SEG_DIS(++i,TEM/10000%10);
        delay(1);       //延时是因为个位显示比较暗
        SEG_DIS(i,22);
        //第一位小数
        SEG_DIS(++i,TEM/1000%10);
        //倒数第3位小数
        if (TEM % 1000) SEG_DIS(++i,TEM/100%10);
        //倒数第2位小数
        if (TEM % 100) SEG_DIS(++i,TEM/10%10);
        //最后1位小数
        if (TEM%10) SEG_DIS(++i,TEM%10);
    }
}

/**********************************************************************************/
/*************************  DS18B20  *********************************************/
/**********************************************************************************/

    //DS18B20初始化
bit DSinit()
{
    bit i;
    DS = 1;
    _nop_();
    DS = 0;
    delay_us(73);  //11.95 + 6.5*x = 480,得x ≈ 73
    DS = 1;     //释放总线
    delay_us(8);  //等待60us, 11.95 + 6.5*x = 60,得x=8
    i = DS;
    delay_us(29);  //等待DS发送完存在信号,200us, 11.95+6.5*x=200,得x=29
    DS = 1;
    return (i);
}

    //DS18B20写时序,写一个字节
void DSWriteByte(uchar DAT)
{
    uchar i;
    DS = 1;
    for (i = 0; i < 8; i++)
    {
        DS = 0;
        _nop_();
        DS = DAT & 0x01;
        DAT >>= 1;
        delay_us(11);  //写0和写1都要求时长超过60us,11.95+6.5*x=80,->x=11
        DS = 1;     //释放总线,等待下一次传输
        _nop_();
    }
    DS = 1;
}

    //DS18B20读一个字节
uchar DSReadByte()
{
    uchar i;
    uchar j;
    uchar dat;
    DS = 1;
    for (i = 0; i < 8; i++)
    {
        DS = 0;
        _nop_();    //产生读时序
        DS = 1;     //释放总线
        delay5us(); //延时一会儿
        j = DS;
        delay_us(10);   //延时等待这次数据DS18B20发送完全
        DS = 1;
        _nop_();
        dat = (j<<7)|(dat>>1);	
    }
    return (dat);     //接收到的数据是从低位到高位,顺序调换一下
}

 4.5 现象

位数会随温度变化而变化,要测试负数,可以自己赋值s=1 

本次笔记对应视频教程的第40,,41集 数字温度传感器DS18B20(理论+实践),到此结束。

|

下次笔记将对应视频教程的第42,43集 红外通讯(理论+实践)

|

如果笔记之中有任何错误,请在评论区指出,谢谢

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值