緒論
Maxim的每片1-Wire器件都有唯一的64位注冊碼,它存儲在只讀存儲器(ROM)中。在1-Wire網(wǎng)絡(luò)中,注冊碼用于1-Wire主機(jī)對從機(jī)器件進(jìn)行逐一尋址。如果1-Wire網(wǎng)絡(luò)中從機(jī)器件的ROM碼是未知的,可以通過搜索算法來找到此碼。本文不僅詳細(xì)地解釋了搜索算法,而且還提供了實(shí)現(xiàn)快速整合的例程。該算法適用于任何具有1-Wire接口特性的現(xiàn)有產(chǎn)品及未來產(chǎn)品。圖1. 64位唯一的ROM注冊碼
搜索算法
搜索算法采用的是二叉樹型結(jié)構(gòu),搜索過程沿各分節(jié)點(diǎn)進(jìn)行,直到找到器件的ROM碼即葉子為止;后續(xù)的搜索操作沿著節(jié)點(diǎn)上的其它路徑進(jìn)行,按照同樣的方式直到找到總線上的所有器件代碼。搜索算法首先通過復(fù)位(reset)和在線應(yīng)答脈沖(presence pulse)時隙將1-Wire總線上的所有器件復(fù)位;成功地執(zhí)行該操作后,發(fā)送1 個字節(jié)的搜索命令;搜索命令使1-Wire器件準(zhǔn)備就緒、開始進(jìn)行搜索操作。
搜索命令分為兩類:標(biāo)準(zhǔn)搜索命令(F0 hex)用來搜索連接到網(wǎng)絡(luò)中所有器件;報警或有條件搜索命令(EC hex)只用來搜索那些處于報警狀態(tài)下的器件,這種方式縮小了搜索范圍,可以快速查找到所需要注意的器件。
搜索命令發(fā)出之后,開始實(shí)際的搜索過程。首先總線上的所有從機(jī)器件同時發(fā)送ROM碼(也叫注冊碼)中的第一位(最低有效位) (參見圖1)。與所有的1-Wire通信一樣,無論是讀取數(shù)據(jù)還是向從機(jī)器件寫數(shù)據(jù),都由1-Wire主機(jī)啟動每一位操作。按照1-Wire的特性,當(dāng)所有從機(jī)器件同時應(yīng)答主機(jī)時,結(jié)果相當(dāng)于全部發(fā)送數(shù)據(jù)位的邏輯AND;從機(jī)發(fā)送其ROM碼的第一位后,主機(jī)啟動下一位操作、接著從機(jī)發(fā)送第一位數(shù)據(jù)的補(bǔ)碼;從兩次讀到的數(shù)據(jù)位可以對ROM碼的第一位做出幾種判斷(參見表1)。
表1. 檢索信息位
Bit (true) | Bit (complement) | Information Known |
0 | 0 | There are both 0s and 1s in the current bit position of the participating ROM numbers. This is a discrepancy. |
0 | 1 | There are only 0s in the bit of the participating ROM numbers. |
1 | 0 | There are only 1s in the bit of the participating ROM numbers. |
1 | 1 | No devices participating in search. |
按照搜索算法的要求,1-Wire主機(jī)必須向總線上的從機(jī)發(fā)回一個指定位;如果從機(jī)器件中ROM碼的當(dāng)前位的值與該數(shù)據(jù)位匹配,則繼續(xù)參與搜索過程;若從機(jī)器件的當(dāng)前位與之不匹配,則該器件轉(zhuǎn)換到等待狀態(tài),并保持等待狀態(tài)直到下一個1-Wire復(fù)位信號到來。其余63位ROM碼的搜索依然按照這種‘讀兩位’、‘寫一位’的模式進(jìn)行重復(fù)操作(參見表2)。按照這種搜索算法進(jìn)行下去,最終除了一個從機(jī)器件外所有從機(jī)將進(jìn)入等待狀態(tài),經(jīng)過最后一輪檢測,就可得到最后保留(未進(jìn)入等待狀態(tài))器件的ROM碼。在后續(xù)搜索過程中,選用不同的路徑(或分支)來查找其它器件的ROM碼。需要注意的是本文ROM碼的數(shù)據(jù)位用第1位(最低有效位)到第64 位(最高有效位)表示,而不是我們常用的那種第0位到第63位的模式;這樣設(shè)置允許將差異位置計數(shù)器初始值置為0,為以后的比較提供了方便。
表2. 1-Wire主機(jī)和從機(jī)的搜索過程
Master | Slave |
1-Wire reset stimulus | Produce presence pulse |
Write search command (normal or alarm) | Each slave readies for search. |
Read 'AND' of bit 1 | Each slave sends bit 1 of its ROM number. |
Read 'AND' of complement bit 1 | Each slave sends complement bit 1 of its ROM number. |
Write bit 1 direction (according to algorithm) | Each slave receives the bit written by master, if bit read is not the same as bit 1 of its ROM number then go into a wait state. |
? | ? |
? | ? |
? | ? |
Read 'AND' of bit 64 | Each slave sends bit 64 of its ROM number. |
Read 'AND' of complement bit 64 | Each slave sends complement bit 64 of its ROM number. |
Write bit 64 direction (according to algorithm) | Each slave receives the bit written by master, if bit read is not the same as bit 64 of its ROM number then go into a wait state. |
從表1可以看出:如果所有總線上的器件在當(dāng)前位具有相同值,那么只有一條分支路徑可選;總線上沒有器件響應(yīng)的情況是一種異常狀態(tài),可能是要查找的器件在搜尋過程中與1-Wire總線脫離。如果出現(xiàn)這種情況,應(yīng)中止搜索,并發(fā)出1-Wire復(fù)位信號起始新的搜索過程。如果當(dāng)前位既有0也有1,這種情況稱為位值差異,它對在后續(xù)搜索過程中查找器件起關(guān)鍵作用。搜索算法指定在第一輪查詢中若出現(xiàn)差異(數(shù)據(jù)位/補(bǔ)碼 = 0/0),則選用‘0’路徑。注意:這一點(diǎn)是由本文檔中介紹的特定算法決定的,其它算法中或許首先選用‘1’路徑。記錄最后一次值差異的位置以供下一次搜索使用,表3列出了出現(xiàn)值差異時路徑的選取情況。
表3. 搜索路徑方向
Search Bit Position vs Last Discrepancy | Path Taken |
= | take the '1' path |
< | take the same path as last time (from last ROM number found) |
> | take the '0' path |
搜索算法計算還對最初8位過程中出現(xiàn)的最后一次位差異保持跟蹤;64位注冊碼的前8位是家族碼,在器件的搜索過程中可以按照其家族碼進(jìn)行分類。記錄家族碼的最后一次差異可以用于有選擇性地跳過1-Wire器件的整個分組。如需進(jìn)行選擇性的搜索,可參考關(guān)于高級變量搜索的詳細(xì)解釋。64位ROM碼中包含8位循環(huán)冗余校驗(yàn)碼(CRC);CRC值用于驗(yàn)證是否搜索到正確的ROM碼。ROM碼的排列如圖1所示。
DS2480B系列串口到1-Wire線路的驅(qū)動程序在硬件中實(shí)現(xiàn)了部分與本文檔中相同的搜索算法;詳細(xì)資料請參閱DS2480B數(shù)據(jù)資料和應(yīng)用筆記192,DS2480B串行接口1-Wire線驅(qū)動器的使用的詳細(xì)介紹;從DS2490 USB口到1-Wire橋接器硬件電路中實(shí)現(xiàn)了整個搜索過程。
圖2列出了對一個從器件進(jìn)行搜索的流程圖;注意:右側(cè)Reference欄對在流程圖中出現(xiàn)的符號進(jìn)行了說明;在本文檔的源代碼附錄中也將用到這些專用符號。
圖2. 搜索流程
圖2. 搜索流程,第2部分
搜索算法通過對LastDiscrepancy、LastFamilyDiscrepancy、LastDeviceFlag和ROM_NO值(參見表4)的處理,利用上述流程實(shí)現(xiàn)了兩個不同類型的搜索操作;這兩個操作是搜索1-Wire器件ROM碼的基礎(chǔ)。
First
‘FIRST’操作是搜索1-Wire總線上的第一個從機(jī)器件。該操作是通過將LastDiscrepancy、 LastFamilyDiscrepancy和LastDeviceFlag置零,然后進(jìn)行搜索完成的。最后ROM碼從ROM_NO寄存器中讀出。若1-Wire總線上沒有器件,復(fù)位序列就檢測不到應(yīng)答脈沖,搜索過程中止。Next
‘NEXT’操作是搜索1-Wire總線上的下一個從機(jī)器件;一般情況下,此搜索操作是在‘FIRST’操作之后或上一次‘NEXT’操作之后進(jìn)行;保持上次搜索后這些值的狀態(tài)不變、執(zhí)行又一次搜索即可實(shí)現(xiàn)‘NEXT’操作。之后從ROM_NO寄存器中來讀出新一個ROM碼。若前一次搜索到的是1-Wire上的最后一個器件,則返回一個無效標(biāo)記FALSE,并且把狀態(tài)設(shè)置成下一次調(diào)用搜索算法時將是‘FIRST’操作的狀態(tài)。圖3 (a, b, c)例舉了三個器件的搜索過程,為便于說明,設(shè)器件的ROM碼只有2位。
圖3. 搜索過程舉例
高級變量搜索
有3種利用同一組狀態(tài)變量LastDiscrepancy、LastFamilyDiscrepancy、LastDeviceFlag、ROM_NO實(shí)現(xiàn)的高級變量搜索算法,這幾種高級搜索算法允許來指定作為搜索目標(biāo)或需要跳過搜索的器件的類型(家族碼)以及驗(yàn)證某類型的器件是否在線(參見表4)。Verify
‘VERIFY’操作用來檢驗(yàn)已知ROM碼的器件是否連接在1-Wire總線上,通過提供ROM碼并對該碼進(jìn)行目標(biāo)搜索就可確定此器件是否在線。首先,將ROM_NO寄存器值設(shè)置為已知的ROM碼值,然后將LastDiscrepancy和LastDeviceFlag標(biāo)志位分別設(shè)置為64 (40h)和0; 進(jìn)行搜索操作,然后讀ROM_NO的輸出結(jié)果;如果搜索成功并且ROM_NO中存儲的仍是要搜索器件的ROM碼值,那么此器件就在1-Wire總線上。Target Setup
‘TARGET SETUP’操作就是用預(yù)置搜索狀態(tài)的方式首先查找一個特殊的家族類型,每個1-Wire器件都有一個字節(jié)的家族碼內(nèi)嵌在ROM碼中(參見圖1),主機(jī)可以通過家族碼來識別器件所具有的特性和功能。若1-Wire總線上有多片器件時,通常是將搜索目標(biāo)首先定位在需注意的器件類型上。為了將一個特殊的家族作為搜索目標(biāo),需要將所希望的家族碼字節(jié)放到ROM_NO寄存器的第一個字節(jié)中,并且將ROM_NO寄存器的復(fù)位狀態(tài)置零,然后將LastDiscrepancy設(shè)置為64 (40h);把LastDeviceFlag和LastFamilyDiscrepancy設(shè)置為0。在執(zhí)行下一次搜索算法時就能找出所期望的產(chǎn)品類型的第一個器件;并將此值存入ROM_NO寄存器。需要注意的是如果1-Wire總線上沒有掛接所期望的產(chǎn)品類型的器件,就會找出另一類型的器件,所以每次搜索完成后,都要對ROM_NO寄存器中存儲的結(jié)果進(jìn)行校驗(yàn)。Family Skip Setup
‘FAMILY SKIP SETUP’操作用來設(shè)置搜索狀態(tài)以便跳過搜索到的指定家族中的所有器件,此操作只有在一個搜索過程結(jié)束后才能使用。通過把LastFamilyDiscrepancy復(fù)制到LastDiscrepancy,并清除LastDeviceFlag即可實(shí)現(xiàn)該操作;在下一搜索過程就會找到指定家族中的下一個器件。如果當(dāng)前家族碼分組是搜索過程中的最后一組,那么搜索過程結(jié)束并將LastDeviceFlag置位。表4. 搜索變量狀態(tài)的設(shè)置
? | LastDiscrepancy | LastFamily- Discrepancy | LastDeviceFlag | ROM_NO |
FIRST | 0 | 0 | 0 | result |
NEXT | leave unchanged | leave unchanged | leave unchanged | result |
VERIFY | 64 | 0 | 0 | set with ROM to verify, check if same after search |
TARGET SETUP | 64 | 0 | 0 | set first byte to family code, set rest to zeros |
FAMILY SKIP SETUP | copy from LastFamilyDiscrepancy | 0 | 0 | leave unchanged |
結(jié)論
本文提供的搜索算法可以找出任意給定的1-Wire器件組中獨(dú)一無二的ROM碼,這是保證多點(diǎn)1-Wire總線應(yīng)用的關(guān)鍵,已知ROM碼后就可以對逐一選定的某個1-Wire器件來進(jìn)行操作。本文還對一些變量搜索算法做了詳細(xì)論述,這些變量搜索算法能夠查找或跳過特定類型的1-Wire器件。附錄給出了實(shí)現(xiàn)搜索過程和所有變量搜索算法的例程,并給出了‘C’程序代碼。附錄
下面給出了搜索算法的‘C’程序代碼及實(shí)現(xiàn)每個變量搜索算法的函數(shù)。FamilySkipSetup和TargetSetup函數(shù)實(shí)際上并沒有進(jìn)行搜索操作,它們只不過是用來設(shè)置搜索寄存器,以便在下一次執(zhí)行‘NEXT’操作時能跳過或找到所期望的類型。需要注意的是低級1-Wire函數(shù)可通過調(diào)用TMEX API實(shí)現(xiàn)。這些程序調(diào)用只是用于系統(tǒng)測試,也可以用特定平臺調(diào)用。關(guān)于TMEX API和其它一些1-Wire API的詳細(xì)資料請參考應(yīng)用筆記155。下列TMEX API測試程序的源代碼可從Maxim網(wǎng)頁下載。
// TMEX API TEST BUILD DECLARATIONS #define TMEXUTIL #include "ibtmexcw.h" long session_handle; // END TMEX API TEST BUILD DECLARATIONS // definitions #define FALSE 0 #define TRUE 1 // method declarations int OWFirst(); int OWNext(); int OWVerify(); void OWTargetSetup(unsigned char family_code); void OWFamilySkipSetup(); int OWReset(); void OWWriteByte(unsigned char byte_value); void OWWriteBit(unsigned char bit_value); unsigned char OWReadBit(); int OWSearch(); unsigned char docrc8(unsigned char value); // global search state unsigned char ROM_NO[8]; int LastDiscrepancy; int LastFamilyDiscrepancy; int LastDeviceFlag; unsigned char crc8; //-------------------------------------------------------------------------- // Find the 'first' devices on the 1-Wire bus // Return TRUE : device found, ROM number in ROM_NO buffer // FALSE : no device present // int OWFirst() { // reset the search state LastDiscrepancy = 0; LastDeviceFlag = FALSE; LastFamilyDiscrepancy = 0; return OWSearch(); } //-------------------------------------------------------------------------- // Find the 'next' devices on the 1-Wire bus // Return TRUE : device found, ROM number in ROM_NO buffer // FALSE : device not found, end of search // int OWNext() { // leave the search state alone return OWSearch(); } //-------------------------------------------------------------------------- // Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing // search state. // Return TRUE : device found, ROM number in ROM_NO buffer // FALSE : device not found, end of search // int OWSearch() { int id_bit_number; int last_zero, rom_byte_number, search_result; int id_bit, cmp_id_bit; unsigned char rom_byte_mask, search_direction; // initialize for search id_bit_number = 1; last_zero = 0; rom_byte_number = 0; rom_byte_mask = 1; search_result = 0; crc8 = 0; // if the last call was not the last one if (!LastDeviceFlag) { // 1-Wire reset if (!OWReset()) { // reset the search LastDiscrepancy = 0; LastDeviceFlag = FALSE; LastFamilyDiscrepancy = 0; return FALSE; } // issue the search command OWWriteByte(0xF0); // loop to do the search do { // read a bit and its complement id_bit = OWReadBit(); cmp_id_bit = OWReadBit(); // check for no devices on 1-wire if ((id_bit == 1) && (cmp_id_bit == 1)) break; else { // all devices coupled have 0 or 1 if (id_bit != cmp_id_bit) search_direction = id_bit; // bit write value for search else { // if this discrepancy if before the Last Discrepancy // on a previous next then pick the same as last time if (id_bit_number < LastDiscrepancy) search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); else // if equal to last pick 1, if not then pick 0 search_direction = (id_bit_number == LastDiscrepancy); // if 0 was picked then record its position in LastZero if (search_direction == 0) { last_zero = id_bit_number; // check for Last discrepancy in family if (last_zero < 9) LastFamilyDiscrepancy = last_zero; } } // set or clear the bit in the ROM byte rom_byte_number // with mask rom_byte_mask if (search_direction == 1) ROM_NO[rom_byte_number] |= rom_byte_mask; else ROM_NO[rom_byte_number] &= ~rom_byte_mask; // serial number search direction write bit OWWriteBit(search_direction); // increment the byte counter id_bit_number // and shift the mask rom_byte_mask id_bit_number++; rom_byte_mask <<= 1; // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask if (rom_byte_mask == 0) { docrc8(ROM_NO[rom_byte_number]); // accumulate the CRC rom_byte_number++; rom_byte_mask = 1; } } } while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 // if the search was successful then if (!((id_bit_number < 65) || (crc8 != 0))) { // search successful so set LastDiscrepancy,LastDeviceFlag,search_result LastDiscrepancy = last_zero; // check for last device if (LastDiscrepancy == 0) LastDeviceFlag = TRUE; search_result = TRUE; } } // if no device found then reset counters so next 'search' will be like a first if (!search_result || !ROM_NO[0]) { LastDiscrepancy = 0; LastDeviceFlag = FALSE; LastFamilyDiscrepancy = 0; search_result = FALSE; } return search_result; } //-------------------------------------------------------------------------- // Verify the device with the ROM number in ROM_NO buffer is present. // Return TRUE : device verified present // FALSE : device not present // int OWVerify() { unsigned char rom_backup[8]; int i,rslt,ld_backup,ldf_backup,lfd_backup; // keep a backup copy of the current state for (i = 0; i < 8; i++) rom_backup[i] = ROM_NO[i]; ld_backup = LastDiscrepancy; ldf_backup = LastDeviceFlag; lfd_backup = LastFamilyDiscrepancy; // set search to find the same device LastDiscrepancy = 64; LastDeviceFlag = FALSE; if (OWSearch()) { // check if same device found rslt = TRUE; for (i = 0; i < 8; i++) { if (rom_backup[i] != ROM_NO[i]) { rslt = FALSE; break; } } } else rslt = FALSE; // restore the search state for (i = 0; i < 8; i++) ROM_NO[i] = rom_backup[i]; LastDiscrepancy = ld_backup; LastDeviceFlag = ldf_backup; LastFamilyDiscrepancy = lfd_backup; // return the result of the verify return rslt; } //-------------------------------------------------------------------------- // Setup the search to find the device type 'family_code' on the next call // to OWNext() if it is present. // void OWTargetSetup(unsigned char family_code) { int i; // set the search state to find SearchFamily type devices ROM_NO[0] = family_code; for (i = 1; i < 8; i++) ROM_NO[i] = 0; LastDiscrepancy = 64; LastFamilyDiscrepancy = 0; LastDeviceFlag = FALSE; } //-------------------------------------------------------------------------- // Setup the search to skip the current device type on the next call // to OWNext(). // void OWFamilySkipSetup() { // set the Last discrepancy to last family discrepancy LastDiscrepancy = LastFamilyDiscrepancy; LastFamilyDiscrepancy = 0; // check for end of list if (LastDiscrepancy == 0) LastDeviceFlag = TRUE; } //-------------------------------------------------------------------------- // 1-Wire Functions to be implemented for a particular platform //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // Reset the 1-Wire bus and return the presence of any device // Return TRUE : device present // FALSE : no device present // int OWReset() { // platform specific // TMEX API TEST BUILD return (TMTouchReset(session_handle) == 1); } //-------------------------------------------------------------------------- // Send 8 bits of data to the 1-Wire bus // void OWWriteByte(unsigned char byte_value) { // platform specific // TMEX API TEST BUILD TMTouchByte(session_handle,byte_value); } //-------------------------------------------------------------------------- // Send 1 bit of data to teh 1-Wire bus // void OWWriteBit(unsigned char bit_value) { // platform specific // TMEX API TEST BUILD TMTouchBit(session_handle,(short)bit_value); } //-------------------------------------------------------------------------- // Read 1 bit of data from the 1-Wire bus // Return 1 : bit read is 1 // 0 : bit read is 0 // unsigned char OWReadBit() { // platform specific // TMEX API TEST BUILD return (unsigned char)TMTouchBit(session_handle,0x01); } // TEST BUILD 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}; //-------------------------------------------------------------------------- // Calculate the CRC8 of the byte value provided with the current // global 'crc8' value. // Returns current global crc8 value // unsigned char docrc8(unsigned char value) { // See Application Note 27 // TEST BUILD crc8 = dscrc_table[crc8 ^ value]; return crc8; } //-------------------------------------------------------------------------- // TEST BUILD MAIN // int main(short argc, char **argv) { short PortType=5,PortNum=1; int rslt,i,cnt; // TMEX API SETUP // get a session session_handle = TMExtendedStartSession(PortNum,PortType,NULL); if (session_handle <= 0) { printf("No session, %d\n",session_handle); exit(0); } // setup the port rslt = TMSetup(session_handle); if (rslt != 1) { printf("Fail setup, %d\n",rslt); exit(0); } // END TMEX API SETUP // find ALL devices printf("\nFIND ALL\n"); cnt = 0; rslt = OWFirst(); while (rslt) { // print device found for (i = 7; i >= 0; i--) printf("%02X", ROM_NO[i]); printf(" %d\n",++cnt); rslt = OWNext(); } // find only 0x1A printf("\nFIND ONLY 0x1A\n"); cnt = 0; OWTargetSetup(0x1A); while (OWNext()) { // check for incorrect type if (ROM_NO[0] != 0x1A) break; // print device found for (i = 7; i >= 0; i--) printf("%02X", ROM_NO[i]); printf(" %d\n",++cnt); } // find all but 0x04, 0x1A, 0x23, and 0x01 printf("\nFIND ALL EXCEPT 0x10, 0x04, 0x0A, 0x1A, 0x23, 0x01\n"); cnt = 0; rslt = OWFirst(); while (rslt) { // check for incorrect type if ((ROM_NO[0] == 0x04) || (ROM_NO[0] == 0x1A) || (ROM_NO[0] == 0x01) || (ROM_NO[0] == 0x23) || (ROM_NO[0] == 0x0A) || (ROM_NO[0] == 0x10)) OWFamilySkipSetup(); else { // print device found for (i = 7; i >= 0; i--) printf("%02X", ROM_NO[i]); printf(" %d\n",++cnt); } rslt = OWNext(); } // TMEX API CLEANUP // release the session TMEndSession(session_handle); // END TMEX API CLEANUP }
評論
查看更多