搜索
bottom↓
回复: 31

18B20测温—只需关中断60us + 多路同时采样的实现

  [复制链接]

出0入0汤圆

发表于 2012-3-31 22:07:57 | 显示全部楼层 |阅读模式
本帖最后由 nicksean 于 2012-4-1 21:15 编辑

最近在两个小项目中用了18B20测温,其中一个项目要输出1.8K的脉冲并且脉冲数可控,用于驱动步进电机驱动器移动指定的距离。脉冲输出用了定时器中断,最开始的做法是测温时关中断,但是发现输出的脉冲不稳定。我用小喇叭接脉冲输出端,可以听到很明显的中断。后来在一本书上《MSP430系列单片机系统工程设计与实践》受到启发,仔细看了18B20的读写时序。发现只需要在几个关键时间点上关中断就可以保证单总线的读写时序要求。

1 初始化总线时序

手册上只给出了单片机拉低总线的最短时间480us,注意,这里并未给出最大时间,所以在这480us 时间内不必关中断,即使被中断了,也只是延长了拉低时间而已。
下一段是单片机释放总线到18B20拉低总线作为响应的时间,15~60us。这段是要关中断了(其实如果中断函数执行时间非常短,比如3us,不关中断也是可以的)。
再下一段是等待18B20释放总线,同样不必关中断。

2 读总线
读总线的最小时间60us,其实只要单片机采样之后就可以开中断了,剩下的时间就没有那么严格的要求了

3 写总线
写总线的最小时间同样是60us,为了保证18B20能读到正确数据,这段时间都要关中断

综上所述,其实在总线操作过程中,关中断的最长时间是60us其余时间都不必关中断。这对于要进行数码管动态扫描这样实时性较高的应用还是可以满足要求的。

代码实现
//STC12C5A60S2  推挽输出, 11.0592MHz 单周期时钟

#ifndef NOP
#define NOP(x)              _nop_()
#endif

#define _PIN_HI(port, bit)       (_SET_BIT(port, bit))
#define _PINS_HI(port, bit)       (_SET_BITS(port, bit))

#define _PIN_LO(port, bit)       (_CLR_BIT(port, bit))
#define _PINS_LO(port, bit)       (_CLR_BITS(port, bit))

#define _PIN_GT(port, bit)       (NOP(), _GET_BIT(port, bit))
#define _PINS_GT(port, bit)       (NOP(), _GET_BITS(port, bit))

#define _PIN_FLP(port, bit)      (_FLP_BIT(port, bit))
#define _PINS_FLP(port, bit)      (_FLP_BITS(port, bit))



#define _PxM0(port)             __ID_COMBINE(port, M0)
#define _PxM1(port)             __ID_COMBINE(port, M1)

//推挽输出
#define _PIN_OUT(port, bit)      (_CLR_BIT(_PxM1(port), bit),   _SET_BIT(_PxM0(port), bit))
#define _PINS_OUT(port, bits)    (_CLR_BITS(_PxM1(port), bits), _SET_BITS(_PxM0(port), bits))
//高阻输入
#define _PIN_IN(port, bit)       (_SET_BIT(_PxM1(port), bit),   _CLR_BIT(_PxM0(port), bit))
#define _PINS_IN(port, bits)     (_SET_BITS(_PxM1(port), bits), _CLR_BITS(_PxM0(port), bits))
//准双向(弱上拉,即普通51方式)
#define _PIN_IN_PH(port, bit)    (_CLR_BIT(_PxM1(port), bit),   _CLR_BIT(_PxM0(port), bit))
#define _PINS_IN_PH(port, bits)  (_CLR_BITS(_PxM1(port), bits), _CLR_BITS(_PxM0(port), bits))
//开漏
#define _PIN_OUT_OD(port, bit)   (_SET_BIT(_PxM1(port), bit),  _SET_BIT(_PxM0(port), bit))
#define _PINS_OUT_OD(port, bits) (_SET_BIT(_PxM1(port), bits), _SET_BIT(_PxM0(port), bits))

//数组大小
#define _ARRAY_SIZE(a)    ((sizeof(a)) / (sizeof(a[0])))

#define _18B20_ERROR_H        (0x7F)
#define _18B20_ERROR_L        (0x7E)

