DS18X20/DS1822 1-Wire温度传感器在微控制器环境中的接口

描述

本应用向用户介绍简单的1-Wire软件,用于将微控制器连接至DS18B20、DS18S20和DS1822 1-Wire温度传感器。本文举例使用DS5000(兼容8051)微控制器。给出了软件示例,说明了延迟、复位、读位、写位、读字节、写字节、ROM搜索、CRC、读温度和读暂存本例程的实现。

介绍

DS1B18、DS20S18或DS20等1822-Wire器件与微控制器的接口有多种方法可供选择。这些方法包括简单的软件解决方案,使用串行接口芯片(如DS2480B),以及将Maxim的VHDL 1-Wire主控制器集成到定制ASIC中。本文向用户介绍最简单的软件方案,用于微控制器与任意数量的DS1x18或DS20温度传感器之间的基本1822-Wire通信。

DS18B20、DS18S20和DS1822的详细时序和工作原理信息可在各自的数据资料中找到,可从Maxim网站获取。

硬件配置

图1中的框图说明了使用多个1-Wire温度传感器时硬件配置的简单性。单线总线为所有设备提供通信访问和电源。总线电源通过 4.7kΩ 上拉电阻从 3V 至 5.5V 电源轨提供。总线上可以连接几乎无限数量的1-Wire器件,因为每个器件都有一个唯一的64位ROM码标识符。

温度传感器

图1.主机微控制器接口。

接口时序

与DS18x20/DS1822的通信通过使用“时隙”实现,允许数据通过1-Wire总线传输。每个通信周期从微控制器的复位脉冲开始,然后是来自DS18x20/DS1822的存在脉冲,如图2所示。

当总线主控将1-Wire总线从逻辑高电平(非活动)拉至逻辑低电平时,将启动写入时隙。所有写入时隙的持续时间必须为60μs至120μs,周期之间的最小恢复时间为1μs。写“0”和写“1”时隙如图3所示。在写入“0”时隙期间,主机微控制器在该时隙的持续时间内将线路拉低。但是,在写入“1”时隙期间,微控制器将线路拉低,然后在时隙开始后15μs内释放线路。

当微控制器将总线拉低1μs,然后释放时隙时启动,DS18x20/DS1822可以控制线路并提供有效数据(高电平或低电平)。所有读取时隙的持续时间必须为60μs至120μs,周期之间的恢复时间至少为1μs(见图3)。

温度传感器

图2.复位脉冲和存在脉冲。

温度传感器

 

温度传感器

图3.写入和读取时间段。

软件控制

为了精确控制1-Wire接口的特殊定时要求,必须首先建立某些关键功能。创建的第一个函数必须是“延迟”函数,它是所有读写控制的组成部分。此功能完全取决于微控制器的速度。本文使用DS5000(兼容8051)微控制器,其工作频率为11.059MHz。右边的示例说明了用于创建时序延迟的“C”原型函数。

延迟示例

