DS18B20温度显示程序(附录每一步的详细解释)

2020-01-22 12:07发布

网上摘录的DS18B20程序,里面有点小问题,修改后再加详细备注。


  #include<reg51.h>
  #include<intrins.h>
  #include <math.H>  //要用到取绝对值函数abs()
  #define uchar unsigned char
  #define uint unsigned int
  //通过DS18B20测试当前环境温度, 并通过数码管显示当前温度值, 目前显示范围: -55~ +125度
  sbit wela = P2^7;  //数码管位选
  sbit dula = P2^6;  //数码管段选
  sbit ds = P2^2;         //ds18b20总线端口
  int tempValue;         //整型温度值
  uchar const timeCount = 4; //动态扫描的时间间隔
   
//0-F数码管的编码(共阴极)
uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//0-9数码管的编码(共阴极), 带小数点
uchar code tableWithDot[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//延时1ms函数。 对于12MHz时钟, 例i=10,则大概延时10ms.
void delay(uchar i)
{
  uint j;
               while(i--)
            {
        for(j = 0; j < 122; j++);
            }
}

//初始化DS18B20
//让DS18B20一段相对长时间低电平, 然后一段相对非常短时间高电平, 即可启动
void dsInit()
{
        //12MHz时钟, unsigned int型的i, 作一个i++操作的时间为3us,i--为4us
        uint i;
        ds = 0;
    i = 100;   //拉低约800us, 符合协议要求的480us以上(400-960)
    while(i>0)
        i--;
    ds = 1;    //产生一个上升沿,释放总线, 进入等待应答状态 ( 15-60 )
    i = 4;
    while(i>0)
        i--;
}

void dsWait()
{
    uint i;
    while(ds);
    while(~ds);  //等待应答脉冲 (DS18B20发出60-240us存在低脉冲)
    i=4;
    while(i>0)
        i--;
}

//向DS18B20读取一位数据
//读一位, 让DS18B20一小周期低电平, 然后两小周期高电平,
//之后DS18B20则会输出持续一段时间的一位数据
bit readBit()
{
   uint i;
   bit b;
   ds=0;
   i++;   //延时约8us, 符合协议要求至少保持1us
   ds=1;
   i++;
   i++;  //延时约16us, 符合协议要求的至少延时15us以上
   b=ds;
   i=8;
   while(i>0)
   i--;  //延时约64us, 符合读时隙不低于60us要求
   return b;
}

//读取一字节数据, 通过调用readBit()来实现
uchar readByte()
{
   uint i;
   uchar j, dat;
   dat = 0;
   for(i=0;i<8;i++)
   {
       j = readBit();
       //最先读出的是最低位数据
       dat=(j<<7)|(dat>>1);
    }
    return dat;
}

//向DS18B20写入一字节数据
void writeByte(uchar dat)
{
        uint i;
        uchar j;
    bit b;
    for(j = 0; j < 8; j++)
    {
       b = dat & 0x01;
       dat >>= 1;
       //写"1", 将DQ拉低15us后, 在15us~60us内将DQ拉高, 即完成写1
       if(b)
       {
            ds = 0;
            i++;
                        i++;  //拉低约16us, 符号要求15~60us内
            ds = 1;  
            i = 8;
                        while(i>0)
                        i--;  //延时约64us, 符合写时隙不低于60us要求
       }
       else  //写"0", 将DQ拉低60us~120us
            ds = 0;
            i = 8;
                        while(i>0)
                        i--;  //拉低约64us, 符号要求
            ds = 1;
                        i++;
                        i++;  //整个写0时隙过程已经超过60us, 这里就不用像写1那样, 再延时64us了
     
    }
}

//向DS18B20发送温度转换命令
void sendChangeCmd()
{
   dsInit();    //初始化DS18B20, 无论什么命令, 首先都要发起初始化
   dsWait();   //等待DS18B20应答
   delay(1);    //延时1ms, 因为DS18B20会拉低DQ 60~240us作为应答信号
   writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
   writeByte(0x44); //写入温度转换命令字 Convert T
}

//向DS18B20发送读取数据命令
void sendReadCmd()
{
   dsInit();
   dsWait();
   delay(1);
   writeByte(0xcc); //写入跳过序列号命令字 Skip Rom
   writeByte(0xbe); //写入读取数据令字 Read Scratchpad
}

//获取当前温度值
int getTmpValue()
{
        uint tmpvalue;
    int value; //存放温度数值
    float t;
    uchar low, high;
    sendReadCmd();
    //连续读取两个字节数据
    low = readByte();
    high = readByte();
    //将高低两个字节合成一个整形变量
    //计算机中对于负数是利用补码来表示的
    //若是负值, 读取出来的数值是用补码表示的, 可直接赋值给int型的value
    tmpvalue = high;
    tmpvalue <<= 8;
    tmpvalue |= low;
    value=tmpvalue;

    //使用DS18B20的默认分辨率12位, 精确度为0.0625度, 即读回数据的最低位代表0.0625度
    t=value*0.0625;
    //将它放大100倍, 使显示时可显示小数点后两位, 并对小数点后第三进行4舍5入
    //如t=11.0625, 进行计数后, 得到value = 1106, 即11.06 度
    //如t=-11.0625, 进行计数后, 得到value = -1106, 即-11.06 度
    value=t*100+(value>0?0.5:-0.5); //大于0加0.5, 小于0减0.5
    return value;
}

//显示当前温度值, 精确到小数点后一位
//若先位选再段选, 由于IO口默认输出高电平, 所以当先位选会使数码管出现乱码
void display(int v)
{
   uchar count;
   uchar datas[] = {0, 0, 0, 0, 0};
   uint tmp = abs(v);     //abs取绝对值函数,包含在math中
   datas[0] = tmp / 10000;
   datas[1] = tmp % 10000 / 1000;
   datas[2] = tmp % 1000 / 100;
   datas[3] = tmp % 100 / 10;
   datas[4] = tmp % 10;

   //显示“-”号
   if(v<0)                  
   {
       P0 = 0xff; //关位选, 去除对上一位的影响
       wela = 1; //打开锁存, 给它一个下降沿量
       wela = 0;
       //段选
       P0 = 0x40; //显示"-"号
       dula = 1;  //打开锁存, 给它一个下降沿量
       dula = 0;

       //位选
       P0 = 0xfe;
       wela = 1; //打开锁存, 给它一个下降沿量
       wela = 0;
       delay(timeCount);
   }

   //显示数值
        for(count=0;count !=5;count++)
        {
        //关位选, 去除对上一位的影响
       P0 = 0xff;
       wela = 1; //打开锁存, 给它一个下降沿量
       wela = 0;
       //段选
       if(count != 2) //显示数字(除了带小数点那位)
       {   
                P0 = table[datas[count]];  //显示数字
       }
       else          // 显示带小数点那位数字
       {
            P0 = tableWithDot[datas[count]]; //显示带小数点数字
       }
       dula = 1;  //打开锁存, 给它一个下降沿量
       dula = 0;

       //位选
       P0 = _crol_(0xfd, count); //选择第(count + 1) 个数码管        (_crol_循环左移命令,包含在intrins中)
       wela = 1; //打开锁存, 给它一个下降沿量
       wela = 0;
       delay(timeCount);
    }
}

void main()
{
  uchar i;

   while(1)
    {
       //启动温度转换
       sendChangeCmd();
       //显示5次
        for(i = 0; i < 5; i++)
        {
           display(tempValue);
             }
        tempValue=getTmpValue();
    }
}


点击此处下载 ourdev_721004GC64H1.rar(文件大小:27K) (原文件名:51.rar)
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
7条回答
千年明月
1楼-- · 2020-01-22 13:27
 精彩回答 2  元偷偷看……
linux-0405209
2楼-- · 2020-01-22 15:11
是否支持读取单一接口多个DS18B20~~的数据,能加上就更好了
pandong
3楼-- · 2020-01-22 17:46
继续加油!
youstupy
4楼-- · 2020-01-22 18:33
不错,谢谢分享
bi大痣
5楼-- · 2020-01-22 19:24
linux-0405209 发表于 2014-9-28 09:33
是否支持读取单一接口多个DS18B20~~的数据,能加上就更好了

要是实现单总线,还得加个读取DS18B20序列号的程序呢!
zhan_li
6楼-- · 2020-01-22 23:45
 精彩回答 2  元偷偷看……

一周热门 更多>