串口數據接收方式
1、 在oncomm 事件中接收數據:
這種方式能充分mscomm控件的特性。oncomm 事件還可以檢查和處理通訊錯誤;可以通過檢查 commevent 屬性的值來查詢事件和錯誤;對於不定長數據以及對數據進行處理比較複雜的情況,此法不是很方便。
private sub mscomm_oncomm ()
select case mscomm1.commevent
錯誤
case comeventbreak 收到 break。
case comeventcdto cd (rlsd) 超時。
case comeventctsto cts timeout。
case comeventdsrto dsr timeout。
case comeventframe framing error
case comeventoverrun 數據丟失。
case comeventrxover接收緩衝區溢出。
case comeventrxparity parity 錯誤。
case comeventtxfull 傳輸緩衝區已滿。
case comeventdcb 獲取 dcb] 時意外錯誤
事件
case comevcd cd 線狀態變化。
case comevcts cts 線狀態變化。
case comevdsr dsr 線狀態變化。
case comevring ring indicator 變化。
case comevreceive 收到 rthreshold # of chars.
case comevsend 傳輸緩衝區有 sthreshold 個字符
case comeveof 輸入數據流中發現 eof 字符
end select
end sub
2.輪循法采集數據:
a、定時器輪循法
對於數據包方式收發數據以及不需即時響應情況,用輪循法更好些。實際上輪循法最大的好處在於集中處理數據而且不太占用cpu。輪循法要注意定時采集的時間片段大小;這裏用二進製收發模式;使屬性rthreshold、sthreshold為0,屏蔽oncomm事件。
inputmode = cominputmodebinary
rthreshold = 0
sthreshold = 0
private sub tmrcomm_timer()
采用輪循法采集數據
dim rx_buff() as byte
dim okstring as string
dim receivedlen as integer
on error goto errorhandler
tmrcomm.enabled = false 關閉定時器
if commport.inbuffercount > 0 then
receivedlen = commport.inbuffercount
rx_buff = commport.input
okstring = strconv(tempbyte, vbunicode)
if receivedlen = 6 then
if chr(tempbyte(0)) = : and tempbyte(3) = &h0a then
....
end if
if instr(okstring ,:@end*,vbbinarycompare) then
....
end if
end if
tmrcomm.enabled = true 打開定時器
end sub
b、直接輪循法
此法用於接收少量控製命令字;
保存輸入子串的緩衝區
dim instring as string
使用 com1。
mscomm1.commport = 1
9600 波特,無奇偶校驗,8 位數據,一個停止位。
mscomm1.settings = 9600,n,8,1
當輸入占用時,
告訴控件讀入整個緩衝區。
mscomm1.inputlen = 0
打開端口。
mscomm1.portopen = true
將 attention 命令送到調製解調器。
mscomm1.output = atv1q0 & chr$(13)
確保
調製解調器以ok響應。
等待數據返回到串行端口。
do
doevents
buffer$ = buffer$ & mscomm1.input
loop until instr(buffer$, ok & vbcrlf)
從串行端口讀 ok 響應。
關閉串行端口。
mscomm1.portopen = false
如何處理不定長數據的接收
在(zai)處(chu)理(li)串(chuan)口(kou)通(tong)訊(xun)時(shi),經(jing)常(chang)會(hui)遇(yu)到(dao)不(bu)定(ding)長(chang)數(shu)據(ju)的(de)接(jie)收(shou)。由(you)於(yu)通(tong)訊(xun)任(ren)務(wu)不(bu)同(tong)及(ji)編(bian)程(cheng)要(yao)求(qiu)的(de)差(cha)異(yi)所(suo)以(yi)采(cai)用(yong)的(de)方(fang)法(fa)也(ye)有(you)所(suo)不(bu)同(tong)。本(ben)文(wen)就(jiu)此(ci)問(wen)題(ti)進(jin)行(xing)探(tan)討(tao)。不(bu)定(ding)長(chang)數(shu)據(ju)從(cong)數(shu)據(ju)格(ge)式(shi)上(shang)分(fen),可(ke)分(fen)為(wei)有(you)格(ge)式(shi)和(he)無(wu)格(ge)式(shi)。
一、無格式不定長數據的接收
這種格式在實際串口通訊中用得不多,一般隻用傳送字符串數據。問題在於怎麼判斷接收結束。一般用時間延遲的方法解決。
a、對於非握手式通訊,可用一個定時器定時輪循接收,並假定每個輪循接收完成。用oncomm事件接收也可,隻是不如定時器定時輪循接收簡便。
b、對於握手方式通訊,可用直接輪循法提高接收的準確性。下麵是實現此法的函數:
function scomm(scommand as string, comreceive as mscomm) as string
dim nreceivecount as integer
if comreceive.portopen = false then
comreceive.portopen = true
end if
comreceive.output = scommand
do
nreceivecount = comreceive.inbuffercount
sleep (2) api 函數,掛起當前進程一段時間
loop until comreceive.inbuffercount = nreceivecount
if comreceive.portopen = true then
scomm = comreceive.input
end if
end function
注:此函數參照了xth一文。
此法一般是能確保數據接收的正確,但由於windows是多任務操作係統,當有耗時的進程運行時會丟失數據。如果係統會出現這種情況,可增大函數sleep()的參數值。
二、不定長格式數據的接收
對dui於yu不bu定ding長chang數shu據ju接jie收shou最zui好hao的de方fang法fa是shi製zhi定ding通tong訊xun協xie議yi,比bi如ru定ding義yi開kai始shi字zi符fu和he結jie束shu字zi符fu。由you於yu單dan片pian機ji係xi統tong通tong訊xun一yi般ban不bu太tai複fu雜za,沒mei必bi要yao去qu製zhi定ding一yi套tao象xiang通tong用yong計ji算suan機ji間jian通tong訊xun的de協xie議yi,而er根gen據ju單dan片pian機ji係xi統tong的de大da小xiao和he性xing能neng要yao求qiu製zhi定ding通tong訊xun協xie議yi。實shi際ji上shang為wei便bian於yu交jiao流liu、維wei護hu以yi及ji一yi致zhi性xing,可ke製zhi定ding一yi套tao可ke伸shen縮suo的de通tong訊xun協xie議yi。定ding義yi了le開kai始shi字zi符fu和he結jie束shu字zi符fu就jiu容rong易yi實shi現xian不bu定ding長chang格ge式shi數shu據ju通tong訊xun,但dan在zai實shi際ji通tong訊xun編bian程cheng還hai是shi容rong易yi出chu現xian一yi些xie比bi較jiao隱yin蔽bi的de通tong訊xun錯cuo誤wu。下xia麵mian就jiu常chang用yong方fang法fa分fen別bie進jin行xing分fen析xi。
a、定時器輪循法。
假jia定ding每mei個ge輪lun循xun期qi數shu據ju接jie收shou完wan畢bi,並bing在zai每mei個ge輪lun循xun期qi處chu理li數shu據ju,由you於yu有you開kai始shi字zi符fu和he結jie束shu字zi符fu很hen容rong易yi確que定ding接jie收shou數shu據ju的de完wan整zheng性xing。好hao象xiang合he理li設she定ding輪lun循xun時shi間jian值zhi就jiu萬wan無wu一yi失shi了le,但dan被bei動dong接jie收shou數shu據ju時shi無wu論lun如ru何he也ye找zhao不bu合he適shi的de輪lun循xun時shi間jian值zhi,因yin為wei啟qi動dong定ding時shi器qi和he數shu據ju到dao來lai基ji本ben不bu同tong步bu,這zhe就jiu會hui出chu現xian一yi次ci發fa送song的de數shu據ju被bei分fen在zai兩liang個ge輪lun循xun期qi接jie收shou,所suo以yi被bei動dong接jie收shou數shu據ju時shi不bu能neng假jia定ding每mei個ge輪lun循xun期qi數shu據ju接jie收shou完wan畢bi。在zai接jie收shou到dao結jie束shu字zi符fu後hou才cai確que定ding一yi次ci數shu據ju接jie收shou完wan畢bi就jiu可ke解jie決jue此ci問wen題ti。
b、oncomm事件法。
方法和定時器輪循法基本相同,因為每次oncommg事件也隻能接收到一部分數據。在vb的在線幫助中這樣注解“設置 rthreshold 為 1,接收緩衝區收到每一個字符都會使 mscomm 控件產生 oncomm 事件。”。但實際上oncomm事件並不是每收到一個字符便觸發一次 oncomm 事件。oncomm事件是在緩衝區收到幾個甚至幾十個字節數據後才被觸發的。版主認為這是windows多任務使操作係統不能實時響應造成的。如果要在每次oncomm事件接收一個字符似乎可設inputlen屬性為1,但實際行不通。vb在線幫助中“有該屬性在從輸出格式為定長數據的機器讀取數據時非常有用”的注解,好象在說對定長字符有效,但版主發現inputlen設為16,接收16個字符定長數據時卻被當作兩次接收了,一次12個,一次4個。建議在oncomm事件中接收數據要定義通訊協議並檢測數據的完整性。 對於不定長格式數據的接收程序員更喜歡定時器輪循法,也許oncomm事件不好控製吧。
對於不定長數據的接收,最佳方法可能是在oncomm事件中啟動定時器輪循接收,並同時停止oncomm事件的觸發,接收完畢後或超時開啟oncomm事件。
用字符方式收發碼值大於127的字符數據
vb的通訊控件友好、功能強大,編程速度快是眾人皆知的。加上vb的易學、易用,快速開發等特點,數據通訊量不是很大時,在單片機通訊領域廣泛地使用vb開發pc上層通訊軟件。實際開發時會有不少問題,這裏就用字符方式收發碼值大127的字符數據進行討論。
在實際開發中經常遇到通訊隻是用來發送一些控製字符命令和少量數據。在vb的中文在線幫助中有“若數據隻用 ansi 字符集,則用 cominputmodetext”的表述。 ansi字符集是0-127這容易使人誤解為&h88也可用“inputmide=cominputmodetext”方式收發。我剛開始用vb編通訊模塊時就為此迷惑過,網上不少網友也時常問及這種問題。實際上在vb中0-127是可以正常收發的,大於127即&h7f的隻有&h80和&hff能夠收發,其餘ansi字符都被過濾為0。由於串口通訊是以字節收發的,數據如以cominputmodetext模式收發則非字符串數據會被過濾。在vb中用“inputmide = cominputmodebinary” 就可以解決這個問題,隻是收發都必須用動態數組來完成。用cominputmodebinary模(mo)式(shi)編(bian)程(cheng)稍(shao)有(you)點(dian)複(fu)雜(za),調(tiao)試(shi)也(ye)不(bu)直(zhi)觀(guan),對(dui)於(yu)初(chu)學(xue)者(zhe)不(bu)易(yi)掌(zhang)握(wo)。另(ling)外(wai)軟(ruan)件(jian)完(wan)成(cheng)後(hou),在(zai)實(shi)際(ji)應(ying)用(yong)時(shi)會(hui)增(zeng)加(jia)工(gong)程(cheng)維(wei)護(hu)難(nan)度(du),因(yin)為(wei)對(dui)於(yu)二(er)進(jin)製(zhi)代(dai)碼(ma)不(bu)是(shi)易(yi)於(yu)理(li)解(jie)的(de)。比(bi)如(ru)下(xia)端(duan)機(ji)發(fa)送(song)現(xian)場(chang)統(tong)計(ji)數(shu)據(ju)233,cominputmodebinary模式下串口監測到“:a &h233;,它代表a探針的溫度。一般串口監測軟件要麼用ascii方式顯示,要麼用二進製方式顯示。用ascii方式則不能看到&h233,而二進製方式則示不好理解,如果顯示58 65 233 59,我想沒有人喜歡這種方式(如果有更好的方式的話)。但如果顯示“:a 2 3 3 ;”不就解決問題了!用cominputmodetext方(fang)式(shi)就(jiu)可(ke)完(wan)成(cheng)任(ren)務(wu)了(le),隻(zhi)是(shi)多(duo)了(le)一(yi)段(duan)數(shu)據(ju)分(fen)離(li)程(cheng)序(xu)。對(dui)於(yu)一(yi)般(ban)通(tong)訊(xun)要(yao)求(qiu)這(zhe)種(zhong)方(fang)法(fa)不(bu)為(wei)是(shi)一(yi)種(zhong)好(hao)方(fang)法(fa)。由(you)於(yu)通(tong)訊(xun)任(ren)務(wu)是(shi)多(duo)種(zhong)多(duo)樣(yang)的(de),有(you)時(shi)候(hou)這(zhe)種(zhong)方(fang)法(fa)就(jiu)有(you)點(dian)力(li)不(bu)從(cong)心(xin)了(le),如(ru)傳(chuan)送(song)較(jiao)多(duo)的(de)的(de)數(shu)據(ju)時(shi),這(zhe)會(hui)顯(xian)著(zhu)地(di)增(zeng)加(jia)通(tong)訊(xun)量(liang),通(tong)訊(xun)變(bian)得(de)複(fu)雜(za)了(le),對(dui)於(yu)單(dan)片(pian)機(ji)係(xi)統(tong)就(jiu)不(bu)太(tai)合(he)適(shi)了(le);還hai有you一yi些xie特te殊shu要yao求qiu,如ru數shu據ju包bao的de識shi別bie符fu也ye不bu適shi此ci法fa,但dan能neng確que定ding傳chuan送song數shu據ju碼ma值zhi範fan圍wei也ye可ke用yong此ci法fa。下xia麵mian介jie紹shao另ling一yi種zhong方fang法fa,此ci法fa適shi用yong比bi較jiao廣guang,傳chuan送song二er進jin製zhi數shu據ju通tong訊xun量liang增zeng加jia也ye不bu大da。
這種方法實際上很簡單,實際運用中有不少采用此法。原理是一碼分為二碼。如設7e為臨界字符,對於7e則分為7e和0兩個ascii碼,依此類推,8f分為7e和11。接收合並時遇到7e則將7e和後一個ascii碼相加為下字符。下麵給出c語言函數,vb轉換一下便可。
由於c語言不能返回兩個參數,所以用數組指針。
void filt(char code[],char c)
{
if(c==f)
{
if(code[0]>=0x7e)
{
code[1]=code[0]-0x7e;
code[0]=0x7e;
}
else
{
code[1]=0xff; /*0xff作為標記code[1]不可能產生0xff*/
}
}
else if(c==h)
{
if(code[0]!=0x7e)
{
code[1]=0xfe; /*轉換完成標記*/
}
else
{
if(code[1]==0xfe)
{
code[1]=0xff; /*接收下一個碼的標記*/
}
else
{
code[0]=code[0]+code[1];
code[1]=0xfe;
}
}
}
發送時:
char sendchar[2]; /*存儲發送的值*/
....
sendchar[0]=c; /*c為待發ascii碼*/
filt(sendchar,f);
if(sendchar(1)==0xff)
{
..... /*發送sendchar[0]*/
}
else
{
...... /*發送sendchar[0],sendchar[1]*/
}
接收時:
char receivechar[2]; /*存儲接收的值*/
.....
receivechar[0]=c0; /*c0接收的ascii碼*/
filt(receivechar,h);
if(receivechar[1]==0xff)
{
receivechar[1]=c1; /*c1為下一個*/
filt(receivechar,h);
}
else if(receivechar[1]==0xfe)
{
...... /*存儲轉換後的receivechar[0]*/
}
以上代碼僅提供一種思路,實際情況視編程需要而定。
串口通訊問答錄
1、q:各位vb高手:我有一個問題想請教一下。我從com口用bin方式接收到數據(一串漢字),存入一byte數組,但無法還原為一串漢字,我認為是ansi和unicode的轉換,請問如何轉換。
例:字符串“我”,按bin方式接收成一byte數組,其值為“206,210”,如用“chr(206)+chr(210)”卻無法得到“我”,實際上“我”=chr(-12860)請問如何能實現byte數組(206,210)與字符串“我”之間的轉換?萬分感謝!!!
jy
1999.10
a:經chr(206)+chr(210)轉換後實際上變成了兩個unicode字符,四個字節了。漢字的收發必須用binary方式。下麵的程序能實現漢字收發。
發:
dim ytemp() as byte
dim stemp as string
stemp = 你好!
ytemp = strconv(stemp, vbfromunicode)
debug.print ubound(ytemp)
mscomm1.output = ytemp
收:
private sub msctest_oncomm()
中文收發
dim ytemp() as byte
dim stemp as string
dim i as integer
if msctest.inbuffercount > 0 then
i = msctest.inbuffercount
ytemp = msctest.input
stemp = strconv(ytemp, vbunicode)
txttest1.text = stemp
end if
end sub
deson
1999-10-16
--------------------------------------------------------------------------------
2、q:各位大俠,在下被兩個問題困擾多時,實在無法找到答案,請各位多多指教。
1、在用mscomm控件設計通訊程序時,我始終無法將asc碼大於127的值發送出去,查閱了vb論壇以前的文章,按部就班也不行,部分 vb 程序如下,請指教:
private sub okbtn_click()
dim data() as byte
dim temp as variant
redim data(10)
for i = 0 to 10
data(i) = int(rnd()*256)
next
temp = data
mscomm.output = temp
end sub
a:接收方式使用了文本方式,用二進製方式即可。
----------------------------------------------
此篇文章從博客轉發
原文地址:
Http://blog.gkong.com/more.asp?id=69123&Name=yangyongxiang