// DELAY - with an 11.059MHz crystal.
// Calling the routine takes about 24us, and then
// each count takes another 16us.
//
void delay(int useconds)
{
int s;
for (s=0; s

由于每个通信周期都必须从微控制器的复位开始,因此“复位”功能是要实现的下一个最重要的功能。复位时隙为 480μs。通过将延迟设置为“3”,后跟“25”(见下面的示例),复位脉冲将持续所需的持续时间。复位后,微控制器必须松开,以便DS18x20/DS1822能够通过拉低线路来指示其“存在”。请注意,如果总线上有多个温度传感器,它们将同时响应存在脉冲。

重置示例

//////////////////////////////////////////////////////////////////////////////
// OW_RESET - performs a reset on the one-wire bus and
// returns the presence detect. Reset is 480us, so delay
// value is (480-24)/16 = 28.5 - we use 29. Presence checked
// another 70us later, so delay is (70-24)/16 = 2.875 - we use 3.
//
unsigned char ow_reset(void)
{
unsigned char presence;
DQ = 0; //pull DQ line low
delay(29); // leave it low for 480us
DQ = 1; // allow line to return high
delay(3); // wait for presence
presence = DQ; // get presence signal
delay(25); // wait for end of timeslot
return(presence); // presence signal returned
} // 0=presence, 1 = no part

以下四个示例中显示的读写函数代码段提供了所有数据位和数据字节读写操作所需的基本结构。

读取位示例

 

//////////////////////////////////////////////////////////////////////////////
// READ_BIT - reads a bit from the one-wire bus. The delay
// required for a read is 15us, so the DELAY routine won't work.
// We put our own delay function in this routine in the form of a
// for() loop.
//
unsigned char read_bit(void)
{
unsigned char i;
DQ = 0; // pull DQ low to start timeslot
DQ = 1; // then return high
for (i=0; i<3; i++); // delay 15us from start of timeslot
return(DQ); // return value of DQ line
}

写入位示例

 

//////////////////////////////////////////////////////////////////////////////
// WRITE_BIT - writes a bit to the one-wire bus, passed in bitval.
//
void write_bit(char bitval)
{
DQ = 0; // pull DQ low to start timeslot
if(bitval==1) DQ =1; // return DQ high if write 1
delay(5); // hold value for remainder of timeslot
DQ = 1;
}// Delay provides 16us per loop, plus 24us. Therefore delay(5) = 104us

读取字节示例

 

//////////////////////////////////////////////////////////////////////////////
// READ_BYTE - reads a byte from the one-wire bus.
//
unsigned char read_byte(void)
{
unsigned char i;
unsigned char value = 0;
for (i=0;i<8;i++)
{
if(read_bit()) value|=0x01<

写入字节示例

 

//////////////////////////////////////////////////////////////////////////////
// WRITE_BYTE - writes a byte to the one-wire bus.
//
void write_byte(char val)
{
unsigned char i;
unsigned char temp;
for (i=0; i<8; i++) // writes byte, one bit at a time
{
temp = val>>i; // shifts val right 'i' spaces
temp &= 0x01; // copy that bit to temp
write_bit(temp); // write bit in temp into
}
delay(5);
}

搜索 ROM 算法

为了充分利用1-Wire网络概念,微控制器必须能够与连接到网络的任意数量的器件进行通信。为此,微控制器必须使用图64所示的“Search ROM”算法学习总线上每个器件的唯一4位ROM识别码。图 4 以下示例解释了具有四个从设备的总线的搜索 ROM 例程。还显示了搜索 ROM 例程的示例代码。一旦识别出所有ROM代码,“匹配ROM”命令可用于与网络上的任何特定设备进行通信。

温度传感器

图4.搜索 ROM 算法。

ROM 搜索示例

在ROM搜索过程中,总线主站必须重复一个简单的三步例程:1)从从设备读取ROM码位,2)读取位的补码,3)写入该位的选定值。总线主机必须执行此三步例程 64 次,每个 ROM 代码位执行一次。完成一次通过后,总线主站将知道总线上一个从设备的ROM代码。其余设备及其ROM代码可以通过额外的通道来识别。

以下示例说明了ROM搜索过程,该示例假设四个不同的器件连接到同一1-Wire总线。四个设备的ROM代码如下所示:

ROM1 00110101...
ROM2 10101010...
ROM3 11110101...
ROM4 00010001...

搜索过程如下:

总线主站通过发出复位脉冲来开始初始化序列。从设备响应方式 同时发出存在脉冲。

然后,总线主控器在1-Wire总线上发出搜索ROM命令。

每个器件将响应搜索ROM命令,将各自ROM码的第一位值放到1-Wire总线上。然后,主站将读取总线值。在这种情况下,ROM1和ROM4将在0-Wire总线上放置一个1,即它们将其拉低。ROM2和ROM3将在1-Wire总线上放置一个1,允许线路保持高电平。结果是线路上所有设备的逻辑 AND;因此,总线主站将读取 0。1-Wire总线上的所有器件都将响应此读取,方法是将其ROM码的第一位补码放到1-Wire总线上:ROM1和ROM4在1-Wire总线上放置一个1,使线路保持高电平,ROM2和ROM3将在总线上放置一个0。 把它拉低。总线主控服务器现在将再次读取总线,并将再次读取 0。

根据从设备ROM代码,总线主站可以从两次读取中获得四种可能的数据组合。这些组合可以解释如下:

 

 

00 有些连接到总线的设备在当前ROM代码位位置具有冲突位。
01 连接到总线的所有设备在此位位置都有一个 0。
10 连接到总线的所有设备在此位位置都有一个 1。
11 没有器件连接到1-Wire总线。

 

在本例中,总线主控器在每次读取时都读取0,这告诉它1-Wire总线上有一些器件在第一个ROM代码位置为0,而其他器件则为1。

为了响应前面的数据,总线主控服务器将 0 写入总线。这将取消选择此搜索通道其余部分的ROM2和ROM3,仅保留ROM1和ROM4“连接”到1-Wire总线。

总线主站再执行两次读取,并收到 0,后跟 1。这表示仍连接到总线的所有设备都将 0 作为其第二个 ROM 数据位。

然后,总线主站写入 0 以保持 ROM1 和 ROM4 连接到总线。

总线主站再次执行两次读取并接收两个 0。这向主机表明,1-Wire总线上的一个器件在第三个ROM码位置为0,另一个器件为1。

总线主站将 0 写入总线,总线取消选择 ROM1,并将 ROM4 保留为唯一仍连接的设备。

总线主站从 ROM4 读取 ROM 位的其余部分,并根据需要继续访问 ROM4 设备。这样就完成了第一个 ROM 搜索传递;总线主站现在通过学习4-Wire总线上的ROM码,唯一地识别了1-Wire总线上的一个从机(ROM<>)。

总线主站通过重复步骤 1 到 7 来启动新的 ROM 搜索序列。

总线主站现在将 1 写入总线(而不是步骤 0 中所做的 8)。这会解耦 ROM4,只留下 ROM1 仍处于连接状态。

总线主站现在从ROM1读取ROM位的其余部分,并可以根据需要与ROM1设备通信。这样就完成了第二个ROM搜索过程,主设备现在已经识别了另一个从设备(ROM1)。

总线主站通过重复步骤 1 到 3 开始新的 ROM 搜索。

总线主控服务器现在将 1 写入总线(而不是步骤 0 中所做的 4)。这将取消选择此搜索传递的其余部分的 ROM1 和 ROM4,只留下耦合到总线的 ROM2 和 ROM3。

总线主站执行两次读取并接收两个 0。

总线主站将 0 写入总线,使 ROM3 去耦,只留下 ROM2 连接到总线。

总线主站从 ROM2 读取 ROM 位的其余部分,并在需要时与 ROM2 设备通信。这样就完成了第三次ROM搜索,主设备现在已经识别了ROM2从设备。

总线主站通过重复步骤 13 到 15 开始第四次也是最后一次 ROM 搜索。

总线主站将 1 写入总线(而不是步骤 0 中所做的 16),这会解耦 ROM2,只留下 ROM3 连接到总线。

总线主站从 ROM3 读取 ROM 位的其余部分,并在需要时与 ROM3 设备通信。这样就完成了第四次ROM搜索传递,在此期间,主设备识别了ROM3设备。此时,主站已经识别了总线上的所有从设备,从这一点开始,总线主站可以使用其ROM代码单独寻址任何设备。

注意:总线主控在每次ROM搜索过程中学习一个1-Wire器件的唯一ROM代码。学习一个ROM代码所需的时间是:

960μs + (8 + 3 × 64) 61μs = 13.16m

因此,总线主站每秒能够识别75个不同的1-Wire从器件。

搜索 ROM 代码示例

如下面的原型功能所示,“查找器件”功能以1-Wire复位开始,以确定网络上是否有任何器件,如果有,则唤醒它们。然后调用“First”函数,以跟踪差异位并返回“Next”,该函数在网络上找到每个唯一的设备。

“Next”功能非常广泛,并且大部分工作都是为网络上的每个设备查找每个唯一的64位ROM代码标识符。

 

// FIND DEVICES
void FindDevices(void)
{
unsigned char m;
if(!ow_reset()) //Begins when a presence is detected
{
if(First()) //Begins when at least one part is found
{
numROMs=0;
do
{
numROMs++;
for(m=0;m<8;m++)
{
FoundROM[numROMs][m]=ROM[m]; //Identifies ROM
\\number on found device
} printf("\nROM CODE =%02X%02X%02X%02X\n",
FoundROM[5][7],FoundROM[5][6],FoundROM[5][5],FoundROM[5][4],
FoundROM[5][3],FoundROM[5][2],FoundROM[5][1],FoundROM[5][0]);
}while (Next()&&(numROMs<10)); //Continues until no additional devices are found
}
}
}
// FIRST
// The First function resets the current state of a ROM search and calls
// Next to find the first device on the 1-Wire bus.
//
unsigned char First(void)
{
lastDiscrep = 0; // reset the rom search last discrepancy global
doneFlag = FALSE;
return Next(); // call Next and return its return value
}
// NEXT
// The Next function searches for the next device on the 1-Wire bus. If
// there are no more devices on the 1-Wire then false is returned.
//
unsigned char Next(void)
{
unsigned char m = 1; // ROM Bit index
unsigned char n = 0; // ROM Byte index
unsigned char k = 1; // bit mask
unsigned char x = 0;
unsigned char discrepMarker = 0; // discrepancy marker
unsigned char g; // Output bit
unsigned char nxt; // return value
int flag;
nxt = FALSE; // set the next flag to false
dowcrc = 0; // reset the dowcrc
flag = ow_reset(); // reset the 1-Wire
if(flag||doneFlag) // no parts -> return false
{
lastDiscrep = 0; // reset the search
return FALSE;
}
write_byte(0xF0); // send SearchROM command
do
// for all eight bytes
{
x = 0;
if(read_bit()==1) x = 2;
delay(6);
if(read_bit()==1) x |= 1; // and its complement
if(x ==3) // there are no devices on the 1-Wire
break;

else
{
if(x>0) // all devices coupled have 0 or 1
g = x>>1; // bit write value for search
else
{
// if this discrepancy is before the last
// discrepancy on a previous Next then pick
// the same as last time
if(m0);
else // if equal to last pick 1
g = (m==lastDiscrep); // if not then pick 0
// if 0 was picked then record
// position with mask k
if (g==0) discrepMarker = m;
}
if(g==1) // isolate bit in ROM[n] with mask k
ROM[n] |= k;
else
ROM[n] &= ~k;
write_bit(g); // ROM search write
m++; // increment bit counter m
k = k<<1; // and shift the bit mask k
if(k==0) // if the mask is 0 then go to new ROM
{ // byte n and reset mask
ow_crc(ROM[n]); // accumulate the CRC
n++; k++;
}
}
}while(n<8); //loop until through all ROM bytes 0-7
if(m<65||dowcrc) // if search was unsuccessful then
lastDiscrep=0; // reset the last discrepancy to 0
else
{
// search was successful, so set lastDiscrep,
// lastOne, nxt
lastDiscrep = discrepMarker;
doneFlag = (lastDiscrep==0);
nxt = TRUE; // indicates search is not complete yet, more
// parts remain
}
return nxt;
}

执行循环冗余校验

循环冗余校验(CRC)可以使用下面显示的功能完成,在执行搜索ROM功能时应包括循环冗余校验。

 

//////////////////////////////////////////////////////////////////////////////
// ONE WIRE CRC
//
unsigned char ow_crc( unsigned char x)
{
dowcrc = dscrc_table[dowcrc^x];
return dowcrc;
}
#define FALSE 0
#define TRUE 1
////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
//
unsigned char ROM[8]; // ROM Bit
unsigned char lastDiscrep = 0; // last discrepancy
unsigned char doneFlag = 0; // Done flag
unsigned char FoundROM[5][8]; // table of found ROM codes
unsigned char numROMs;
unsigned char dowcrc;
unsigned char code dscrc_table[] = {
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53};

读取设备温度

如果网络上只有一个设备,则可以直接使用“读取温度”功能,如下所示。但是,如果网络上有多个设备,为了避免数据冲突,必须使用“匹配ROM”功能来选择特定的设备。

下面的代码示例是专门为DS18S20温度传感器编写的。当此代码与DS18B20或DS1822配合使用时,由于温度寄存器格式的差异,必须稍作修改。有关温度寄存器格式信息,请参阅相应的数据表。

 

void Read_Temperature(void)
{
char get[10];
char temp_lsb,temp_msb;
int k;
char temp_f,temp_c;
ow_reset();
write_byte(0xCC); //Skip ROM
write_byte(0x44); // Start Conversion
delay(5);
ow_reset();
write_byte(0xCC); // Skip ROM
write_byte(0xBE); // Read Scratch Pad
for (k=0;k<9;k++){get[k]=read_byte();}
printf("\n ScratchPAD DATA = %X%X%X%X%X\n",get[8],get[7],get[6],get[5],get[4],get[3],get[2],get[1],get[0]);
temp_msb = get[1]; // Sign byte + lsbit
temp_lsb = get[0]; // Temp data plus lsb
if (temp_msb <= 0x80){temp_lsb = (temp_lsb/2);} // shift to get whole degree
temp_msb = temp_msb & 0x80; // mask all but the sign bit
if (temp_msb >= 0x80) {temp_lsb = (~temp_lsb)+1;} // twos complement
if (temp_msb >= 0x80) {temp_lsb = (temp_lsb/2);}// shift to get whole degree
if (temp_msb >= 0x80) {temp_lsb = ((-1)*temp_lsb);} // add sign bit
printf( "\nTempC= %d degrees C\n", (int)temp_lsb ); // print temp. C
temp_c = temp_lsb; // ready for conversion to Fahrenheit
temp_f = (((int)temp_c)* 9)/5 + 32;
printf( "\nTempF= %d degrees F\n", (int)temp_f ); // print temp. F
}

读取暂存器内存

暂存盘存储器为用户提供所有必要的设备数据,包括温度、TH 和 TL 可编程温度计设置,以及分数温度测量中使用的计数剩余和每 C 计数数据。CRC字节也包含在暂存器存储器中。

 

void Read_ScratchPad(void)
{
int j;
char pad[10];
printf("\nReading ScratchPad Data\n");
write_byte(0xBE);
for (j=0;j<9;j++){pad[j]=read_byte();}
printf("\n ScratchPAD DATA =
%X%X%X%X%X%X\n",pad[8],pad[7],pad[6],pad[5],pad[4],pad[3],pad[2],pad[1],pad[0]);
}

“读取ROM”命令用于在网络上只有一个设备时查找64位ROM代码。多个设备需要使用“搜索ROM”功能。

 

void Read_ROMCode(void)
{
int n;
char dat[9];
printf("\nReading ROM Code\n");
ow_reset();
write_byte(0x33);
for (n=0;n<8;n++){dat[n]=read_byte();}
printf("\n ROM Code = %X%X%X%X\n",dat[7],dat[6],dat[5],dat[4],dat[3],dat[2],dat[1],dat[0]);
}

“匹配ROM”功能必须提供64位ROM-ID才能选择网络上的单个设备。

 

// Perform Match ROM
//
unsigned char Send_MatchRom(void)
{
unsigned char i;
if(ow_reset()) return false;
write_byte(0x55); // match ROM
for(i=0;i<8;i++)
{
write_byte(FoundROM[numROMs][i]); //send ROM code
}
return true;
}

审核编辑:郭婷

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

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

×
20
完善资料,
赚取积分