ENGLISH 简体中文 日本語 한국어  


应用笔记120

利用1-Wire主机通讯

摘要:本应用笔记从理论上逐步阐述了如何充分利用1-Wire®功能,并列举了一些示例。1-Wire主机已被设计到客户的ASIC芯片中,主控CPU用于读取1-Wire温度传感器的数值。所提供的源代码用于1WM对4个温度传感器发送温度转换指令,并提供转换温度。假设读者已经了解DS18B20温度传感器、DS1WM 1-Wire主机以及Dallas Semiconductor的1-Wire协议。

引言

DS1WM 1-Wire主机为单总线控制器,专为简化主控CPU与周边1-Wire器件之间的单总线通讯而设计,利用DS1WM不必考虑位时序。本应用笔记从理论上逐步阐述了如何充分利用1-Wire功能,并列举了一些示例。假设读者已经了解DS18B20温度传感器、DS1WM 1-Wire主机以及Dallas Semiconductor 1-Wire通讯协议,详细资料请参考:Book of iButton Standards (PDF), DS1WM数据资料, DS18B20数据资料, 应用笔记119:嵌入1-Wire主机

图1. 示例电路
图1. 示例电路

图1是以下示例所参考的电路配置,1-Wire主机已被设计到客户的ASIC芯片中,主控CPU通过它对4个DS18B20温度传感器发送温度转换命令,并读取转换的温度值。1-Wire总线和INTR均上拉一个5k电阻,50MHz的系统时钟通过CLK引脚为1-Wire主机提供时钟信号。1-Wire主机已被映射到CPU的端口地址存储器中。

概述

本示例的程序采用C语言编写,主程序为:GetTemperatures,它完成1-Wire主机的初始化,搜索1-Wire总线上的所有器件,并指定它们测量温度,然后将测量值送回CPU并存储。如果总线上没有1-Wire器件,则退出主程序并返回值“1”,否则,则返回值0。其它函数将在下面详细描述。程序中的常数BASE是1-Wire主机被映射到CPU地址空间中的地址。TEMPS和ROMS是全局变量。

//individual Serial #s and readings
#define DEVICES 4
int ROMS[DEVICES][8];
int TEMPS[DEVICES];

int GetTemperatures(int BASE)
{
  // init. the 1-Wire Master
  Initialize(BASE);
  // exit if no devices can be found
  if(Reset(BASE)) return(1);

  // find all individual Serial#s
  FindROMs(BASE);

  // Convert temp. and read devices
  ConvertT(BASE);
  ReadTemps(BASE);

  return(0);
}

运行程序

主控制器首先向时钟分频寄存器写入合适的控制字,使DS1WM得到正确的时序,初始化1-Wire主机。50MHz输入时钟对应的数字是:0x0Fh (见DS1WM数据资料)。主控制器初始化INTR是通过向中断使能寄存器写入0x3Dh实现的。这样,当有数据发送或复位完成后,INTR引脚将产生低电平中断请求信号。

void Initialize(int BASE)
{
  // Divide the clock
  outp(BASE+4,0x0F);

  // Generate INTs on reset and send
  outp(BASE+3,0x09);
}

主控制器必须确定1-Wire总线上是否有器件,为此,通过向命令寄存器写入0x01,产生一个复位信号,等待中断信号产生。收到中断信号后,CPU读取中断寄存器的值,如果第2位为低,则表明总线上有器件存在,否则,说明没有1-Wire器件或总线有故障,需采取适当措施。WaitforInterrupt()函数本文并未做定义,用户可以自行定义该等待程序,保证主程序执行需要完成的任务。

int Reset(int BASE)
{
  outp(BASE,0x02);  // send reset
  WaitforInterrupt();

  if(inp(BASE+2) & 0x02)
    return(1);   //no presence found
  else
    return(0);   //presence found
}
主控制器必须获知1-Wire总线上每个器件的ROM序列号,通过运行Search ROM算法获得1-Wire器件的序列号。主机向从机发送Search ROM命令,使1-Wire主机进入搜索模式;然后,主机基于前一次搜索读到的ROM代码发送16位搜索值,第一次运行时,该搜索值为0x00,返回的16位值包含新搜索到的ROM代码,用于产生下次搜索所需参数。

重复搜索过程,直到发现相同的序列号为止。本示例中查找到4个器件,由于存储空间只分配存储4个ROM码,因此只需4次就能完成该循环。RecoverROM函数将产生新的发送数据,并从最近收到的数据中提取新的ROM代码。完整的RecoverROM函数附在本应用笔记后,搜索程序的详细描述见DS1WM数据资料。

