原文連接配接:http://blog.sina.com.cn/s/blog_57ad1bd20102uxxw.html
1-Wire搜尋算法詳解(3)
#include
#include sbitDQ=P0^7; //1-wire總線
sbitK1=P0^6; //标志變量,用于觀察進出某段代碼所用時間
sbit P_Read=P0^5; //
sbit P_Write=P0^4; //// definitions
#define FALSE 0
#define TRUE //幾個延時函數,供一線低級操作時調用
//如果改用不同的MPU,如12T,則必須修改這幾個函數,確定時間符合協定要求
voidDelay480us(); //@12.000MHz
voidDelay410us(); //@12.000MHz
void Delay3_88us(unsigned char i);//一線低級操作函數
bit OWReset(); //複位
void OWWriteBit(bitbit_value); //寫一位
bit OWReadBit(); //讀一位
void OWWriteByte(unsigned charbyte_value); //寫一個位元組
////搜尋函數
bit OWSearch(); //算法核心函數,完成一次ROM搜尋過程
bit OWFirst(); //調用OWSearch完成第一次搜尋
bit OWNext(); //調用OWSearch完成下一次搜尋
unsigned char docrc8(unsigned charvalue); //執行CRC校驗//全局搜尋變量
unsigned char ROM_NO[8]; //數組,存放本次搜尋到的ROM碼(8個位元組)
charLastDiscrepancy; //每輪搜尋後指向最後一個走0的差異位
charLastFamilyDiscrepancy; //指向家族碼(前8位)中最後一個走0的差異位
bitLastDeviceFlag; //搜到最後一個ROM後,程式通過判别将該變量置1,下輪搜尋時即會結束退出
unsigned char crc8; //CRC校驗變量
//--------------------------------------------------------------------------
// 在單總線上搜尋第一個器件
// 傳回TRUE: 找到, 存入ROM_NO緩沖;FALSE:無裝置
// 先将初始化3個變量,然後調用OWSearch算法進行搜尋
//--------------------------------------------------------------------------
bit OWFirst()
{
LastDiscrepancy = 0;
LastDeviceFlag = FALSE;
LastFamilyDiscrepancy =0;
return OWSearch();
}//--------------------------------------------------------------------------
// 在單總線上搜尋下一個器件
// 傳回TRUE: 找到, 存入ROM_NO緩沖;FALSE:無裝置,結束搜尋
// 在前一輪搜尋的基礎上(3個變量均在前一輪搜尋中有明确的值),再執行一輪搜尋
//--------------------------------------------------------------------------
bit OWNext()
{
return OWSearch();
}
//--------------------------------------------------------------------------
// 單總線搜尋算法,利用了一些狀态變量,這是算法的核心程式,代碼也較長
// 傳回TRUE: 找到, 存入ROM_NO緩沖;FALSE:無裝置,結束搜尋
//--------------------------------------------------------------------------bit OWSearch()
{
charid_bit_number; //訓示目前搜尋ROM位(取值範圍為1-64)
//下面三個狀态變量含義:
//last_zero: 指針,記錄一次搜尋(ROM1-64位)最後一位往0走的混碼點編号
//search_direction:搜尋某一位時選擇的搜尋方向(0或1),也是“一寫”的bit位值
//rom_byte_number: ROM位元組序号,作為ROM_no[]數組的下标,取值為1—8
bit id_bit,cmp_id_bit,search_direction; //二讀(正碼、反碼)、及一寫(決定二叉搜尋方向)
unsigned char rom_byte_mask; //ROM位元組掩碼, //初始化本次搜尋變量
id_bit_number = 1;
last_zero = 0;
rom_byte_number = 0;
rom_byte_mask = 1;
search_result = 0;
//------------------------------------------------------------------
//1。是否搜尋完成(已到最後一個裝置)?
//-------------------------------------------------------------------
if(!LastDeviceFlag) //LastDeviceFlag由上輪搜尋确定是否為最後器件,當然首次進入前必須置False
{
if(OWReset()) //複位總線
{
LastDiscrepancy =0; //複位幾個搜尋變量
LastDeviceFlag = FALSE;
LastFamilyDiscrepancy = 0;
returnFALSE; //如果無應答,傳回F,退出本輪搜尋程式
} OWWriteByte(0xF0); //發送ROM搜尋指令F0H
//=====================================================================
// 開始循環處理1-64位ROM,每位必須進行“二讀”後進行判斷,确定搜尋路徑
// 然後按標明的路徑進行“一寫”,直至完成全部位的搜尋,這樣一次循環
// 可以完成一輪搜尋,找到其中一個ROM碼。
//=====================================================================
do //逐位讀寫搜尋,1-64位循環
{
id_bit =OWReadBit(); //二讀:先讀正碼、再讀反碼
cmp_id_bit = OWReadBit(); if (id_bit &&cmp_id_bit) //二讀11,則無器件退出程式
break;
else //二讀不為11,則需分二種情況
{
//*********************************************
//第一種情況:01或10,直接可明确搜尋方向
if (id_bit != cmp_id_bit)
search_direction = id_bit; //*********************************************
else
{
// 否則就是第二種情況:遇到了混碼點,需分三種可能分析:
// 1。目前位未到達上輪搜尋的“最末走0混碼點”(由LastDiscrepancy存儲)
// 說明目前經曆的是一個老的混碼點,判别特征為目前位在(小于)LastDiscrepancy前
// 不管上次走的是0還是1,隻需按上次走的路即可,該值需從ROM_NO中的目前位擷取
if (id_bit_number < LastDiscrepancy)
search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask)> 0);
// 從
else
// 2。目前位正好為上輪标記的最末的混碼點,這個混碼點也就是上次走0的點
// 那麼這次就需要走1
// 3。除去上二種可能,那就是第3種可能: 這是一個新的混碼點,
// id_bit_number>LastDiscrepancy
//。。然而下一條語句巧妙地将上二種可能合在一起處理,看不懂我也沒辦法了
search_direction = (id_bit_number == LastDiscrepancy);
//************************************************ // 确定了混碼點的路徑方向還沒完事,還需要更新一個指針:last_zero
//這個指針每搜尋完一位後(注意是一bit不是一輪)總是指向新的混碼點
//凡遇到新的混碼點,我們按算法都是先走0,是以凡遇走0的混碼點必須更新此指針
if (search_direction == 0)
{
last_zero = id_bit_number; // 下面二條是程式的進階功能了:64位ROM中的前8位是器件的家族代碼,
// 用LastFamilyDiscrepancy這個指針來記錄前8位ROM中的最末一個混碼點
// 可用于在多類型器件的單線網絡中對家族分組進行操作
if (last_zero < 9)
LastFamilyDiscrepancy = last_zero;
}
} // 确定了要搜尋的方向search_direction,該值即ROM中目前位的值,需要寫入ROM
// 然而64位ROM需分8個位元組存入ROM_NO[],程式使用了一個掩碼位元組rom_byte_mask
//以最低位為例:該位元組值為00000001,如記錄1則二位元組或,寫0則與反掩碼
if (search_direction == 1)
ROM_NO[rom_byte_number] |= rom_byte_mask;
else
ROM_NO[rom_byte_number] &=~rom_byte_mask; // 關鍵的一步操作終于到來了:一寫
OWWriteBit(search_direction); // 一個位的操作終于完成,但還需做些工作,以準備下一位的操作:
// 包括:位變量id_bit_number指向下一位;位元組掩碼左移一位
id_bit_number++;
rom_byte_mask <<= 1; // 如果夠8位一位元組了,則對該位元組計算CRC處理、更新位元組号變量、重設掩碼
if (rom_byte_mask == 0)
{
docrc8(ROM_NO[rom_byte_number]); //CRC計算原理參考其他文章
rom_byte_number++;
rom_byte_mask = 1;
}
}
}
while(rom_byte_number < 8); // ROM bytes編号為0-7
//流程圖中描述從1到64的位循環,本代碼中是利用rom_byte_number<8來判斷的
//=================================================================================
// 一輪搜尋成功,找到的一個ROM碼也校驗OK,則還要處理二個變量
if (!((id_bit_number < 65) || (crc8 != 0)))
{
// 一輪搜尋結束後,變量last_zero指向了本輪中最後一個走0的混碼位
//然後再把此變量儲存在LastDiscrepancy中,用于下一輪的判斷
//當然,last_zero在下輪初始為0,搜尋是該變量是不斷變動的
LastDiscrepancy = last_zero; // 如果這個指針為0,說明全部搜尋結束,再也沒有新ROM号器件了
if (LastDiscrepancy == 0)
LastDeviceFlag = TRUE; //設定結束标志
search_result =TRUE; //傳回搜尋成功
}
//------------------------------------------------------------------
//搜尋完成,如果搜尋不成功包括搜尋到了但CRC錯誤,複位狀态變量到首次搜尋的狀态。
//-------------------------------------------------------------------
if (!search_result ||!ROM_NO[0])
{
LastDiscrepancy = 0;
LastDeviceFlag = FALSE;
LastFamilyDiscrepancy = 0;
search_result = FALSE;
}
return search_result;
}//=====================================================================
至此,OWSearch函數結束。函數實作的是一輪搜尋,如成功,可得到一個ROM碼
//=======================================================================
//=====================================================================
// 1-Wire函數調用所需的延時函數,注意不同MCU下須重調參數
//=====================================================================voidDelay480us() //@12.000MHz
{
unsigned char i, j;
i = 2;
j = 90;
do
{
while (--j);
} while (--i);
}voidDelay410us() //@12.000MHz
{
unsigned char i, j;
i = 2;
j = 40;
do
{
while (--j);
} while (--i);
} void Delay3_88us(unsigned chari) //@12.000MHz 隻能3-80us
{
//i*=3;
i-=1;
while (--i);
}
//=====================================================================
// 一線函數:複位、讀一位、寫一位、寫一位元組
//=====================================================================
bit OWReset()
{
bit result;
unsigned char i;
DQ=0; //拉低總線啟動複位信号
Delay480us();
DQ=1; //Releases the bus
i=53; //延時70us
while(--i);
result = DQ; // 采樣總線上從機存在信号
Delay410us(); //
return result; //
}//-----------------------------------------------------------------------------
void OWWriteBit(bitdat) //寫一位函數
//
{
unsigned char i;
P_Write=1; //寫函數執行的标志變量,用于調試
if(dat) //寫1
{
DQ=0; //Drives DQ low
i=4; //延時6us
u
while(--i);
DQ=1; //釋放總線
i=38; //延時54us,不要看i的值,實測為54us
while(--i); //
}
else //寫0
{
DQ=0; //
i=35; // 延時60
while(--i);
DQ=1; //釋放總線
i=7; //延時10
while(--i);
}
P_Write=0;
}//-----------------------------------------------------------------------------
bitOWReadBit() //讀一位
{
bit result;
unsigned char i;
P_Read=1; //時間隙6+9+55
DQ=0; //Drives DQ low
i=8; //6
while(--i);
DQ=1; //釋放
i=6; //9
while(--i);
result = DQ; // 取樣總線
i=32; //45
while(--i); //
P_Read=0;
return result;
}//-----------------------------------------------------------------------------
void OWWriteByte(unsigned char dat) //寫一位元組
{
char loop;
for (loop = 0; loop < 8; loop++) //低位先傳
{
OWWriteBit(dat & 0x01);
dat >>= 1; //右移
}
}//-----------------------------------------------------------------------------
charOWReadByte(void) //讀一位元組,本例中并未用到
{
char loop, result=0;
for (loop = 0; loop < 8; loop++)
{
result >>= 1;
if (OWReadBit())
result |= 0x80;
}
return result;
}// CRC計算用表
static unsigned char 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};
//--------------------------------------------------------------------------
//疊代計算CRC,傳回目前CRC值
unsigned char docrc8(unsigned char value)
{
// 詳見應用筆記AN27
crc8 = dscrc_table[crc8 ^value]; //^表示按位異或
return crc8;
}//主函數,
void main()
{
bit rslt;
K1=0; //這三個位用于觀察“二讀一寫”
P_Read=0;
rslt =OWFirst(); //搜尋第一個ROM
while(rslt) //如果搜尋成功,繼續搜尋下一個
{
rslt = OWNext();
}
while(1);
}