应用笔记 120

利用API控制DS1WM 1-Wire®总线主机


摘要 : 本应用笔记提供了一些利用Maxim API控制DS1WM 1-Wire总线主机的C语言例程。本文讨论的内容基于DS1WM已经设计到FPGA或ASIC内,主控CPU通过API调用控制DS1WM。DS1WM通过两个文件(DS1WM.c和DS1WM.h)进行通信,这两个文件构成了API。本文讨论的例程包括:初始化、1-Wire复位、ROM搜索算法,说明了1-Wire常见功能的使用。假设读者已经了解1-Wire器件、DS1WM 1-Wire主机以及1-Wire总线协议。

引言

DS1WM1-Wire总线主机内部生成1-Wire时序和控制信号,无需CPU产生逐位控制时序。该控制功能使系统程序设计人员能够使用API函数进行程序开发。DS1WM API采用ANSI C语言编写,可以为多种支持ANSI C的微处理器平台所用。以下例程介绍了如何在网络中识别、选择1-Wire从设备,并与其进行通信。

图1. 1-Wire网络总线示例电路
图1. 1-Wire网络总线示例电路

图1所示的电路配置适用于以下所有示例程序。主机CPU利用DS1WM与带有链路模式和GPIO的1-Wire数字温度传感器DS28EA00以及1-Wire 1Kb EEPROM DS2431进行通信。示例程序重点演示API,而非从设备功能。16MHz的系统时钟通过CLK引脚提供1-Wire主机时序。1-Wire主机的端口引脚需要映射到应用微处理器的引脚。API文件DS1WM.h中包含了引脚映射功能。API头文件中的MaxNumberDevices宏必须更改为实际可能的最大器件数。DS1WM.c API文件中的ReadByteFromRegister和WriteByte2Register函数也需要进行修改,以便正确触发微处理器端口。VPUP电压典型值为5.0V,RPUP不大于2.2kΩ。

概述

通过Maxim支持中心,可以申请免费的DS1WM和API。下面讨论了初始化和1-Wire复位控制的DS1WM代码。此外还提供了一个示例,用于演示如何使用DS1WM ROM搜索引擎确定每个器件的唯一注册码(ROM ID)。使用存储的注册码以及家族码,可以识别每个1-Wire从设备,并通过匹配ROM命令进行访问。家族码列表,请参见应用笔记155:“1-Wire®软件资源指南”。DS28EA00示例演示了如何进行温度转换。第二个示例给出了高速读写DS2431暂存器的两种方法。

总线主机基本操作

微处理器通过触发复位引脚初始化DS1WM,API接口执行所有1-Wire通信。示例程序通过调用DS1WM API函数进行工作。由于响应中断时会改变微处理器代码,因此后续示例中采用轮询方式替代中断方式通信。

后续的示例程序中用到了表1所示的1-Wire操作。

表1. 1-Wire操作
Operation* Description
OWReset Sends the 1-Wire reset stimulus and checks for the pulses of 1-Wire slave devices that are present on the bus.
OWWriteByte/OWReadByte Sends or receives a single byte of data from the 1-Wire bus.
OWWriteBytes/OWReadBytes Sends or receives multiple bytes of data from the 1-Wire bus.
OWSearch Performs the 1-Wire Search Algorithm (see application note 187, "1-Wire Search Algorithm").
OverdriveEnable Sets the 1-Wire communication speed for the DS1WM to overdrive. Note that this only changes the communication speed of the DS1WM; the 1-Wire slave device must be instructed to make the switch when going from normal to overdrive. The 1-Wire slave will always revert to standard speed when it encounters a standard-speed 1-Wire reset.
OverdriveDisable Sets the 1-Wire communication speed for the DS1WM to standard. Note that this only changes the communication speed of the DS1WM; a standard-speed 1-Wire reset is required for slave devices to exit overdrive.
MatchROM Selects device by issuing the Match ROM command followed by the 64-bit ROM ID selected.
SetClockFrequency Sets the clock frequency for the DS1WM.
InterruptEnableRegisterWrite Writes a single byte of data to the DS1WM Interrupt Enable register.
InterruptRegisterRead Reads a single byte of data from the DS1WM Interrupt register.
ReceiveBufferRead Reads a single byte of data from the DS1WM Receive Buffer register.
*该表涵盖了示例中用到的子程序,详细说明请参见DS1WM.c文件。

