用软件实现1-Wire通信

通信设计应用

63人已加入

描述

摘要:在没有专用总线主机(如DS2480B、DS2490)的情况下,微处理器可以轻松地产生1-Wire时序信号。本应用笔记给出了一个采用‘C’语言编写、支持标准速率的1-Wire主机通信基本子程序实例。1-Wire总线的四个基本操作是:复位、写“1”、写“0”和读数据位。字节操作可以通过反复调用位操作实现,本文提供了通过各种传输线与1-Wire器件进行可靠通信的时间参数。

引言

在没有专用总线主机的情况下,微处理器可以轻松地产生1-Wire时序信号。本应用笔记给出了一个采用‘C’语言编写、支持标准速率的1-Wire主机通信基本子程序实例。此外,本文也讨论了高速通信模式。要使该实例中的代码正常运行,系统必须满足以下几点要求:

  1. 微处理器的通信端口必须是双向的,其输出为漏极开路,且线上具有弱上拉。这也是所有1-Wire总线的基本要求。关于简单的1-Wire主机微处理器电路实例,请参见1-Wire网络可靠设计指南(应用笔记148)的附录A。
  2. 微处理器必须能产生标准速度1-Wire通信所需的精确1µs延时和高速通信所需要的0.25µs延时。
  3. 通信过程不能被中断。
1-Wire总线有四种基本操作:复位、写1位、写0位和读位操作。在产品资料中,将完成一位传输的时间称为一个时隙。于是字节传输可以通过多次调用位操作来实现,下表1是各个位操作的简要说明以及实现这些操作所必须的步骤列表。图1为其时序波形图。表2给出了通常线路条件下1-Wire主机与1-Wire器件通信的最短、最长和推荐时间。如果与1-Wire主机相连的器件比较特殊或者线路条件比较特殊,则可以采用最小值或最大值。

表1. 1-Wire操作
Operation Description Implementation
Write 1 bit Send a '1' bit to the 1-Wire slaves (Write 1 time slot) Drive bus low, delay A
Release bus, delay B
Write 0 bit send a '0' bit to the 1-Wire slaves (Write 0 time slot) Drive bus low, delay C
Release bus, delay D
Read bit Read a bit from the 1-Wire slaves (Read time slot) Drive bus low, delay A
Release bus, delay E
Sample bus to read bit from slave
Delay F
Reset Reset the 1-Wire bus slave devices and ready them for a command Delay G
Drive bus low, delay H
Release bus, delay I
Sample bus, 0 = device(s) present, 1 = no device present
Delay J

Wire
图1. 1-Wire时序图

表2. 1-Wire主机时序
Parameter Speed Min (µs) Recommended (µs) Max (µs) Notes
A Standard 5 6 15 1, 2
Overdrive 1 1.5 1.85 1, 3
B Standard 59 64 N/A 2, 4
Overdrive 7.5 7.5 N/A 3, 4
C Standard 60 60 120 2, 5
Overdrive 7 7.5 14 3, 5
D Standard 8 10 N/A 2, 6
Overdrive 2.5 2.5 N/A 3, 6
E Standard 5 9 12 2, 7, 8
Overdrive 0.5 0.75 0.85 3, 7, 8
F Standard 50 55 N/A 2, 9
Overdrive 6.75 7 N/A 3, 9
G Standard 0 0 0  
Overdrive 2.5 2.5 N/A 3, 14
H Standard 480 480 640 2, 10, 15
Overdrive 68 70 80 3, 10
I Standard 63 70 78 2, 11
Overdrive 7.2 8.5 8.8 3, 11
J Standard 410 410 N/A 2, 12, 13
Overdrive 39.5 40 N/A 3, 12

有关表中这些值的详细计算,可参考:http://files.dalsemi.com/auto_id/public/an126.zip

