單片機(jī)通信協(xié)議處理.doc
《單片機(jī)通信協(xié)議處理.doc》由會員分享,可在線閱讀,更多相關(guān)《單片機(jī)通信協(xié)議處理.doc(7頁珍藏版)》請在裝配圖網(wǎng)上搜索。
單片機(jī)通信協(xié)議處理 現(xiàn)在大部分的儀器設(shè)備都要求能過通過上位機(jī)軟件來操作,這樣方便調(diào)試,利于操作。其中就涉及到通信的過程。在實際制作的幾個設(shè)備中,筆者總結(jié)出了通信程序的通用寫法,包括上位機(jī)端和下位機(jī)端等 1. 自定義數(shù)據(jù)通信協(xié)議 這里所說的數(shù)據(jù)協(xié)議是建立在物理層之上的通信數(shù)據(jù)包格式。所謂通信的物理層就是指我們通常所用到的RS232、RS485、紅外、光纖、無線等等通信方式。在這個層面上,底層軟件提供兩個基本的操作函數(shù):發(fā)送一個字節(jié)數(shù)據(jù)、接收一個字節(jié)數(shù)據(jù)。所有的數(shù)據(jù)協(xié)議全部建立在這兩個操作方法之上。 通信中的數(shù)據(jù)往往以數(shù)據(jù)包的形式進(jìn)行傳送的,我們把這樣的一個數(shù)據(jù)包稱作為一幀數(shù)據(jù)。類似于網(wǎng)絡(luò)通信中的TCPIP協(xié)議一般,比較可靠的通信協(xié)議往往包含有以下幾個組成部分:幀頭、地址信息、數(shù)據(jù)類型、數(shù)據(jù)長度、數(shù)據(jù)塊、校驗碼、幀尾。 幀頭和幀尾:用于數(shù)據(jù)包完整性的判別,通常選擇一定長度的固定字節(jié)組成,要求是在整個數(shù)據(jù)鏈中判別數(shù)據(jù)包的誤碼率越低越好。減小固定字節(jié)數(shù)據(jù)的匹配機(jī)會,也就是說使幀頭和幀尾的特征字節(jié)在整個數(shù)據(jù)鏈中能夠匹配的機(jī)會最小。通常有兩種做法: 一、減小特征字節(jié)的匹配幾率。 二、增加特征字節(jié)的長度。 通常選取第一種方法的情況是整個數(shù)據(jù)鏈路中的數(shù)據(jù)不具有隨即性,數(shù)據(jù)可預(yù)測,可以通過人為選擇幀頭和幀尾的特征字來避開,從而減小特征字節(jié)的匹配幾率。使用第二種方法的情況更加通用,適合于數(shù)據(jù)隨即的場合。通過增加特征字節(jié)的長度減小匹配幾率,雖然不能夠完全的避免匹配的情況,但可以使匹配幾率大大減小,如果碰到匹配的情況也可以由校驗碼來進(jìn)行檢測,因此這種情況在絕大多說情況下比較可靠。 地址信息:主要用于多機(jī)通信中,通過地址信息的不同來識別不同的通信終端。在一對多的通信系統(tǒng)中,可以只包含目的地址信息。同時包含源地址和目的地址則適用于多對多的通信系統(tǒng)。 數(shù)據(jù)類型、數(shù)據(jù)長度和數(shù)據(jù)塊:是主要的數(shù)據(jù)部分。數(shù)據(jù)類型可以標(biāo)識后面緊接著的是命令還是數(shù)據(jù)。數(shù)據(jù)長度用于指示有效數(shù)據(jù)的個數(shù)。 校驗碼:則用來檢驗數(shù)據(jù)的完整性和正確性。通常對數(shù)據(jù)類型、數(shù)據(jù)長度和數(shù)據(jù)塊三個部分進(jìn)行相關(guān)的運算得到。最簡單的做法可是對數(shù)據(jù)段作累加和,復(fù)雜的也可以對數(shù)據(jù)進(jìn)行CRC運算等等,可以根據(jù)運算速度、容錯度等要求來選取。 2. 上位機(jī)和下位機(jī)中的數(shù)據(jù)發(fā)送 物理通信層中提供了兩個基本的操作函數(shù),發(fā)送一個字節(jié)數(shù)據(jù)則為數(shù)據(jù)發(fā)送的基礎(chǔ)。數(shù)據(jù)包的發(fā)送即把數(shù)據(jù)包中的左右字節(jié)按照順序一個一個的發(fā)送數(shù)據(jù)而已。當(dāng)然發(fā)送的方法也有不同。 在單片機(jī)系統(tǒng)中,比較常用的方法是直接調(diào)用串口發(fā)送單個字節(jié)數(shù)據(jù)的函數(shù)。這種方法的缺點是需要處理器在發(fā)送過程中全程參與,優(yōu)點是所要發(fā)送的數(shù)據(jù)能夠立即的出現(xiàn)在通信線路上,能夠立即被接收端接收到。 另外一種方法是采用中斷發(fā)送的方式,所有需要發(fā)送的數(shù)據(jù)被送入一個緩沖區(qū),利用發(fā)送中斷將緩沖區(qū)中的數(shù)據(jù)發(fā)送出去。這種方法的優(yōu)點是占用處理器資源小,但是可能出現(xiàn)需要發(fā)送的數(shù)據(jù)不能立即被發(fā)送的情況,不過這種時延相當(dāng)?shù)男?。對?1系列單片機(jī),比較傾向于采用直接發(fā)送的方式,采用中斷發(fā)送的方式比較占用RAM資源,而且對比直接發(fā)送來說也沒有太多的優(yōu)點。以下是51系列單片機(jī)中發(fā)送單個字節(jié)的函數(shù)。 void SendByte(unsigned char ch) { SBUF = ch; while(TI == 0); TI = 0; } 上位機(jī)中關(guān)于串口通信的方式也有多種,這種方式不是指數(shù)據(jù)有沒有緩沖的問題,而是操作串口的方式不同,因為PC上數(shù)據(jù)發(fā)送基本上都會被緩沖后再發(fā)送。對于編程來說操作串口有三種方式, 一、使用windows系統(tǒng)中自帶的串口通信控件,這種方式使用起來比較簡單,需要注意的是接收時的阻塞處理和線程機(jī)制。 二、使用系統(tǒng)的API直接進(jìn)行串口數(shù)據(jù)的讀取,在windows和linux系統(tǒng)中,設(shè)備被虛擬為文件,只需要利用系統(tǒng)提供的API函數(shù)即可進(jìn)行串口數(shù)據(jù)的發(fā)送和讀取。 三、使用串口類進(jìn)行串口操作。在此只介紹windows環(huán)境下利用串口類編程的方式。 CSerialport是比較好用的串口類。它提供如下的串口操作方法: void WriteToPort(char* string, int len); 串口初始化成功后,調(diào)用此函數(shù)即可向串口發(fā)送數(shù)據(jù)。為了避免串口緩沖所帶來的延時,可以開啟串口的沖刷機(jī)制。 3. 下位機(jī)中的數(shù)據(jù)接收和協(xié)議解析 下位機(jī)接收數(shù)據(jù)也有兩種方式, 一、等待接收,處理器一直查詢串口狀態(tài),來判斷是否接收到數(shù)據(jù)。 二、中斷接收。 兩種方法的優(yōu)缺點在此前的一篇關(guān)于串口通信的文章中詳細(xì)討論過。得出的結(jié)論是采用中斷接收的方法比較好。 數(shù)據(jù)包的解析過程可以設(shè)置到不同的位置。如果協(xié)議比較簡單,整個系統(tǒng)只是處理一些簡單的命令,那么可以直接把數(shù)據(jù)包的解析過程放入到中斷處理函數(shù)中,當(dāng)收到正確的數(shù)據(jù)包的時候,置位相應(yīng)的標(biāo)志,在主程序中再對命令進(jìn)行處理。如果協(xié)議稍微復(fù)雜,比較好的方式是將接收的數(shù)據(jù)存放于緩沖區(qū)中,主程序讀取數(shù)據(jù)后進(jìn)行解析。也有兩種方式交叉使用的,比如一對多的系統(tǒng)中,首先在接收中斷中解析“連接”命令,連接命令接收到后主程序進(jìn)入設(shè)置狀態(tài),采用查詢的方式來解析其余的協(xié)議。 以下給出具體的實例。在這個系統(tǒng)中,串口的命令非常簡單。所有的協(xié)議全部在串口中斷中進(jìn)行。數(shù)據(jù)包的格式如下: 0x55, 0xAA, 0x7E, 幀頭 0x12, 目的地址 0xF0, 源地址 0x02, 數(shù)據(jù)長度 0x23, 0x45, 數(shù)據(jù) SUM, XOR, 累加,異或 0x0D 幀尾 0x55, 0xAA, 0x7E為數(shù)據(jù)幀的幀頭, 0x0D為幀尾, 0x12為設(shè)備的目的地址, 0xF0為源地址, 0x02為數(shù)據(jù)長度, 后面接著兩個數(shù)據(jù)0x23, 0x45, 從目的地址開始結(jié)算累加、異或校驗和,到數(shù)據(jù)的最后一位結(jié)束。 協(xié)議解析的目的,首先判斷數(shù)據(jù)包的完整性,正確性,然后提取數(shù)據(jù)類型,數(shù)據(jù)等數(shù)據(jù),存放起來用于主程序處理。代碼如下: 0x55, 0xAA, 0x7E, 幀頭 0x12, 目的地址 0xF0, 源地址 0x02, 數(shù)據(jù)長度 0x23, 0x45, 數(shù)據(jù) SUM, XOR, 累加,異或 0x0D 幀尾 if(state_machine == 0) // 協(xié)議解析狀態(tài)機(jī) { if(rcvdat == 0x55) // 接收到幀頭第一個數(shù)據(jù) state_machine = 1; else state_machine = 0; // 狀態(tài)機(jī)復(fù)位 } else if(state_machine == 1) { if(rcvdat == 0xAA) // 接收到幀頭第二個數(shù)據(jù) state_machine = 2; else state_machine = 0; // 狀態(tài)機(jī)復(fù)位 } else if(state_machine == 2) { if(rcvdat == 0x7E) // 接收到幀頭第三個數(shù)據(jù) state_machine = 3; else state_machine = 0; // 狀態(tài)機(jī)復(fù)位 } else if(state_machine == 3) { sumchkm = rcvdat; // 開始計算累加、異或校驗和 xorchkm = rcvdat; if(rcvdat == m_SrcAdr) // 判斷目的地址是否正確 state_machine = 4; else state_machine = 0; } else if(state_machine == 4) { sumchkm += rcvdat; xorchkm ^= rcvdat; if(rcvdat == m_DstAdr) // 判斷源地址是否正確 state_machine = 5; else state_machine = 0; } else if(state_machine == 5) { lencnt = 0; // 接收數(shù)據(jù)計數(shù)器 rcvcount = rcvdat; // 接收數(shù)據(jù)長度 sumchkm += rcvdat; xorchkm ^= rcvdat; state_machine = 6; } else if(state _machine == 6 || state _machine == 7) { m_ucData[lencnt++] = rcvdat; // 數(shù)據(jù)保存 sumchkm += rcvdat; xorchkm ^= rcvdat; if(lencnt == rcvcount) // 判斷數(shù)據(jù)是否接收完畢 state_machine = 8; else state_machine = 7; } else if(state_machine == 8) { if(sumchkm == rcvdat) // 判斷累加和是否相等 state_machine = 9; else state_machine = 0; } else if(state_machine == 9) { if(xorchkm == rcvdat) // 判斷異或校驗和是否相等 state_machine = 10; else state_machine = 0; } else if(state_machine == 10) { if(0x0D == rcvdat) // 判斷是否接收到幀尾結(jié)束符 { retval = 0xaa; // 置標(biāo)志,表示一個數(shù)據(jù)包接收到 } state_machine = 0; // 復(fù)位狀態(tài)機(jī) } 此過程中,使用了一個變量state_machine作為協(xié)議狀態(tài)機(jī)的轉(zhuǎn)換狀態(tài),用于確定當(dāng)前字節(jié)處于一幀數(shù)據(jù)中的那個部位,同時在接收過程中自動對接收數(shù)據(jù)進(jìn)行校驗和處理,在數(shù)據(jù)包接收完的同時也進(jìn)行了校驗的比較。因此當(dāng)幀尾結(jié)束符接收到的時候,則表示一幀數(shù)據(jù)已經(jīng)接收完畢,并且通過了校驗,關(guān)鍵數(shù)據(jù)也保存到了緩沖去中。主程序即可通過retval的標(biāo)志位來進(jìn)行協(xié)議的解析處理。 接收過程中,只要哪一步收到的數(shù)據(jù)不是預(yù)期值,則直接將狀態(tài)機(jī)復(fù)位,用于下一幀數(shù)據(jù)的判斷,因此系統(tǒng)出現(xiàn)狀態(tài)死鎖的情況非常少,系統(tǒng)比較穩(wěn)定,如果出現(xiàn)丟失數(shù)據(jù)包的情況也可由上位機(jī)進(jìn)行命令的補(bǔ)發(fā),不過這種情況筆者還沒有碰到。 對于主程序中進(jìn)行協(xié)議處理的過程與此類似,主程序循環(huán)中不斷的讀取串口緩沖區(qū)的數(shù)據(jù),此數(shù)據(jù)即參與到主循環(huán)中的協(xié)議處理過程中,代碼與上面所述完全一樣。 4. 上位機(jī)中的數(shù)據(jù)接收和命令處理 上位機(jī)中數(shù)據(jù)接收的過程與下位機(jī)可以做到完全一致,不過針對不同的串口操作方法有所不同。對于阻賽式的串口讀函數(shù),例如直接進(jìn)行API操作或者調(diào)用windows的串口通信控件,最好能夠開啟一個線程專門用于監(jiān)視串口的數(shù)據(jù)接收,每接收到一個數(shù)據(jù)可以向系統(tǒng)發(fā)送一個消息。筆者常用的CSerialPort類中就是這樣的處理過程。CSerialPort打開串口后開啟線程監(jiān)視串口的數(shù)據(jù)接收,將接收的數(shù)據(jù)保存到緩沖區(qū),并向父進(jìn)程發(fā)送接收數(shù)據(jù)的消息,數(shù)據(jù)將隨消息一起發(fā)送到父進(jìn)程。父進(jìn)程中開啟此消息的處理函數(shù),從中獲取串口數(shù)據(jù)后就可以把以上的代碼拷貝過來使用。 CSerialPort向父類發(fā)送的消息號如下: #define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer. 因此需要手動添加此消息的響應(yīng)函數(shù): afx_msg LONG OnCommunication(WPARAM ch, LPARAM port); ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication) 響應(yīng)函數(shù)的具體代碼如下: LONG CWellInfoView::OnCommunication(WPARAM ch, LPARAM port) { int retval = 0; rcvdat = (BYTE)ch; if(state_machine == 0) // 協(xié)議解析狀態(tài)機(jī) { if(rcvdat == 0x55) // 接收到幀頭第一個數(shù)據(jù) state_machine = 1; else state_machine = 0; // 狀態(tài)機(jī)復(fù)位 } else if(state_machine == 1) { if(rcvdat == 0xAA) // 接收到幀頭第二個數(shù)據(jù) state_machine = 2; else state_machine = 0; // 狀態(tài)機(jī)復(fù)位 ...... 5. 總結(jié) 以上給出的是通信系統(tǒng)運作的基本雛形,雖然簡單,但是可行。實際的通信系統(tǒng)中協(xié)議比這個要復(fù)雜,而且涉及到數(shù)據(jù)包響應(yīng)、命令錯誤、延時等等一系列的問題,在這樣的一個基礎(chǔ)上可以克服這些困難并且實現(xiàn)出較為穩(wěn)定可靠的系統(tǒng)。- 1.請仔細(xì)閱讀文檔,確保文檔完整性,對于不預(yù)覽、不比對內(nèi)容而直接下載帶來的問題本站不予受理。
- 2.下載的文檔,不會出現(xiàn)我們的網(wǎng)址水印。
- 3、該文檔所得收入(下載+內(nèi)容+預(yù)覽)歸上傳者、原創(chuàng)作者;如果您是本文檔原作者,請點此認(rèn)領(lǐng)!既往收益都?xì)w您。
下載文檔到電腦,查找使用更方便
9.9 積分
下載 |
- 配套講稿:
如PPT文件的首頁顯示word圖標(biāo),表示該PPT已包含配套word講稿。雙擊word圖標(biāo)可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設(shè)計者僅對作品中獨創(chuàng)性部分享有著作權(quán)。
- 關(guān) 鍵 詞:
- 單片機(jī) 通信協(xié)議 處理
鏈接地址:http://www.820124.com/p-9157585.html