初始化

通过初始化DS1WM,启动主机,提供正确的1-Wire总线时序。主机将数值16 (代表频率值)传递至SetClockFrequency函数,将0x90h写入时钟分频寄存器(请参见DS1WM数据资料)。中断使能寄存器设置为0x00h,防止INTR引脚产生中断。上电时,接收缓存可能包含无效值,因此在发送1-Wire命令之前,最好对接收缓存进行清理。启动时对中断寄存器和接收缓存器的读操作将清除其中的各个位。

//----------------------------------------------------------------------------------------------------
//Start of initialization example

SetClockFrequency(16);                  //Set clock frequency to 16MHz (power-on default)
InterruptEnableRegisterWrite(0x00);     //Clear interrupts

//Flush receive buffer
InterruptRegisterData = InterruptRegisterRead();
ReceiveBufferRead();

//End of initialization example
//----------------------------------------------------------------------------------------------------

1-Wire复位(OWReset API函数)

初始化完成后,主机必须确定1-Wire总线上是否接有设备。主机调用OWReset函数,以实现该目的。如果检测到器件存在脉冲,该函数返回1;如果没有检测到器件或发送错误,则函数返回0。如果返回值为0,需要检查ErrorStatus变量,以确定故障状态。1-Wire复位的优先级高于所有1-Wire命令(例如,Match ROM、Skip ROM、Read ROM),但调用OWSearch函数时除外,该函数自身带有复位。软件设计人员应当在程序中加入适当的故障处理代码,在发生故障时能够触发执行这些代码。ErrorStatus值意味着发生了如下错误中的一种:没有进行在线检测;没有检测到器件;存在1-Wire短路;1-Wire总线始终处于低电平状态。

        Result = OWReset();
        if(!Result){
                switch(ErrorStatus){
                        case -1:        //DS1WM did not recognize 1-Wire reset (PD=0)
                                //To do: add your error code here
                                break;
                        case -2:        //No device found (PDR=1)
                                //To do: add your error code here
                                break;
                        case -7:        //1-Wire IO is shorted (OW_SHORT=1)
                                //To do: add your error code here
                                break;
                        case -8:        //1-Wire IO is shorted (OW_LOW=1)
                                //To do: add your error code here
                                break;
                }
        }
        

利用ROM搜索引擎确定ROM ID

总线上的每个1-Wire设备都必须进行识别。存储在每个1-Wire器件中的唯一64位ROM ID用于选择各个器件,并根据家族码识别器件类型。为降低例程的复杂度,采用8位家族码识别DS28EA00和DS2431。

注意:如果1-Wire总线上连接了不止一种器件类型(例如,具有相同的家族码),则后续的方法无法对器件进行识别。不对总线上的多个1-Wire器件进行识别,则无法对特定的1-Wire器件发送功能命令。如果使用了两个或更多的类似器件,则必须通过查找表才能正确访问特定器件。

调用OWSearch函数可以识别总线上所有的1-Wire器件,该函数将8字节ROM ID排成一列。这期间无需调用OWReset函数,因为该函数已经包含在OWSearch函数中。OWSearch函数例外的包含了OWReset函数,是因为其搜索算法具有迭代过程。OWSearch函数的返回值为搜索到的1-Wire器件个数,并将每个64位ROM ID写入一个名为ROMCodes的二维数组。

