极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 13576|回复: 1

18B20温度传感器读写探讨(续: 程序)(转)

[复制链接]
发表于 2011-5-26 21:14:04 | 显示全部楼层 |阅读模式
原文地址:http://www.embedream.com/bjzm/2010-06-08/93.html
18B20温度传感器读写探讨”一文发表后,有不少读者来咨询、交流,为了大家能进一步加深对18B20的理解和掌握,并通过这个尝试提高编程水平,我将自己所写的程序公布,希望能起到抛砖引玉的作用。
       前文详细分析了程序设计的需求、构思,此处不再赘述。
       我所用的MCU是STC12LE5610AD,属于改进型51单片机,其主要优势是速度和内置PCA,以及I/O口的模式设置,这三个特征程序中均用到了。因为18B20属于单总线,所以必须使用OC门驱动,STC12LE5610AD的I/O口可以设置成开漏模式(实际上,目前新型单片机均支持I/O口的模式设置,ARM更是如此)。
       根据前文所述,18B20整个读写周期还是比较长的,而且对时序要求极严格,所以如果不用中断方式实现,只能关闭其它中断,专做此事,似乎在嵌入式系统中基本不允许,特别是实时控制类的应用,因为必然有一些随机任务需要响应。
        为此,我做了尝试:用PCA的定时功能来控制18B20的读写时序,通过PCA的中断完成读写。

      具体如下:
      首先,根据程序的需要定义所用常数:

/* —————— 18B20常数定义  090511—————— */
#define  RESET_18B20  1   //  操作定义
#define  WRITE_18B20 2
#define  READ_18B20  3
#define  SKIP_ROM  0xCC
#define  WRITE_RAM  0x4E
#define  READ_RAM  0xBE
#define  START_MEA  0x44
#define  TEMP_12BIT  0x7F
#define  RD18B20_PERIOD  1000   // 18B20 读周期 ,1ms 计数
#define  RD18B20_COMMAND_NUM  14  // 一次操作的总命令数,13个命令
#define  RD18B20_START_DELAY  10000  // 启动延时,约1ms, 为避免别的中断导致未初始化完即到了。
#define  OUT_0_2us  1      //位操作定义
#define  OUT_0_480us 2
#define  OUT_1_12us  3
#define  OUT_1_70us  4
#define  OUT_BIT_58us 5
#define  IN_BIT_58us  6
#define  IN_BIT_410us  7
#define  DELAY480us  5308  
#define  DELAY70us  774
#define  DELAY410us  4534
#define  DELAY58us  575    // 52us 对应值,因为有指令造成的延时
#define  DELAY2us  0    // 特殊处理
#define  DELAY12us  10    // 高字节为零,则用循环延时

定义变量:

typedef union
{
unsigned int all;
unsigned char b[2];
}timer_val;
// ------------------18B20 处理用变量  090511
unsigned int  idata gc_uiRead18B20TimeCnt;  // 18B20 数据读时间间隔计数
bit       g_bRead18B20;     // 启动 18B20 读处理
bit       g_b18B20Reading;    // 正在读18B20
// 复位 SkipROM 读命令 温度低字节 温度高字节  复位  SkipROM  写命令 TH(0xFF) TL(0xFF) Config 复位 SkipROM 启动转换
Unsigned char code  ga_uc18B20Command[23] =
{ RESET_18B20, WRITE_18B20,SKIP_ROM, WRITE_18B20,READ_RAM, READ_18B20, READ_18B20, RESET_18B20, WRITE_18B20,SKIP_ROM, WRITE_18B20, WRITE_RAM, WRITE_18B20, 0x00,WRITE_18B20, 0x00,WRITE_18B20, TEMP_12BIT, RESET_18B20,WRITE_18B20, SKIP_ROM,WRITE_18B20,START_MEA};
unsigned char data gc_uc18B20CommandCnt;    // 18B20 命令处理计数器
unsigned char data gi_uc18B20CommandPtr;    // 18B20 取命令指针
timer_val idata ga_iTemperature[2];     // 18B20 温度值 , 用两个单元保存,存2次数据,以免读错误
bit     g_b18B20SaveFirst;     // 保存标志,用此方式提高中断中处理的速度(似乎数组处理偏慢)
unsigned char data gi_uc18B20BytePtr; // 高低字节存放指针
unsigned char data gc_uc18B20BitCnt; // 处理字节的位计数器
unsigned char data gc_uc18B20BitStatCnt;  // 位处理中的时间状态计数
timer_val    data g_ui18B20CtrlTime;   // 18B20 用的比较时间寄存器
timer_val   data g_ui18B20TimeBuf;   // 延时用缓冲单元
unsigned char code ga_uc18B20BitOp[9] =
{ OUT_0_480us, OUT_1_70us,IN_BIT_410us,  OUT_0_2us,OUT_BIT_58us,OUT_1_12us,OUT_0_2us, OUT_1_12us, IN_BIT_58us};
unsigned char data gi_ucBitOpPtr;      
unsigned  char data g_uc18B20Command;  // 操作命令
unsigned char data g_uc18B20RW_Data;  // 读写字节缓冲
bit       g_b18B20_OK;    // 如果复位得到正确回应,则为真