int FindROMs(int BASE)
{
  int loop;
  int dev;
  int TData[16];
  int RData[16];

  // reset RecoverROM and generate the
  // starting TData
  RecoverROM(NULL,TData,NULL);

  //run once for each device
  for(dev=0;dev<4;dev++)
  {
    outp(BASE,0x01);   // send reset
    WaitforInterrupt();
    outp(BASE+1,0xF0);   // send SeachROM
    WaitforInterrupt();

    // enter Accelerator mode
    outp(BASE,0x02);

    // transmit the TDATA and receive
    // the RDATA.
    for(loop=0;loop<16;loop++)
    {
      outp(BASE+1,TData[loop]);
      WaitforInterrupt();
      inp(BASE+1,RData[loop]);
    }

    //decode recovered ROM and generate
    //next Search value
    RecoverROM(RDATA,TData,ROMS[dev]);
  }
}

寻找到的唯一序列号可用于以后每个1-Wire器件的数据读取,利用该序列号不再需要给所有器件发送读写命令。通过写入0x44h,主控制器命令所有从机器件执行温度转换命令。总线复位后,温度转换命令紧随Skip ROM (0xCC) 命令,可以同时发送到所有的1-Wire器件。

int ConvertT(int BASE)
{
  outp(BASE,0x01);   // send reset
  WaitforInterrupt();

  outp(BASE+1,0xCC);   // skip ROM
  WaitforInterrupt();

  outp(BASE+1,0x44);   // convert Temp.
  WaitforInterrupt();
DS18B20温度转换速度很快,无须等待时间。主控制器必须逐个访问从机器件读取它们的温度。总线复位后,主控制器发送一个Match ROM命令和64位序列号,然后是读取缓存器命令。之后,主控制器读取2个字节的温度信息,注意:在1-Wire总线上读数时,必须先发送0xFFh。由这2个字节得到一个16位的温度值。该流程对每个器件重复循环一次。

int ReadTemps(int BASE)
{
  int dev,loop;
  int LSB,MSB;

  for(dev=0;dev<4;dev++)
  {
    outp(BASE,0x01);   // send reset
    WaitforInterrupt();
    outp(BASE+1,0x55);   // match ROM
    WaitforInterrupt();

    // send 8 bytes of ROM code
    for(loop=0;loop<8;loop++)
    {
      outp(BASE+1,ROMS[dev][loop]);
      WaitforInterrupt();
    }

    outp(BASE+1,0xBE);   // read memory
    WaitforInterrupt();

    outp(BASE+1,0xFF);   // read LSB
    WaitforInterrupt();
    LSB = inp(BASE+1);

    outp(BASE+1,0xFF);   // read MSB
    WaitforInterrupt();
    MSB = inp(BASE+1);

    TEMPS[dev] = MSB<<8 + LSB;
  }
}
程序运行完成后,GetTemperatures函数要么返回一个值“1”,表明总线上无器件;要么返回一个值“0”,利用从4个1-Wire器件获得的数字,更新ROM代码和温度值。

如果已知器件的ROM代码,可以跳过整个Search ROM程序。如果总线上只有一个器件,其ROM序列号不必搜索,每次处理时,执行Skip ROM命令。

如果无法提供中断请求信号线,则WaitforInterrupt函数可以编写成通过查询中断寄存器完成命令。然而这样一来,CPU必须等待1-Wire主机完成其操作。

RecoverROM源代码

下面提供的源代码包含用于产生16字节发送值的用户代码,并从Search ROM过程中获得的16个字节中提取最新的ROM码。

该函数需要16字节接收数据、16字节发送数据和8字节ROM码的指针。如果总线上仍有未知器件,该函数将返回值“0”,当总线上所有器件均被找到时则返回值“1”,这一特性在上述例子中未采用。

该函数没有出错检验,因此,如果总线上无器件,则发现的ROM代码是错误的。

/////////////////////////////////////////////////////////////////////////////////
// RecoverROM performs two functions. Given 16 bytes of receive data taken from
// the 1-Wire Master during a Search ROM function, it will extract the ROM code
// found into an 8 byte array and it will generate the next 16 bytes to be trans-
// mitted during the next Search ROM.
// RecoverROM must be initialized by sending a NULL pointer in ReceivedData. It
// will write 16 bytes of zeros into TransmitData and clear the discrepancy tree.
// The discrepancy tree keeps track of which ROM discrepancies have already been
// explored.
// RecoverROM also returns a value telling whether there are any more ROM codes to
// be found. If a zero is returned, there are still discrepancies. If a one is
// returned all ROMs on the bus have been found. Running RecoverROM again in this
// case will result in repeating ROM codes already found
////////////////////////////////////////////////////////////////////////////////

int RecoverROM(int* ReceiveData, int* TransmitData, int* ROMCode)
{
  int loop;
  int result;
  int TROM[64];   // the transmit value being generated
  int RROM[64];   // the ROM recovered from the received data
  int RDIS[64];   // the discrepancy bits in the received data

  static int TREE[64];   // used to keep track of which discrepancy bits have
                         // already been flipped.

  // If receivedata is NULL, this is the first run. Transmit data should be all
  // zeros, and the discrepancy tree must also be reset.

  if(ReceiveData == NULL)
  {
    for(loop = 0; loop < 64; loop++) TREE[loop] = 0;
    for(loop = 0; loop < 16; loop++) TransmitData[loop] = 0;
    return 1;
  }
  // de-interleave the received data into the new ROM code and the discrepancy bits
  for(loop = 0; loop < 16; loop++)
  {
    if((ReceiveData[loop] & 0x02) == 0x00) RROM[loop*4] = 0; else RROM[loop*4 ] = 1;
    if((ReceiveData[loop] & 0x08) == 0x00) RROM[loop*4+1] = 0; else RROM[loop*4+1] = 1;
    if((ReceiveData[loop] & 0x20) == 0x00) RROM[loop*4+2] = 0; else RROM[loop*4+2] = 1;
    if((ReceiveData[loop] & 0x80) == 0x00) RROM[loop*4+3] = 0; else RROM[loop*4+3] = 1;

    if((ReceiveData[loop] & 0x01) == 0x00) RDIS[loop*4] = 0; else RDIS[loop*4 ] = 1;
    if((ReceiveData[loop] & 0x04) == 0x00) RDIS[loop*4+1] = 0; else RDIS[loop*4+1] = 1;
    if((ReceiveData[loop] & 0x10) == 0x00) RDIS[loop*4+2] = 0; else RDIS[loop*4+2] = 1;
    if((ReceiveData[loop] & 0x40) == 0x00) RDIS[loop*4+3] = 0; else RDIS[loop*4+3] = 1;
  }

  // initialize the transmit ROM to the recovered ROM

  for(loop = 0; loop < 64; loop++) TROM[loop] = RROM[loop];

  // work through the new transmit ROM backwards setting every bit to 0 until the
  // most significant discrepancy bit which has not yet been flipped is found.
  // The transmit ROM bit at that location must be flipped.

  for(loop = 63; loop >= 0; loop--)
  {
    // This is a new discrepancy bit. Set the indicator in the tree, flip the
    // transmit bit, and then break from the loop.

    if((TREE[loop] == 0) && (RDIS[loop] == 1) && (TROM[loop] == 0))
    {
      TREE[loop] = 1;
      TROM[loop] = 1;
      break;
    }
    if((TREE[loop] == 0) && (RDIS[loop] == 1) && (TROM[loop] == 1))
    {
      TREE[loop] = 1;
      TROM[loop] = 0;
      break;
    }

    // This bit has already been flipped, remove it from the tree and continue
    // setting the transmit bits to zero.

    if((TREE[loop] == 1) && (RDIS[loop] == 1)) TREE[loop] = 0;
    TROM[loop] = 0;
  }
  result = loop;   // if loop made it to -1, there are no more discrepancy bits
                   // and the search can end.

  // Convert the individual transmit ROM bit into a 16 byte format
  // every other bit is don't care.

  for(loop = 0; loop < 16; loop++)
  {
    TransmitData[loop] = (TROM[loop*4]<<1) +
                         (TROM[loop*4+1]<<3) +
                         (TROM[loop*4+2]<<5) +
                         (TROM[loop*4+3]<<7);
  }

  // Convert the individual recovered ROM bits into an 8 byte format

  for(loop = 0; loop < 8; loop++)
  {
    ROMCode[loop] = (RROM[loop*8]) +
                    (RROM[loop*8+1]<<1) +
                    (RROM[loop*8+2]<<2) +
                    (RROM[loop*8+3]<<3) +
                    (RROM[loop*8+4]<<4) +
                    (RROM[loop*8+5]<<5) +
                    (RROM[loop*8+6]<<6) +
                    (RROM[loop*8+7]<<7);
  }
  if(result == -1) return 1;   // There are no DIS bits that haven't been flipped
                               // Tell the main loop the seach is over
  return 0;   // else continue
}

1-Wire是Maxim Integrated Products, Inc.的注册商标。


我们期待您的反馈!
喜欢?不喜欢?有待改善?或为我们提供建议?请与我们联系 — 我们将根据您的意见或建议改善我们的工作。 网页评价或提供建议


自动更新
需要自动接收最新发布的应用笔记吗?请订阅EE-Mail™ (English only)。



更多信息  APP 120: Sep 03, 2004
DS18B20 分辨率可编程设置的1-Wire数字温度计 完整的数据资料
(PDF, 188kB)
DS1WM 综合1-Wire总线主控器 完整的数据资料
(PDF, 124kB)
DS2408 1-Wire、8通道、可编址开关 完整的数据资料
(PDF, 396kB)
免费样品
DS2502-E48 48位节点地址芯片 完整的数据资料
(PDF, 48kB)
免费样品
 

下载,PDF格式下载,PDF格式 (46kB)
 AN120, AN 120, APP120, Appnote120, Appnote 120



         


      隐私权政策    法律声明

      © 2008 Maxim Integrated Products, Dallas Semiconductor版权所有