打印

分享一个DS18B20测温的完整程序

[复制链接]
3282|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
leoright|  楼主 | 2008-12-13 11:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
//------------------------------------------------------------------------
//            
//                     DS18B20温度计C程序
//                     2008.12.8通过调试
//------------------------------------------------------------------------
//使用AT89C2051单片机,12MHZ晶振,用共阳LED数码管
//P1口输出段码,P3口扫描
//
#include "reg51.h"
#include "intrins.h"     //_nop_();延时函数用

#define  Disdata    P0   //段码输出口
#define  discan     P2   //扫描口
#define  uchar unsigned char
#define  uint  unsigned int    


sbit  DQ=P3^7;        //温度输入口
sbit  DIN=P0^7;       //LED小数点控制
int   TH;             //上限报警温度

//共阴LED段码表        "0"  "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "_"
uchar code dis_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x08};         
uchar code scan_con[4]={0xf7,0xfb,0xfd,0xfe};    //列扫描控制字
uchar data temp0,temp1;                            // 读出温度暂放
uchar data display[4]={0x00,0x00,0x00,0x00};//显示单元数据,共4个数据,一个运算暂存用


//------------------------------------------------------------------------
//函数的声明
//
void delay15us(uint t);                      //延时15us
void scan(void);                          //动态扫描数码管
void Reset_DQ(void);                      //DS18B20复位函数
void write_bit(uchar w_bit);              //写一bit数据函数
void write_byte(uchar w_byte);              //写一byte数据函数
int read_bit(void);                          //读出一bit数据
int read_byte(void);                      //读出一byte数据
void read_temp(void);                      //读出温度
void process_temp(void);                  //处理温度显示
void initTS(void);                          //初始化DS18B20
void dlms(void);                          //延时约10ms
void set_temp(void);                      //温度设置函数

//------------------------------------------------------------------------
//delay 15 us.
//
void delay15us(uint t)
{
    for(;t>0;t--)
    {
    //_nop_()函数少一个,或者多一个都会使占用CPU的资源增多。
    _nop_();_nop_();_nop_();_nop_(); 
    }
}

//------------------------------------------------------------------------
//Display And Scan module
//
void scan(void)
{
char loop;
    for(loop=0;loop<4;loop++)         //四位LED扫描控制
     {
      Disdata=dis_7[display][loop]];
      if(loop==2)
      {DIN=1;}
      else DIN=0;
      discan=scan_con[loop];
      delay15us(100);
      discan=0xff;
     }
 }

//------------------------------------------------------------------------
//Reset DS18B20
//
void Reset_DQ(void)
{
    DQ=1;_nop_();_nop_();
    DQ=0;
    delay15us(38);
    DQ=1;
    delay15us(3);
    DQ=0;
    delay15us(15);
    DQ=1;
}
//------------------------------------------------------------------------
//Send a 1-wire write bit.Probide 10us recovery time
//
void write_bit(uchar w_bit)
{

    if(w_bit)
    {
        //Write '1' bit
         DQ=0;
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        DQ=1;
        delay15us(4);
    }
    else
    {
        //Write '0' bit
         DQ=0;
        delay15us(4);
        DQ=1;
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
    }
}

//------------------------------------------------------------------------
//Write 1-Wire data byte
//
void write_byte(uchar w_byte)
{
    int loop;
    //Loop to write each int bit in the byte,Ls_bit first
    for(loop=0;loop<8;loop++)
    {
        write_bit(w_byte&0x01);
        //shift the data byte for the next bit
        w_byte>>=1;
    }

}
//------------------------------------------------------------------------
//Read a bit from the 1-Wire bus and return it.Probide 10us recovery time.
//
int read_bit(void)
{
     int result;
    DQ=0; //Drives DQ low
    _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
    DQ=1; //Rleases the bus
    _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
    _nop_();_nop_();_nop_();
    result=DQ;    //Samples the bit value from the slave
    delay15us(4); //Complete the time slot and 10us recovery

    return result;
}    
//------------------------------------------------------------------------
//Read 1-Wire data byte and return it.
//
int read_byte(void)
{
    int loop,result=0;
    for(loop=0;loop<8;loop++)
    {
         result>>=1;
        if(read_bit())
            result|=0x80;
    }
    return result;
}

//------------------------------------------------------------------------
//Read temperature 
//
void read_temp(void)
{
    Reset_DQ();               //总线复位
    write_byte(0xCC);         //由于只使用了一片DS18B20,发Skip ROM命令
    write_byte(0xBE);         //发读命令
    temp0=read_byte();      //温度低8位
    temp1=read_byte();      //温度高8位
//    TH=read_byte();            //读出上限告警温度
    Reset_DQ();
    write_byte(0xCC);         // Skip ROM
    write_byte(0x44);         // 发转换命令,温度转换以后750ms可以读出温度。

}
//------------------------------------------------------------------------
//Process temperature.Display temperature in the LEDs.
//数码管只有四位,常用测温范围为0-100,不考虑温度为负数和大于100情况,测温精
//度为0.5,分辨率是0.0625,如果温度出现负数,将会读出负的温度值,不过没有显示‘-’                                                                                                  
void process_temp(void)
{
    int tempL,tempH;
    int n;
    tempH=((temp0&0xf0)>>4)|((temp1&0x0f)<<4);               //温度的整数部分
//取测得温度的bit4~bit11位与TH和TL分别比较,如果大于TH或者小于TL,则S=1,即tempH>127                                                             
    if(tempH>127)
    {
        tempH=256-tempH;
        n=1;
    }
    display[3]=tempH/10;                                     //温度十位
    display[2]=tempH%10;                                     //温度个位
    
       
    tempL=temp0&0x0f;
    tempL=tempL*6.25;                                          //温度的小数部分
    display[1]=tempL/10;                                     //温度小数第一位
    display[0]=tempL%10;                                     //温度小数第二位

    //Compare with the TH
    if((tempH+(temp0&0x0f)*0.0625)>TH)
    {P3&=0X80;    delay15us(1000);}


    if(!display[3]){display[3]=0x0A;}                         //最高位为0时不显示 
}

