Hanker

18B20温度传感器读写探讨(续: 程序)—— 采用中断方式实现读写之程序

0
阅读(4308)

[提要] 本文为“18B20温度传感器读写探讨”一文之续,将前文所构思的方法具体实现的程序公布,以期抛砖引玉。


        “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日星期二