#define _CHANNEL_NUM        2          //这个定义的在同一个端口上连接18B20 的个数2个
#define _PORT_MASK          0x06       //这个定义的是在端口上哪几个位连接的18B20


void OneWire_Delay2us(BYTE n)        //@11.0592MHz STC fast clock
{   
    while(n--)
    {
        NOP();        NOP();        NOP();        NOP();        NOP();
        NOP();
    };
}

void bitTest_n1Wire(BYTE in, SBYTE* pBuf)
{
    BYTE ch;
    BYTE chMsk;

    if(pBuf)
    {
        for(ch = 0, chMsk = 0x01; chMsk && ch < _CHANNEL_NUM; chMsk <<= 1)
        {
            if(chMsk & _PORT_MASK)  //说明是有效数据位
            {
                pBuf[ch] = (in & chMsk)? _18B20_ERROR_H : _18B20_ERROR_L;
                ++ch;
            }
        }
    }
}

BYTE init_n1Wire()
{
    BYTE ret = 0;

    _PINS_LO(P1, _PORT_MASK);
    _PINS_OUT(P1, _PORT_MASK);
    OneWire_Delay2us(240);  // >= 480 us

    EA = 0;
    _PINS_IN(P1, _PORT_MASK);
    OneWire_Delay2us(30);   //15~60 us
    OneWire_Delay2us(10);   //
    ret = _PINS_GT(P1, _PORT_MASK);
    EA = 1;

    OneWire_Delay2us(240);

    return ret;
}

BYTE bitRead_n1Wire()
{
    BYTE ret = 0;

    EA = 0;
    _PINS_LO(P1, _PORT_MASK);
    _PINS_OUT(P1, _PORT_MASK);
    OneWire_Delay2us(1);  // >= 1 us
    _PINS_IN(P1, _PORT_MASK);
    OneWire_Delay2us(3);  // T rc
    ret = _PINS_GT(P1, _PORT_MASK);
    EA = 1;

    OneWire_Delay2us(25); // total >= 60 us

    return ret;
}

BYTE lineRead_n1Wire()
{
    BYTE ret = 0;

    EA = 0;
    _PINS_IN(P1, _PORT_MASK);
    OneWire_Delay2us(5);  // >= 1 us
    ret = _PINS_GT(P1, _PORT_MASK);
    EA = 1;

    return ret;
}

void bitWrite_n1Wire(BYTE bits)
{
    EA = 0;
    _PINS_LO(P1, _PORT_MASK);
    _PINS_OUT(P1, _PORT_MASK);
    OneWire_Delay2us(1);  // >= 1 us
    _CLR_BITS(P1, _PORT_MASK);
    _SET_BITS(P1, bits & _PORT_MASK);
    EA = 1;

    OneWire_Delay2us(14); // >= 14 us, <= 120 us
    _PINS_IN(P1, _PORT_MASK);
    OneWire_Delay2us(7);  // T rc
}


void writeByte_n1Wire(BYTE val)
{
    BYTE msk;
    for(msk = 0x01; msk; msk <<= 1)
    {
        bitWrite_n1Wire((val & msk)? _PORT_MASK : 0x00);
    }
}

void readByte_n1Wire(BYTE* pBuf)
{
    BYTE ch;
    BYTE msk;
    BYTE chMsk;
    BYTE col;      

    if(!pBuf)
    {
        return;
    }

    for(msk = 0x01; msk; msk <<= 1) //列数据掩码
    {
        col = bitRead_n1Wire();        //读出一列数据,一列指的是多个单总线构成的单个位数据的集合
        for(ch = 0, chMsk = 0x01; chMsk && ch < _CHANNEL_NUM; chMsk <<= 1)
        {
            if(chMsk & _PORT_MASK)  //说明是有效数据位
            {
                pBuf[ch] |= (col & chMsk)? msk : 0;
                ++ch;
            }
        }
    }

}

void measure_n1Wire()
{
    init_n1Wire();

    writeByte_n1Wire(0xCC);
    writeByte_n1Wire(0x44);
}