注释:
  1. 在产品数据资料中,表示为tW1L (写1低)减去ε (上升至VTH的时间)加上tF (下降至VTL的时间)。
  2. 假定网络为标准速度的中等距离网络,且上升和下降时间不超过3µs。
  3. 假定网络为高速的小型网络,且上升和下降时间不超过0.5µs。
  4. 数据资料中,表示为tSLOT (时隙时间)减去‘A’所代表的时间。
  5. 数据资料中,表示为tW0L (写0低)减去δ (上升至VIHMASTER的时间)加上tF (下降至VTL的时间)。
  6. 数据资料中,表示为tREC (恢复时间)加上δ (上升至VIHMASTER的时间)。
  7. 数据资料中,表示为tMSR (主机采样读时间)加上tF (下降至VTL的时间),再减去‘A’。
  8. 在该范围内,采样要尽可能晚,以便获得最长的恢复时间。
  9. 数据资料中,表示为tSLOT (时隙时间)减去‘A’,再减去‘E’。
  10. 数据资料中,表示为tRSTL (复位为低的时间)减去ε (上升至VTH的时间)加上tF (下降至VTL的时间)。
  11. 数据资料中,表示为tMSP (主机采样应答时间)加上ε (上升至VTH的时间)。
  12. 数据资料中,其最小值表示为tRSTL (复位低电平时间)减去‘I’所用的时间。
  13. 这里所提到的1-Wire复位操作没有把DS2404和DS1994使用的扩展应答(报警)脉冲序列考虑进去,关于这种特殊情况,请查看产品的数据资料。在1-Wire复位序列的未尾进行采样,可以验证1-Wire总线是否已返回到上拉电平。如果电平仍为0,则可能是1-Wire总线与地之间短路,或DS2404/DS1994发出了报警信号。
  14. 表示为tREC (恢复时间)减去‘D’所用的时间。在高速应用时,一些器件在tRSTL (低电平复位时间)之前要求额外的延时,以保证器件的寄生电源被完全充满。
  15. 对于低电压工作方式,有些器件可能需要更长的延时。关于合适的参数值,请参阅器件数据资料。

代码实例

下面代码实例都依赖于两个通用的‘C’函数outp和inp,从IO端口读写字节数据。他们通常位于标准库中。当应用于其它平台时,可以采用合适的函数来替代它们。

// send 'databyte' to 'port'
int outp(unsigned port, int databyte);

// read byte from 'port'
int inp(unsigned port);
代码中的常量PORTADDRESS (图3)用来定义通信端口的地址。这里我们假定使用通信端口的第0位控制1-Wire总线。设定该位为1,将使1-Wire总线变为低电平;设定该位为0,1-Wire总线将被释放,此时1-Wire总线被电阻上拉,或被1-Wire从器件下拉。

代码中的tickDelay函数是一个用户编制的子程序,此函数用于产生一个1/4µs整数倍的延时。在不同的平台下,该函数的实现也是不同的,故在此不做具体描述。以下是tickDelay函数声明代码,以及一个SetSpeed函数,用于设定标准速度和高速模式的延时时间。

实例1. 1-Wire时序的生成

// Pause for exactly 'tick' number of ticks = 0.25us
void tickDelay(int tick); // Implementation is platform specific

// 'tick' values
int A,B,C,D,E,F,G,H,I,J;

//-----------------------------------------------------------------------------
// Set the 1-Wire timing to 'standard' (standard=1) or 'overdrive' (standard=0).
//
void SetSpeed(int standard)
{
        // Adjust tick values depending on speed
        if (standard)
        {
                // Standard Speed
                A = 6 * 4;
                B = 64 * 4;
                C = 60 * 4;
                D = 10 * 4;
                E = 9 * 4;
                F = 55 * 4;
                G = 0;
                H = 480 * 4;
                I = 70 * 4;
                J = 410 * 4;
        }
        else
        {
                // Overdrive Speed
                A = 1.5 * 4;
                B = 7.5 * 4;
                C = 7.5 * 4;
                D = 2.5 * 4;
                E = 0.75 * 4;
                F = 7 * 4;
                G = 2.5 * 4;
                H = 70 * 4;
                I = 8.5 * 4;
                J = 40 * 4;
        }
}
1-Wire基本操作的代码程序如实例2所示。

实例2. 基本的1-Wire函数