下列例程仅演示了调用一次OWSearch的情况,因此后续连接至该1-Wire总线的器件将无法通过该代码进行识别。OWSearch函数成功执行后将返回搜索到的器件个数。如果搜索失败,ErrorStatus变量将返回错误值,其中包括产生OWReset的故障条件。搜索成功后,对ROMCodes数组进行了遍历,并保存每个器件的数组索引。这种器件索引方式是通过比较搜索到的每个器件的家族码实现的。随后调用数组索引,可以实现与搜索到的特定1-Wire器件进行通信。这相当于创建了一个简单的链表(注:可以通过指针进行操作)。

//----------------------------------------------------------------------------------------------------
//Start of DS1WM search ROM accelerator example

//Devices on the 1-Wire IO are:
//DS28EA00      Family Code = 42h (6900000004E8C842)
//DS2431        Famliy Code = 2Dh (5A0000000FDE052D)

//Find all devices on 1-Wire line and populate ROMCodes array
Result = OWSearch(ROMCodes);    //Returns number of devices found if successful

//Set number of 1-Wire devices found
if(Result)
        NumberOfDevices = Result;

if(!Result){
        switch(ErrorStatus){
                case -1:        //DS1WM did not recognize 1-Wire Reset (PD=0)
                        //To do: add your error code here
                        break;
                case -2:        //No device found (PDR=1)
                        //To do: add your error code here
                        break;
                case -7:        //1-Wire IO is shorted (OW_SHORT=1)
                        //To do: add your error code here
                        break;
                case -8:        //1-Wire IO is shorted (OW_LOW=1)
                        //To do: add your error code here
                        break;
                case -9:        //Invalid CRC for device
                        //To do: add your error code here
                        break;
                case -10:       //ROMCodes array too small (Edit MaxNumberDevices in DS1WM.h)
                        //To do: add your error code here
                        break;
        }
}

//Note: This function is intended to be used when there is only one device with the same
//Family Code present on the line
for(i=0;i<NumberOfDevices;i++){
        if(ROMCodes[i][0] == 0x42){
                DS28EA00 = i;           //Save off array index for DS28EA00
                continue;
        }

        if(ROMCodes[i][0] == 0x2D){
                DS2431 = i;             //Save off array index for DS2431
                continue;
        }
}

利用DS1WM API函数控制DS28EA00

该示例利用之前搜索到的ROM ID与DS28EA00进行通信。OWReset之后发送Match ROM命令。ROM码数组连同被访问的器件索引一起,传递到Match ROM函数。1-Wire总线上的器件会在Match ROM命令0x55h之后收到一个64位ROM码,可以使总线主机在多点总线上寻址到特定的DS28EA00。只有与64位ROM序列完全匹配的DS28EA00才能响应接下来的命令,其它的从器件将等待下一个复位脉冲。

OWWriteByte和OWReadByte API函数用来产生存储器命令(例如,Write/Copy/Read Scratchpad命令)。DS1WM通过发送Write Scratchpad命令(0x4Eh)以及随后的高温、低温和配置寄存器设置值,来设定温度报警以及分辨率。上述操作完成后,发送OWReset以及Match ROM命令、ROM ID和Copy Scratchpad命令(0x48h) (将暂存器的内容复制到寄存器存储器),完成温度分辨率设置。必须为CPU主机增加10ms的延时,以完成复制操作。考虑到微处理器延时子程序的差异,API仅提供了注释伪码。

复制操作完成后,再次执行OWReset,然后发送Match ROM、ROM ID和转换温度命令(0x44h)。需要增加100ms的延时,以完成温度转换。最后,发送OWReset和Match ROM、ROM ID和Read Scratchpad命令(0xBEh),读取两字节温度数据。需要注意的是,选择DS28EA00时,必须始终遵循OWReset之后发送Match ROM命令和ROM ID的命令格式。当总线上仅有一个器件时,才能够使用Skip ROM命令(即无需Search ROM)。

//----------------------------------------------------------------------------------------------------
//Start of DS28EA00 example