通过以下函数启动读写过程:

/********************************************/
/*名称: Read18B20       */
/*用途: 启动18B20读处理,在PCA中断中完成 */
/********************************************/
// 因为18B20的读写时间较长 约10ms,而且位时序要球严格,所以安排在PCA中断中完成 090511
// 改用 PCA3  20100124
void Read18B20(void)
{
gc_uc18B20CommandCnt = RD18B20_COMMAND_NUM;     
gi_uc18B20CommandPtr = 0;            
gc_uc18B20BitCnt  = 0;
gc_uc18B20BitStatCnt = 0;
gi_uc18B20BytePtr = 1;     // 因为 18B20 是先低后高存放
g_uc18B20Command = RESET_18B20;  // 避免误操作

g_ui18B20CtrlTime.b[0] = CH;
g_ui18B20CtrlTime.b[1] = CL;
if(g_ui18B20CtrlTime.b[0] != CH)
{
  g_ui18B20CtrlTime.b[0] = CH;
  g_ui18B20CtrlTime.b[1] = CL;
}

g_ui18B20CtrlTime.all += RD18B20_START_DELAY;
CCAP3L = g_ui18B20CtrlTime.b[1];      // 加载比较值
CCAP3H = g_ui18B20CtrlTime.b[0];

CCAPM3 = EnCMP_C|EnMAT_C|EnCCFI_C;    // PCA 的模块 3 用于控制18B20读时序,计时器模式,比较、匹配中断,100124

g_b18B20Reading = true;        // 建立正在读写处理标志
}

