弘毅 发表于 2011-5-26 21:14:04

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

原文地址: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—————— */
#defineRESET_18B201   //操作定义
#defineWRITE_18B20 2
#defineREAD_18B203
#defineSKIP_ROM0xCC
#defineWRITE_RAM0x4E
#defineREAD_RAM0xBE
#defineSTART_MEA0x44
#defineTEMP_12BIT0x7F
#defineRD18B20_PERIOD1000   // 18B20 读周期 ,1ms 计数
#defineRD18B20_COMMAND_NUM14// 一次操作的总命令数,13个命令
#defineRD18B20_START_DELAY10000// 启动延时,约1ms, 为避免别的中断导致未初始化完即到了。
#defineOUT_0_2us1      //位操作定义
#defineOUT_0_480us 2
#defineOUT_1_12us3
#defineOUT_1_70us4
#defineOUT_BIT_58us 5
#defineIN_BIT_58us6
#defineIN_BIT_410us7
#defineDELAY480us5308
#defineDELAY70us774
#defineDELAY410us4534
#defineDELAY58us575    // 52us 对应值,因为有指令造成的延时
#defineDELAY2us0    // 特殊处理
#defineDELAY12us10    // 高字节为零,则用循环延时

定义变量:

typedef union
{
unsigned int all;
unsigned char b;
}timer_val;
// ------------------18B20 处理用变量090511
unsigned intidata gc_uiRead18B20TimeCnt;// 18B20 数据读时间间隔计数
bit       g_bRead18B20;   // 启动 18B20 读处理
bit       g_b18B20Reading;    // 正在读18B20
// 复位 SkipROM 读命令 温度低字节 温度高字节复位SkipROM写命令 TH(0xFF) TL(0xFF) Config 复位 SkipROM 启动转换
Unsigned char codega_uc18B20Command =
{ 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;   // 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 =
{ 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;      
unsignedchar data g_uc18B20Command;// 操作命令
unsigned char data g_uc18B20RW_Data;// 读写字节缓冲
bit       g_b18B20_OK;    // 如果复位得到正确回应,则为真

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

/********************************************/
/*名称: Read18B20       */
/*用途: 启动18B20读处理,在PCA中断中完成 */
/********************************************/
// 因为18B20的读写时间较长 约10ms,而且位时序要球严格,所以安排在PCA中断中完成 090511
// 改用 PCA320100124
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 = CH;
g_ui18B20CtrlTime.b = CL;
if(g_ui18B20CtrlTime.b != CH)
{
g_ui18B20CtrlTime.b = CH;
g_ui18B20CtrlTime.b = CL;
}

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

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.b = g_uc18B20RW_Data;
      }
      else
      {
       ga_iTemperature.b = g_uc18B20RW_Data;
      }
      gi_uc18B20BytePtr--;
   }
   
   if(gc_uc18B20CommandCnt == 0)
   {
      // 完成一次读写处理
      g_b18B20Reading = false;
      g_b18B20SaveFirst = ~g_b18B20SaveFirst;//完成一次读,将另一个作为工作单元
      CCAPM3 = 0;          // 停止中断
   }
   else
   {
      // 命令处理
      g_uc18B20Command = ga_uc18B20Command;
      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;
      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++;
    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;
   CCAP3H = g_ui18B20CtrlTime.b;
    }

    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 = CH;   // 恢复控制时间
   g_ui18B20CtrlTime.b = CL;
   if(g_ui18B20CtrlTime.b != CH)
   {
      g_ui18B20CtrlTime.b = CH;
      g_ui18B20CtrlTime.b = CL;
   }
   
   g_ui18B20CtrlTime.all += DELAY58us;
   CCAP3L = g_ui18B20CtrlTime.b;
   CCAP3H = g_ui18B20CtrlTime.b;
    }
   
    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 = CH;   // 恢复控制时间
   g_ui18B20CtrlTime.b = CL;
   if(g_ui18B20CtrlTime.b != CH)
   {
      g_ui18B20CtrlTime.b = CH;
      g_ui18B20CtrlTime.b = CL;
   }
   
   g_ui18B20CtrlTime.all += DELAY58us;
   CCAP3L = g_ui18B20CtrlTime.b;
   CCAP3H = g_ui18B20CtrlTime.b;   
    }
   
    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;
   CCAP3H = g_ui18B20CtrlTime.b;
    }

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

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

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

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

Ansifa 发表于 2013-7-27 15:56:10

{:soso_e141:}STC用C语言去读18B20还是挺简单的。。。这个写的好复杂
页: [1]
查看完整版本: 18B20温度传感器读写探讨(续: 程序)(转)