//-----------------------------------------------------------------------------
// Generate a 1-Wire reset, return 1 if no presence detect was found,
// return 0 otherwise.
// (NOTE: Does not handle alarm presence from DS2404/DS1994)
//
int OWTouchReset(void)
{
        int result;

        tickDelay(G);
        outp(PORTADDRESS,0x00); // Drives DQ low
        tickDelay(H);
        outp(PORTADDRESS,0x01); // Releases the bus
        tickDelay(I);
        result = inp(PORTADDRESS) & 0x01; // Sample for presence pulse from slave
        tickDelay(J); // Complete the reset sequence recovery
        return result; // Return sample presence pulse result
}

//-----------------------------------------------------------------------------
// Send a 1-Wire write bit. Provide 10us recovery time.
//
void OWWriteBit(int bit)
{
        if (bit)
        {
                // Write '1' bit
                outp(PORTADDRESS,0x00); // Drives DQ low
                tickDelay(A);
                outp(PORTADDRESS,0x01); // Releases the bus
                tickDelay(B); // Complete the time slot and 10us recovery
        }
        else
        {
                // Write '0' bit
                outp(PORTADDRESS,0x00); // Drives DQ low
                tickDelay(C);
                outp(PORTADDRESS,0x01); // Releases the bus
                tickDelay(D);
        }
}

//-----------------------------------------------------------------------------
// Read a bit from the 1-Wire bus and return it. Provide 10us recovery time.
//
int OWReadBit(void)
{
        int result;

        outp(PORTADDRESS,0x00); // Drives DQ low
        tickDelay(A);
        outp(PORTADDRESS,0x01); // Releases the bus
        tickDelay(E);
        result = inp(PORTADDRESS) & 0x01; // Sample the bit value from the slave
        tickDelay(F); // Complete the time slot and 10us recovery

        return result;
}
该程序包括了1-Wire总线的所有位操作,通过调用该程序可以构成以字节为处理对象的函数,见实例3。

实例3. 派生的1-Wire函数

//-----------------------------------------------------------------------------
// Write 1-Wire data byte
//
void OWWriteByte(int data)
{
        int loop;

        // Loop to write each bit in the byte, LS-bit first
        for (loop = 0; loop < 8; loop++)
        {
                OWWriteBit(data & 0x01);

                // shift the data byte for the next bit
                data >>= 1;
        }
}

//-----------------------------------------------------------------------------
// Read 1-Wire data byte and return it
//
int OWReadByte(void)
{
        int loop, result=0;

        for (loop = 0; loop < 8; loop++)
        {
                // shift the result to get it ready for the next bit
                result >>= 1;

                // if result is one, then set MS bit
                if (OWReadBit())
                        result |= 0x80;
        }
        return result;
}

//-----------------------------------------------------------------------------
// Write a 1-Wire data byte and return the sampled result.
//
int OWTouchByte(int data)
{
        int loop, result=0;

        for (loop = 0; loop < 8; loop++)
        {
                // shift the result to get it ready for the next bit
                result >>= 1;

                // If sending a '1' then read a bit else write a '0'
                if (data & 0x01)
                {
                        if (OWReadBit())
                                result |= 0x80;
                }
                else
                        OWWriteBit(0);

                // shift the data byte for the next bit
                data >>= 1;
        }
        return result;
}

//-----------------------------------------------------------------------------
// Write a block 1-Wire data bytes and return the sampled result in the same
// buffer.
//
void OWBlock(unsigned char *data, int data_len)
{
        int loop;

        for (loop = 0; loop < data_len; loop++)
        {
                data[loop] = OWTouchByte(data[loop]);
        }
}

//-----------------------------------------------------------------------------
// Set all devices on 1-Wire to overdrive speed. Return '1' if at least one
// overdrive capable device is detected.
//
int OWOverdriveSkip(unsigned char *data, int data_len)
{
        // set the speed to 'standard'
        SetSpeed(1);

        // reset all devices
        if (OWTouchReset()) // Reset the 1-Wire bus
                return 0; // Return if no devices found

        // overdrive skip command
        OWWriteByte(0x3C);

        // set the speed to 'overdrive'
        SetSpeed(0);

        // do a 1-Wire reset in 'overdrive' and return presence result
        return OWTouchReset();
}
OWTouchByte函数可以同时完成读写1-Wire总线数据,通过该函数可以实现数据块的读写。在一些平台上执行效率更高, Maxim提供的API就采用了这种函数。通过OWTouchByte函数,OWBlock函数简化了1-Wire总线的数据块发送和接收。注意:OWTouchByte(0xFF)与OWReadByte()等效,OWTouchByte(data)与OWWriteByte(data)等效。