void readTemperature_n1Wire(SWORD temperatures[_CHANNEL_NUM])
{
    BYTE index;
    BYTE init;
    BYTE res[2][_CHANNEL_NUM] = {0}; // 2 means two values

    init = lineRead_n1Wire();
    bitTest_n1Wire(init, temperatures);     //检测与地短路的情况,若短路,temperatures中的值为_18B20_ERROR_L否则为_18B20_ERROR_H

    init = init_n1Wire();

    writeByte_n1Wire(0xCC);
    writeByte_n1Wire(0xBE);

    readByte_n1Wire(res[0]);                // low byte
    readByte_n1Wire(res[1]);                // high byte

    for(index = 0; index < _CHANNEL_NUM; ++index)
    {
        if(temperatures[index] != _18B20_ERROR_L)                   //对地短路错误
     {
            if(res[0][index] == 0xFF && res[1][index] == 0xFF)          //对正短路错误
        {
                temperatures[index] = _18B20_ERROR_H;               //置错误标志
        }
            else
            {
                temperatures[index] = (SBYTE)((res[1][index] << 4) | (res[0][index] >> 4));
            }
        }
    }
}


static volatile signed short idata s_tempBuf[_CHANNEL_NUM] = {0};          //保存温度测量值

void main()
{
    unsigned short cntr1= 0;
    unsigned short cntr2 = 0;

    while(1)
    {
        ++cntr1;
        if(cntr1 == 0)
        {
             ++cntr2;
             if(cntr2 == 0)
             {
                   measure_n1Wire();
             }
             else if(cntr2 == 0x7FFF)
             {
                   readTemperature_n1Wire(s_tempBuf);
             }
        }
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2012-3-31 22:10:19 | 显示全部楼层
收藏mark

出0入0汤圆

 楼主| 发表于 2012-3-31 22:10:59 | 显示全部楼层
附上电路图,VCC = 5V

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2012-3-31 22:17:05 | 显示全部楼层
1楼可真够快的。main() 函数是我临时编的,嘿嘿嘿。测温分两步:测量和读取

出0入0汤圆

发表于 2012-4-1 06:48:20 | 显示全部楼层
好资料。可惜看不到图片哦

出0入0汤圆

发表于 2012-4-1 09:15:40 来自手机 | 显示全部楼层
位写也不用关中断60us,
CPU送数据后就可以恢复中断,
总的来说只需要在每次位的读、写时关中断几us

出0入0汤圆

发表于 2012-4-1 09:29:28 | 显示全部楼层
值得参考,刚好要8路以上同时采集绘曲线    同时用8个IO口同时读取操作也是不错的

出0入0汤圆

 楼主| 发表于 2012-4-1 12:37:45 | 显示全部楼层
请6 楼说得再详细些吧。位读该怎么处理呢?

出0入0汤圆

发表于 2012-4-1 12:44:09 | 显示全部楼层
本帖最后由 BXAK 于 2012-4-1 12:45 编辑
nicksean 发表于 2012-4-1 12:37
请6 楼说得再详细些吧。位读该怎么处理呢?
  1. /*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
  2. 从DS18B20读1Byte数据
  3. ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
  4. uint8 DS18B20_ReadByte(void)
  5. {
  6.     uint8 i;
  7.     uint8 dat = 0;

  8.     for(i=0;i<8;i++)            
  9.     {       
  10.             #if( USE_INTER  )  
  11.             EA=0;
  12.             #endif

  13.         DQ = 0;                    //开始时间片
  14.         YSus(1);                   //(1us < 保持低 )
  15.         DQ = 1;                    //恢复总线高电平,准备接收
  16.                 dat >>= 1;
  17.         if( DQ ) dat |= 0x80;      //读取数据 (『开始时间片~读取数据 』< 15us)

  18.                 #if( USE_INTER  )
  19.             EA=1;
  20.             #endif

  21.         YSus(30);                  //等待读取1Bit据时间结束( >= 60us )
  22.     }

  23.     return dat;
  24. }

  25. /*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
  26. 函数:向DS18B20写1Byte数据
  27. ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
  28. void DS18B20_WriteByte(uint8 dat)
  29. {
  30.     uint8 i;

  31.     for(i=0;i<8;i++)            
  32.     {
  33.         #if( USE_INTER  )  
  34.             EA = 0;
  35.             #endif

  36.                 DQ = 0;                     //开始时间片
  37.         YSus(1);                    //(1us < 保持低)
  38.         DQ = (bit)(dat & 0x01);                //传送1位数据

  39.                 #if( USE_INTER  )
  40.             EA = 1;
  41.             #endif

  42.                 dat >>= 1;                  //下一位数据准备
  43.         YSus(30);                   //等待超过1Bit采样时间( >= 60us )
  44.         DQ = 1;                     //恢复总线高电平
  45.                 YSus(1);                    //(1us < 间隔 < ∞)
  46.     }
  47. }
复制代码

出0入0汤圆

 楼主| 发表于 2012-4-1 12:57:00 | 显示全部楼层
谢谢!我再回去研究研究

出0入0汤圆

发表于 2012-4-1 13:03:34 | 显示全部楼层
抛砖引玉,不错。学习一下

出0入0汤圆

发表于 2012-4-1 13:49:39 | 显示全部楼层
学习楼主的方法

出0入0汤圆

发表于 2012-4-1 13:52:41 | 显示全部楼层
感觉这个更是内置了1wire总线控制器,就是不会使用。。。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

 楼主| 发表于 2012-4-1 21:20:44 | 显示全部楼层
我改了代码,不知7楼如何粘贴的代码,这么整齐?

出0入0汤圆

发表于 2012-4-2 00:32:28 | 显示全部楼层
   你可以压缩成文件再发上来

出0入0汤圆

发表于 2012-4-2 00:33:38 | 显示全部楼层
还有最重要的注释没写!!!!!!!!!!!!

出0入0汤圆

发表于 2012-4-2 00:36:39 | 显示全部楼层
     不过模块化编程还不错吧

出0入0汤圆

发表于 2012-4-2 08:10:36 | 显示全部楼层
nicksean 发表于 2012-4-1 21:20
我改了代码,不知7楼如何粘贴的代码,这么整齐?

编辑时选着工具栏中的“<>”添加代码文字 按钮,复制粘贴代码就可以

出0入0汤圆

发表于 2012-4-2 09:44:45 来自手机 | 显示全部楼层
只是想马克一下子

出0入0汤圆

发表于 2012-5-23 09:37:05 | 显示全部楼层
这个方法超级NB啊O(∩_∩)O~

出0入0汤圆

发表于 2012-5-23 10:40:02 | 显示全部楼层
的确是好文章

出0入0汤圆

发表于 2012-5-24 22:27:03 | 显示全部楼层
记号

出0入0汤圆

发表于 2012-5-25 09:42:50 | 显示全部楼层
MARK    收藏

出0入0汤圆

发表于 2012-6-5 09:34:11 | 显示全部楼层
记号   

出0入0汤圆

发表于 2012-8-20 14:19:37 | 显示全部楼层
本帖最后由 yuanjie 于 2012-8-20 14:22 编辑

跟据DS1820时序,直接用CPU读取会用到大量延时,白白浪费CPU时间.
不如用CPLD做控制器,这样会大大减轻CPU时间.CPU可以像SRAM一样直接读取CPLD内部已转化的温度数据.
这样在CPLD内部可以做多个DS1820控制器.
CPLD可以用EPM240,我做过这个硬件程序.如果有需要的我可以公开发布!
EMAIL:TL_YUAN@163.com

出0入0汤圆

发表于 2012-8-20 14:33:17 | 显示全部楼层
很不错奥

出0入0汤圆

发表于 2014-4-16 10:58:55 | 显示全部楼层
Mark一记,以前写过就一个没OS的能运行,后面加了RTX不能运行,就是因为时间片切换导致时序出问题的原因。

出0入0汤圆

发表于 2014-4-16 11:18:55 | 显示全部楼层
好资料,真的不错。

出0入0汤圆

发表于 2016-1-13 18:27:33 | 显示全部楼层
刚要进行8路同时采集,程序写了一多半,不知道是否可行,看到帖子,放心多了。

出0入0汤圆

发表于 2016-1-13 22:40:37 | 显示全部楼层
好资料,学习学习

出0入0汤圆

发表于 2016-1-19 11:25:05 | 显示全部楼层
yuanjie 发表于 2012-8-20 14:19
**** 作者被禁止或删除 内容自动屏蔽 ****

表示感兴趣,可否公开,tigeroser@163.com

出0入0汤圆

发表于 2016-1-19 13:16:40 | 显示全部楼层
谢谢楼主分享!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-1 18:41

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表