Result = OWReset();
        if(!Result){
                switch(ErrorStatus){
                        case -1:        //DS1WM did not recognize 1-Wire reset(PD=0)
                                //To do: add your error code here
                                break;
                        case -2:        //No device found(PDR=1)
                                //To do: add your error code here
                                break;
                        case -7:        //1-Wire IO is shorted(OW_SHORT=1)
                                //To do: add your error code here
                                break;
                        case -8:        //1-Wire IO is shorted(OW_LOW=1)
                                //To do: add your error code here
                                break;
                }
        }

        //Set temperature resolution
        Result = MatchROM(ROMCodes,DS28EA00);           //Select device
        Result = OWWriteByte(0x4E);                     //Issue Write Scratchpad command
        Result = OWWriteByte(0x00);                     //TH register data
        Result = OWWriteByte(0x00);                     //TL degister data
        Result = OWWriteByte(0x1F);                     //Config. reg. data (set 9-bit temp. resolution)

        OWReset();                                      //Error code removed for conciseness
        MatchROM(ROMCodes,DS28EA00);                    //Select device
        OWWriteByte(0x48);                              //Issue Copy Scratchpad command

        //To do: add microprocessor-specific code delay to allow copy to complete
        //Delay(10MS);
//Psuedo code

        OWReset();                                      //1-Wire reset
        MatchROM(ROMCodes,DS28EA00);                    //Select device
        OWWriteByte(0x44);                              //Issue Convert Temperature command

        //To do: add microprocessor-specific code delay to allow temperature conversion to complete
        //Delay(100MS);
//Psuedo code

        //Read temperature results from scratchpad
        OWReset();                                      //1-Wire reset
        MatchROM(ROMCodes,DS28EA00);                    //Select device
        OWWriteByte(0xBE);                              //Issue Read Scratchpad command
        TempLSB = OWReadByte();                 	 //Read byte
        TempMSB = OWReadByte();                 	 //Read byte
//End of DS28EA00 example
//----------------------------------------------------------------------------------------------------

利用DS1WM API函数控制高速模式下的DS2431

该示例利用之前搜索到的ROM ID与DS2431进行通信。OWReset之后发送Overdrive Skip ROM命令(0x3Ch),将所有支持高速模式的1-Wire器件置于高速模式。调用OverdriveEnable函数激活DS1WM高速时序。此时所有1-Wire器件工作在高速模式。标准速率模式和高速模式下的1-Wire时序请参见应用笔记126:“用软件实现1-Wire®通信”。

使用目标地址TA1和TA2变量

在第一个示例方法中,变量TA1和TA2设置为DS2431期望的存储器地址。发送OWReset,之后是Match ROM、DS2431 ROM ID、Write Scratchpad命令(0x0Fh)、目标地址1 & 2以及要写入DS2431 64位暂存器的8字节数据。推荐进行回读及CRC16校验操作。关于CRC16的详细讨论,请参考应用笔记27:“理解和运用Maxim iButton®产品中的循环冗余校验(CRC)”。

OWWriteBytes和OWReadBytes

在第二个示例方法中,调用了两个新的API函数:OWWriteBytes和OWReadBytes。这两个API函数简化了大量数据至暂存器的读写操作。

写暂存器的方法是:设置目标地址1 & 2、将数据写入WriteBytes数组、执行OWReset、然后发送Match ROM、DS2431 ROM ID、Write Scratchpad命令(0x0Fh)、目标地址1 & 2、利用OWWriteBytes函数写入保存在WriteBytes的所有10个字节、回读CRC16并对CRC16进行校验。

读暂存器的方法是:执行OWReset、发送Match ROM、DS2431 ROM ID、Read Scratchpad命令(0xAAh)、利用OWReadBytes函数读取所有13个字节(TA1、TA2、ES、CRC16 & 8字节数据)并保存到ReadBytes。

调用API函数OverdriveDisable,并随后发送标准OWReset,可以退出高速模数,使所有器件返回至标准速率。