//------------------------------------------------------------------------
//Initialize the DS18B20
//
void initTS(void)
{
    Reset_DQ();
    write_byte(0xcc);
    write_byte(0x4e);        //写温度限制指令
    write_byte(0x64);        //TH为100℃
    write_byte(0x80);        //TL为-10℃
    write_byte(0x7f);        //设置转换为12位,分辨率达到0.0625
//    Reset_DQ();
}

//------------------------------------------------------------------------
//Main Function
//
main()
{
    int h;
    Disdata=0xff;            //初始化端口
    discan=0xff;

    initTS();               //初始化DS18B20,开机时显示85.00℃
    while(1)
     {
     read_temp();           //读出18B20温度数据
      process_temp();        //处理温度数据 
      for(h=0;h<250;h++)
      {scan();}              //显示温度值约1.5秒,温度显示时间较长,对变化不是很敏感,但可
                             //以减少占用CPU的资源。
     
     }
                    
}

//*********************结束**************************//




                                                                                               




相关帖子

沙发
leoright|  楼主 | 2008-12-13 11:20 | 只看该作者

注释部分有点小错误

使用特权

评论回复
板凳
ayb_ice| | 2008-12-13 11:34 | 只看该作者

你这也是完整程序

搜索ID程序呢,这才是1WIRE的精华,难点。。。
再说程序基本都没有返回值,是否有器件在总线上,操作是否成功等都不知道,关键程序都没有禁止中断,实际使用中出错的可能很大。。。

使用特权

评论回复
地板
leoright|  楼主 | 2008-12-13 12:07 | 只看该作者

回复:你这也是完整程序

多谢指导!
新手嘛,犯错误是很正常的……

使用特权

评论回复
5
ayb_ice| | 2008-12-13 12:14 | 只看该作者

还有

tempH=((temp0&0xf0)>>4)|((temp1&0x0f)<<4);
这里最好不用>>4这种用法,因为tempH是有符号数,
命令也最好用#define的方法,因为0xcc代表什么你很快就忘记了,其它人更不知道代表什么,
if((tempH+(temp0&0x0f)*0.0625)>TH)这个地方完全可以不用浮点数操作,直接用除法效率更高....

使用特权

评论回复
6
leoright|  楼主 | 2008-12-13 17:31 | 只看该作者

正想修改

将tempH和tempL定义为int费事,占用了太多的代码空间,是应该修改的!
DS18B20的测温范围是-50℃到125℃,超出这个范围就不精确了,完全可以用无符号字符来定义tempH和tempL。
至于那个#define是否可以这样理解,你建议我将温度告警的标志进行预定义,这个也是很好的想法!最后一句“用除法的效率较高”不是很清楚,恳请细讲,谢谢!

使用特权

评论回复
7
garnettliu| | 2008-12-13 17:37 | 只看该作者

建议贴张时序图

但总线编程时序很重要,所以最好有张时序图。

使用特权

评论回复
8
leoright|  楼主 | 2008-12-13 17:42 | 只看该作者

好的!吃完饭回来贴,datasheet上有的!

使用特权

评论回复
9
ayb_ice| | 2008-12-13 18:04 | 只看该作者

*0.065 = /16

*0.0625是浮点数操作,/16是整数操作,你说哪个效率高.

使用特权

评论回复
10
leoright|  楼主 | 2008-12-13 21:38 | 只看该作者

怎么确认DS18B20是否在线!?

如果DS18B20不在线的话,复位应该是不成功的。
如何在reset_DQ中返回一个能知道DS18B20是否在线的值!?

使用特权

评论回复
11
原野之狼| | 2008-12-14 11:02 | 只看该作者

嗯,楼主还得努力~

ayb_ice说得对,临界段代码一定要做保护。

使用特权

评论回复
12
leoright|  楼主 | 2008-12-16 01:07 | 只看该作者

路漫漫其修远兮,吾将上下而求索……

今天和老师交流了下思路,确实挺糟糕的!
DS18B20转换温度需要大约350ms(10位),而单片机将温度传进来的时间是微妙级的,这样单片机得等待一段时间再来读数据,虽然单片机这段时间可以做其他的事情,但是这样读出来的数据就大约是1s前的温度。做一个简单的温度计当然没什么问题,不过因为后面还有控制恒温的部分,而处理温度的PID算法也需要一定的时间,再加上执行元件的延时,时间的延时会导致温度控制的精度不能做得很高。

使用特权

评论回复
13
SkyCode| | 2008-12-19 16:58 | 只看该作者

这样写程序当读18B20的时候LED会有闪动的

 我想这样做的程序
 while(1)
     {
     read_temp();           //读出18B20温度数据
      process_temp();        //处理温度数据 
      for(h=0;h<250;h++)
      {scan();}              //显示温度值约1.5秒,温度显示时间较长,对变化不是很敏感,但可
                             //以减少占用CPU的资源。
     
     }
由于读回18B20要很长的时间,人眼会明显的看到这个LED 多多少少会闪动

就解决的办法:
scan()函数要改写了  换定时器去扫描比较好点  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

24

帖子

0

粉丝