这些函数和tickDelay函数一起构成了1-Wire总线进行位、字节和块操作时所必需的全部函数。实例4给出了利用这些函数读取DS2432的SHA-1认证页的实例。

实例4. 读DS2432实例

//-----------------------------------------------------------------------------
// Read and return the page data and SHA-1 message authentication code (MAC)
// from a DS2432.
//
int ReadPageMAC(int page, unsigned char *page_data, unsigned char *mac)
{
        int i;
        unsigned short data_crc16, mac_crc16;

        // set the speed to 'standard'
        SetSpeed(1);

        // select the device
        if (OWTouchReset()) // Reset the 1-Wire bus
                return 0; // Return if no devices found

        OWWriteByte(0xCC); // Send Skip ROM command to select single device

        // read the page
        OWWriteByte(0xA5); // Read Authentication command
        OWWriteByte((page << 5) & 0xFF); // TA1
        OWWriteByte(0); // TA2 (always zero for DS2432)

        // read the page data
        for (i = 0; i < 32; i++)
                page_data[i] = OWReadByte();
        OWWriteByte(0xFF);

        // read the CRC16 of command, address, and data
        data_crc16 = OWReadByte();
        data_crc16 |= (OWReadByte() << 8);

        // delay 2ms for the device MAC computation
        // read the MAC
        for (i = 0; i < 20; i++)
                mac[i] = OWReadByte();

        // read CRC16 of the MAC
        mac_crc16 = OWReadByte();
        mac_crc16 |= (OWReadByte() << 8);

        // check CRC16...
        return 1;
}

附加软件

本应用笔记给出了1-Wire总线操作的基本函数,这些基本函数都是构建复杂的1-Wire应用的基础。本文忽略的一个重要操作是1-Wire搜索。通过1-Wire搜索可以搜索到挂接在总线上的多个1-Wire从机器件的唯一ID号。在应用笔记187:"1-Wire搜索算法"一文中详细介绍了这种搜索方法,同时也给出了实现这些1-Wire基本函数的C程序代码。

1-Wire公共开发包中含有大量针对特定器件编写的代码,由下列链接提供:
http://www.ibutton.com/software/1wire/wirekit.html

其它更详细的资料请参阅应用笔记155:"1-Wire软件资源指南"。

替换方案

如果对于某一特定应用,通过软件实现1-Wire主机方案不可行,则作为替换方案,可以采用1-Wire主机芯片或合成的1-Wire主机单元。

Maxim提供了采用Verilog和VHDL编写的1-Wire主机。
DS1WM

如需获取1-Wire主机的Verilog/VHDL代码,请提交技术支持申请。

合成的1-Wire主机工作方式在应用笔记120:"利用1-Wire主机通讯"和应用笔记119:"嵌入1-Wire主机"中进行了说明。

有多种1-Wire主机芯片可以作为微处理器的外设。串行、1-Wire线驱动器DS2480B能够很容易地与标准串行口连接。

关于DS2480B的操作,详见应用笔记192:"DS2480B串行接口1-Wire线驱动器的使用")。

这是为远距离1-Wire传输线专门设计的先进的1-Wire线驱动器,在应用笔记244:"性能优异的1-Wire网络驱动器"中,描述了专门为长线设计、更为精细的1-Wire线驱动器。

修订历史记录
07/06/00:1.0版—最初版本。
05/28/02:2.0版—纠正了1-Wire复位采样时间。增加了波形图、链接和更多的代码实例。
02/02/04:2.1版—增加了对高速模式的支持,给出了时序的最小值、最大值,更新了代码实例。
09/06/05:2.2版—修正代码例程说明中的PIO极性。
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分