|
在串口通訊程序中,經(jīng)常要收到數(shù)據(jù)包,常有網(wǎng)友問及如何從這些數(shù)據(jù)包中提取需要的數(shù)據(jù),如何處理校驗(yàn)等,在這篇文章里我舉兩個(gè)例子予以說明,程序說明為VC++6.0。關(guān)于串口編程建立程序的細(xì)節(jié),請參閱我主頁上的其它文章。同時(shí),此文也適于其它通訊程序中艱數(shù)據(jù)報(bào)文的處理。
首先,應(yīng)該指出的是,所有這些處理均在串口事件處理函數(shù)oncommunication()中進(jìn)行。每當(dāng)串口緩沖區(qū)中有一個(gè)或一個(gè)以上字符時(shí)觸發(fā)串口通訊事件,該事件就驅(qū)動(調(diào)用)串口事件通訊處理函數(shù)oncommunication(),在這里就可以對接收到的數(shù)據(jù)進(jìn)行處理,提取需要的數(shù)據(jù)。
舉兩個(gè)例子,一個(gè)是較為簡單的位數(shù)據(jù)格式的處理,另一個(gè)是NMEA無線通訊格式的處理,最后回答一位網(wǎng)友提出的問題,大家也可以探討一下。
1.問題:
一個(gè)數(shù)據(jù)包,其串頭為一個(gè)字符,字符值為7EH(16進(jìn)制)'~',其后緊跟一字符‘E’,然后是數(shù)據(jù)串,串尾也為字符值為7EH的一個(gè)字符: 即 ~Exxxxxx...~ 如何處理這些數(shù)據(jù)? 我們?nèi)砸源谡{(diào)試助手源程序及其詳細(xì)編程過程之一 中的OnComm()處理為例: void CSCommTestDlg::OnComm() { // TODO: Add your control notification handler code here VARIANT variant_inp; COleSafeArray safearray_inp; LONG len,k; BYTE rxdata[2048]; //設(shè)置BYTE數(shù)組 An 8-bit integerthat is not signed. CString strtemp; if(m_ctrlComm.GetCommEvent()==2) //事件值為2表示接收緩沖區(qū)內(nèi)有字符 { ////////以下你可以根據(jù)自己的通信協(xié)議加入處理代碼 variant_inp=m_ctrlComm.GetInput(); //讀緩沖區(qū) safearray_inp=variant_inp; //VARIANT型變量轉(zhuǎn)換為ColeSafeArray型變量 len=safearray_inp.GetOneDimSize(); //得到有效數(shù)據(jù)長度 for(k=0;k<len;k++) safearray_inp.GetElement(&k,rxdata+k);//轉(zhuǎn)換為BYTE型數(shù)組 for(k=0;k<len;k++) //將數(shù)組轉(zhuǎn)換為Cstring型變量 { BYTE bt=*(char*)(rxdata+k); //字符型 strtemp.Format("%c",bt); //將字符送入臨時(shí)變量strtemp存放 m_strRXData+=strtemp; //加入接收編輯框?qū)?yīng)字符串,在這兒,編輯框不是必須的,可做相應(yīng)處理 char ch=(char)bt; if(ch=='E') { //在此處設(shè)置一個(gè)可以接收數(shù)據(jù)的全局標(biāo)志,說明接收到數(shù)據(jù)前的‘E’標(biāo)志了,下一步可以讀數(shù)據(jù)了,同時(shí)將m_strRXData清空 flag=2; m_strRXData.Empty(); //下一次接收的便為有用的數(shù)據(jù) } if(ch==0x7e) { flag=1; //下面可以提取數(shù)據(jù)了 } if(flag==1) //標(biāo)志為1, { ...//提取數(shù)據(jù) flag=0; //提取完后,置標(biāo)志為0 }
} } //UpdateData(FALSE); //更新編輯框內(nèi)容 }
2 NMEA無線通訊格式的處理
2.1 NMEA-0183報(bào)文格式
字符串(ASCII字符)格式如下: $XXXX,XX,XX,XX,……*hh<CR><LF> $:串頭 XXXX: 串頭 XX:數(shù)據(jù)字段,字母或數(shù)字 XX:數(shù)據(jù)字段,字母或數(shù)字 XX:數(shù)據(jù)字段,字母或數(shù)字 ,:逗號 …… *:星號,串尾 hh:$與*之間所有字符代碼的校驗(yàn)和,(注意:校驗(yàn)和h為半Byte校驗(yàn),*后第1個(gè)h表示高4位校驗(yàn)和,第2個(gè)h表示低4位校驗(yàn)和。得到校驗(yàn)值后,再轉(zhuǎn)換成ASCII字符。) <CR>:0DH,回車控制符 <LF>:0AH,換行控制符
2.2 校驗(yàn)處理
由于數(shù)據(jù)是動態(tài)接收,所以數(shù)據(jù)的處理也是動態(tài)進(jìn)行,盡管有時(shí)會收到幾個(gè)字符才觸發(fā)一個(gè)串口事件,但字符的接收是一個(gè)一個(gè)接收的,因此就可以在程序中先判斷串頭$是否到達(dá),若串頭到達(dá),就可以開始計(jì)算校驗(yàn),直至串尾*到達(dá),這時(shí)*號后面的兩個(gè)字符就是校驗(yàn)碼,收到這兩個(gè)校驗(yàn)字符,就可以與自己計(jì)算的校驗(yàn)值比較,若不正確,就報(bào)錯,并繼續(xù)處理下面的數(shù)據(jù),若正確,則處理接收的字符,提取需要的數(shù)據(jù)。
2.3 程序 CString m_strReceived; CString m_strChecksum; int flag; char ch為每次收到的字符
m_strReceived += (char)ch; switch(ch) { case '$': checksum=0; //開始計(jì)算CheckSum flag=0; break; case '*': flag=2; c2=checksum & 0x0f; c1=((checksum >> 4) & 0x0f); if (c1 < 10) c1+= '0'; else c1 += 'A' - 10; if (c2 < 10) c2+= '0'; else c2 += 'A' - 10; break; case CR: break; case LF: m_strReceived[port-1].Empty(); break; default: if(flag>0) { m_strChecksum += ch; if(flag==1) { strCheck=strCheck+c1+c2; if(strCheck!=m_strChecksum) { m_strReceived.Empty(); } else { strInstruction=m_strReceived[port-1].Left(6); if(strInstruction=="$QGOKU") //如果串頭正確 { char *temp=(char*)((LPCTSTR)m_strReceived);//轉(zhuǎn)換
int speed=(atoi(temp+7));// 提取int 型數(shù)據(jù) char splevel=*(temp+25); //提取 char 型數(shù)據(jù)
}
} m_strChecksum.Empty(); } flag--; } else checksum=checksum^ch; break; }
3 網(wǎng)友的問題
另外,我回答了一位網(wǎng)友的問題,大家也可以探討一下: 問題如下3:
我用你的串口程序收來的十六進(jìn)制數(shù)據(jù)是這個(gè)樣的: 00 10 10 C0 00 F0 F0 AB AC AD 我現(xiàn)在要將高四位取出來,也就是 011C0FFAAA(這點(diǎn)我不會,但我用Left實(shí)現(xiàn)了,可得到的是字符,不是我要的數(shù)值) 我只要011C0FF. 我要把011C0FF進(jìn)行如下的處理 011轉(zhuǎn)化成十進(jìn)制 C不變 0FF也變成十進(jìn)制 后顯示,成 17 C 255
答:右移得到011C0FF后,可將其放在一個(gè)字符型變量CString m_strReceive中: 然后將其轉(zhuǎn)換: char *temp=(char*)((LPCTSTR)m_strReceive;
char tbuf[6]; //temporary viable tbuf[0]=temp[1]; tbuf[1]=temp[2]; tbuf[2]=temp[3]; tbuf[3]=0; //011 最后為0表示結(jié)束 int data1=atoi(tbuf); char chdata2==temp[4]; //C tbuf[0]=temp[5]; tbuf[1]=temp[6]; tbuf[2]=temp[7]; tbuf[3]=0; int data3=atoi(tbuf); //0FF
以上data1,chdata2,data3即為你要的數(shù)據(jù)
|