PCA中断处理程序:

  if(g_b18B20Reading)
  {
   // 18B20 处理 20100124
   if(gc_uc18B20BitStatCnt == 0)
   {
    if(gc_uc18B20BitCnt == 0)
    {
     if(g_uc18B20Command == READ_18B20)
     {
      // 保存所读的数据
      if(g_b18B20SaveFirst)
      {
       ga_iTemperature[0].b[gi_uc18B20BytePtr] = g_uc18B20RW_Data;
      }
      else
      {
       ga_iTemperature[1].b[gi_uc18B20BytePtr] = g_uc18B20RW_Data;
      }
      gi_uc18B20BytePtr--;
     }
     
     if(gc_uc18B20CommandCnt == 0)
     {
      // 完成一次读写处理
      g_b18B20Reading = false;
      g_b18B20SaveFirst = ~g_b18B20SaveFirst;  //完成一次读,将另一个作为工作单元
      CCAPM3 = 0;          // 停止中断
     }
     else
     {
      // 命令处理
      g_uc18B20Command = ga_uc18B20Command[gi_uc18B20CommandPtr];
      switch(g_uc18B20Command)
      {
       case RESET_18B20:
       {
        gc_uc18B20BitCnt = 0;   // 因复位操作发完三个状态后即结束,没有处理
        gc_uc18B20BitStatCnt = 3;
        gi_ucBitOpPtr = 0;
        break;
       }
      
       case WRITE_18B20:
       {
        gc_uc18B20BitCnt = 7;   // 因先操作后计数,所以 7 - 0 对应 8 位
        gc_uc18B20BitStatCnt = 3;
        gi_ucBitOpPtr = 3;
        gi_uc18B20CommandPtr++;  // 取要写的内容
        g_uc18B20RW_Data =
ga_uc18B20Command[gi_uc18B20CommandPtr];
        break;
       }
      
       case READ_18B20:
       {
        gc_uc18B20BitCnt = 7;   // 因先操作后计数,所以 7 - 0 对应 8 位
        gc_uc18B20BitStatCnt = 3;
        gi_ucBitOpPtr = 6;
        break;
       }
            
       default: break;
      }
      
      gc_uc18B20CommandCnt--;   // 命令计数
      gi_uc18B20CommandPtr++;   // 指向下一个命令
      CCF3 = true;       // 强制再次进入中断,处理位状态
     }   
    }
    else
    {
     // 下一位处理
     gc_uc18B20BitStatCnt = 3;    // 恢复位状态计数
     gi_ucBitOpPtr -=3;   
     
     gc_uc18B20BitCnt --;      // 位计数
     CCF3 = true;        // 强制再次进入中断,处理位状态
    }
   }
   else
   {
    //进入下一个状态处理
repeat:
    ucBitOp = ga_uc18B20BitOp[gi_ucBitOpPtr];

    gi_ucBitOpPtr++;
    gc_uc18B20BitStatCnt--;      // 状态计数

    // 位状态操作
    if(ucBitOp == OUT_0_2us)
    {
     g_b18B20_Data = 0;
     goto repeat;
    }
      
    if(ucBitOp == OUT_1_12us)
    {
     g_b18B20_Data = 1;
     
     //短延时,采用循环方式实现
     for(i=0; i<DELAY12us; i++)
     {
     }
     
     if(g_uc18B20Command == READ_18B20)
     {
      goto repeat;     // 读必须快处理
     }
     else
     {
      CCF3 = true;      // 强制再次进入中断,写无所谓,同时需要处理下一位   
     }
    }
   
    if(ucBitOp == OUT_1_70us)
    {
     g_b18B20_Data = 1;
     
     // 长延时,使用 PCA
     g_ui18B20CtrlTime.all += DELAY70us;
     CCAP3L = g_ui18B20CtrlTime.b[1];
     CCAP3H = g_ui18B20CtrlTime.b[0];
    }

    if(ucBitOp == OUT_BIT_58us)
    {
     if(g_uc18B20RW_Data&0x01)
     {
      g_b18B20_Data = 1;
     }
     else
     {
      g_b18B20_Data = 0;
     }
     g_uc18B20RW_Data >>= 1;
     
     // 长延时,使用 PCA
     
     g_ui18B20CtrlTime.b[0] = CH;   // 恢复控制时间
     g_ui18B20CtrlTime.b[1] = CL;
     if(g_ui18B20CtrlTime.b[0] != CH)
     {
      g_ui18B20CtrlTime.b[0] = CH;
      g_ui18B20CtrlTime.b[1] = CL;
     }
     
     g_ui18B20CtrlTime.all += DELAY58us;
     CCAP3L = g_ui18B20CtrlTime.b[1];
     CCAP3H = g_ui18B20CtrlTime.b[0];
    }
   
    if(ucBitOp == IN_BIT_58us)
    {
     g_uc18B20RW_Data >>= 1;
     
     if(g_b18B20_Data)
     {
      g_uc18B20RW_Data |= 0x80;   // 因为移位后高位填“0”,所以不用处理 g_b18B20_Data = 0
     }
     
     // 长延时,使用 PCA
     
     g_ui18B20CtrlTime.b[0] = CH;     // 恢复控制时间
     g_ui18B20CtrlTime.b[1] = CL;
     if(g_ui18B20CtrlTime.b[0] != CH)
     {
      g_ui18B20CtrlTime.b[0] = CH;
      g_ui18B20CtrlTime.b[1] = CL;
     }
     
     g_ui18B20CtrlTime.all += DELAY58us;
     CCAP3L = g_ui18B20CtrlTime.b[1];
     CCAP3H = g_ui18B20CtrlTime.b[0];     
    }
     
    if(ucBitOp == IN_BIT_410us)
    {
     if(g_b18B20_Data)
     {
      g_b18B20_OK = false;   // 因为是复位操作
     }
     else
     {
      g_b18B20_OK = true;
     }
     
     // 长延时,使用 PCA
     g_ui18B20CtrlTime.all += DELAY410us;
     CCAP3L = g_ui18B20CtrlTime.b[1];
     CCAP3H = g_ui18B20CtrlTime.b[0];
    }

    if(ucBitOp == OUT_0_480us)
    {
     g_b18B20_Data = 0;

     // 长延时,使用 PCA
     g_ui18B20CtrlTime.all += DELAY480us;
     CCAP3L = g_ui18B20CtrlTime.b[1];
     CCAP3H = g_ui18B20CtrlTime.b[0];
    }         
   }   
  }

       以上就是中断读写18B20的全部程序,MCU所用晶振是 22.1184MHz。
       因为是C语言编写,所以应该可以很容易移植到别的MCU上,只要速度够,有PCA,且I/O口支持开漏输出。如目前流行的STM32,我想应该可以。

       本程序也只是个尝试,肯定有不完善之处,期待大家改善之。
南京嵌入之梦工作室
2010年6月8日星期二

回复

使用道具 举报

发表于 2013-7-27 15:56:10 | 显示全部楼层
{:soso_e141:}STC用C语言去读18B20还是挺简单的。。。这个写的好复杂
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-4 05:43 , Processed in 0.037887 second(s), 18 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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