//----------------------------------------------------------------------------------------------------
//Start of DS2431 example

        Result = OWReset();                     //Error code removed for conciseness
        Result = OWWriteByte(0x3C);             //Overdrive Skip ROM (all devices are now in overdrive)
        OverdriveEnable();                      //Enable Overdrive Mode

        //Write scratchpad with data
        //First method
        TA1 = 0x00;
        TA2 = 0x00;
        Result = OWReset();                     //1-Wire reset
        Result = MatchROM(ROMCodes,DS2431);     //Select device
        Result = OWWriteByte(0x0F);             //Issue Write Scratchpad command
        Result = OWWriteByte(TA1);              //Send target address 1 (TA1)
        Result = OWWriteByte(TA2);              //Send target address 2 (TA2)

        //Write 8 Bytes of Data
        Result = OWWriteByte(0x11);             //Send Data Byte for all
        Result = OWWriteByte(0x22);
        Result = OWWriteByte(0x33);
        Result = OWWriteByte(0x44);
        Result = OWWriteByte(0x55);
        Result = OWWriteByte(0x66);
        Result = OWWriteByte(0x77);
        Result = OWWriteByte(0x88);

        //It is recommended that the CRC16 be read back and verified
        //CRC16 code was left out for conciseness

        Result = OWReset();                     //1-Wire Reset
        Result = MatchROM(ROMCodes,DS2431);     //Select device
        Result = OWWriteByte(0xAA);             //Issue Read Scratchpad command
        Result = OWReadByte();                  //Read TA1
        if(Result != TA1){
                //To do: Add your error code here
        }
        Result = OWReadByte();                  //Read TA2
        if(Result != TA2){
                //To do: Add your error code here
        }

        ES = OWReadByte();                      //Read ES

        //To do: add your error code after reads
        Result = OWReadByte();                  //Read Data Byte (0x11)
        Result = OWReadByte();                  //Read Data Byte (0x22)
        Result = OWReadByte();                  //Read Data Byte (0x33)
        Result = OWReadByte();                  //Read Data Byte (0x44)
        Result = OWReadByte();                  //Read Data Byte (0x55)
        Result = OWReadByte();                  //Read Data Byte (0x66)
        Result = OWReadByte();                  //Read Data Byte (0x77)
        Result = OWReadByte();                  //Read Data Byte (0x88)

        //It is recommended that the CRC16 be read back and verified
        //CRC16 code was left out for conciseness

        //Second method
        TA1 = 0x00;
        TA2 = 0x00;

        WriteBytes[0] = TA1;
        WriteBytes[1] = TA2;

        for(i=2;i<10;i++){
                WriteBytes[i] = i;
        }

        Result = OWReset();                     //1-Wire reset
        Result = MatchROM(ROMCodes,DS2431);     //Select device
        Result = OWWriteByte(0x0F);             //Issue Write Scratchpad command

        //Write 10 bytes of data (TA1, TA2 & 8 bytes of data)
        OWWriteBytes(WriteBytes,10);            //Write data bytes

        //It is recommended that the CRC16 be read back and verified
        //CRC16 code was left out for conciseness

        Result = OWReset();                     //1-Wire reset
        Result = MatchROM(ROMCodes,DS2431);     //Select device
        Result = OWWriteByte(0xAA);             //Issue Read Scratchpad command

        //Read 13 bytes of data (TA1, TA2, ES, CRC16 & 8 bytes of data)
        OWReadBytes(ReadBytes,13);              //Read data bytes

        //It is recommended that the CRC16 be read back and verified
        //CRC16 code was left out for conciseness

        //Exit overdrive
        OverdriveDisable();
        Result = OWReset();                     //Std. reset issued (all devices are now in standard speed)

//End of DS2431 example
//----------------------------------------------------------------------------------------------------

结论

本应用笔记给出了使用API函数控制DS1WM的示例,无需主机CPU产生1-Wire时序。用户现在应该对选择和访问总线上多个1-Wire器件的通用API函数有了一定的了解。本文给出了DS28EA00和DS2431总线示例器件的控制和访问示例,此外还演示了高速模式以及单字节/多字节数据的读写操作。