|
VB5.0/6.的MSComm通信控件提供了一系列標(biāo)準(zhǔn)通信命令的接口,它允許建立串口連接,可以連接到其他通信設(shè)備(如Modem). 還可以發(fā)送命令、進(jìn)行數(shù)據(jù)交換以及監(jiān)視和響應(yīng)在通信過程中可能發(fā)生的各種錯(cuò)誤和事件,從而可以用它創(chuàng)建全雙工 、事件驅(qū) 動(dòng)的、高效實(shí)用的通信程序。但在實(shí)際通信軟件設(shè)計(jì)過程中,MSComm控件并非像想像中那樣完美和容易控制.特別是在中文Wln 95/98下通信時(shí)更會(huì)出現(xiàn)問題。下面就從基礎(chǔ)開始介紹,然后逐步討淪MSComm控件在編程中出現(xiàn)的問題以及編程技巧。
一、用MSComm控件通信 1.串口通信基礎(chǔ)知識(shí) 一般悅來,計(jì)算機(jī)都有一個(gè)或多個(gè)串行端口,它們依次為com1、Com2、…,這些串口還提供了外部設(shè)備與pC進(jìn)行數(shù)據(jù)傳輸和 皿信的通道。這些串口在CPU和外設(shè)之間充當(dāng)解釋器的角色。當(dāng)字符數(shù)據(jù)從CPU發(fā)送給外設(shè)時(shí),這些字符數(shù)據(jù)將被轉(zhuǎn)換成串行比特 流數(shù)據(jù);當(dāng)接收數(shù)據(jù)時(shí),比特流數(shù)據(jù)被轉(zhuǎn)換為字符數(shù)據(jù)傳遞給CPU,再進(jìn)一步說,在操作系統(tǒng)方面,Windows用通信驅(qū)動(dòng)程序 (COMM.DRV)調(diào)用API函數(shù)發(fā)送和接收數(shù)據(jù),當(dāng)用通信控件或聲明調(diào)用API函數(shù)時(shí),它門由COMM. DRV解釋并傳遞給設(shè)備驅(qū)動(dòng)程序, 作為一個(gè)vB程序員,要編寫通信程序.只需知道通信控件提供給Windows通信AP1函數(shù)的接口即可.換句話說,只需設(shè)定和監(jiān)視通 信控件的屬性和事件即可。 2.使用Mscomm控件 在開始使用MSComm控件之前。需要先了解其屬性、事件或錯(cuò)誤 屬性 描述 CommPort 設(shè)置或返回通信端口號(hào) Settings 以字符串的形式設(shè)置或返回波特率、奇偶校驗(yàn)、數(shù)據(jù)位和停止位 PortOpen 設(shè)置或返回通信端口的狀態(tài)。也可以打開和關(guān)閉端口 Input 返回和刪除接收緩沖區(qū)中的字符 Output 將字符串寫入發(fā)送緩沖區(qū) CommEvent屬性為通信事件或錯(cuò)誤返回下列值之一。在該控件的對(duì)象庫(kù)中也可以找到這些常量。 常量 值 描述 ComEventBreak 1001 收到了斷開信號(hào) ComEventCTSTO 1002 Clear To Send Timeout。在發(fā)送字符時(shí),在系統(tǒng)指定的事1件內(nèi),CTS(Clear To Send)線是低電平 ComEventDSRTO 1003 Data Set Ready Timeout。在發(fā)送字符時(shí),在系統(tǒng)指定的事件內(nèi),DSR(Data Set Ready)線是低電平 ComEventFrame 1004 數(shù)據(jù)幀錯(cuò)誤。硬件檢測(cè)到一個(gè)數(shù)據(jù)幀錯(cuò)誤 ComEventOverrun 1006 端口溢出。硬件中的字符尚未讀,下一個(gè)字符又到達(dá),并且丟失 ComEventCDTO 1007 Carrier Detect Time。在發(fā)送字符時(shí),在系統(tǒng)指定的事件內(nèi),CD(Carrier Detect)線是低電平。CD 也稱為RLSD(Receive Line Singal Detect,接收線信號(hào)檢測(cè)) ComEventRxOver 1008 接收緩沖區(qū)溢出。在接收緩沖區(qū)中沒有空間 ComEventRxParity 1009 奇偶校驗(yàn)錯(cuò)。硬件檢測(cè)到奇偶校驗(yàn)錯(cuò)誤7 ComEventTxFull 1010 發(fā)送緩沖區(qū)滿。在對(duì)發(fā)送字符排隊(duì)時(shí),發(fā)送緩沖區(qū)滿 ComEventDCB 1011 檢取端口DCB(Device Control Blick)時(shí)發(fā)生了沒有預(yù)料到的錯(cuò)誤 通信事件包含了下面的設(shè)置: 常量 值 描述 ComEvSend 1 發(fā)送緩沖區(qū)中的字符數(shù)比Sthreshold值低 ComEvReceive 2 接收到了Rthreshold個(gè)字符。持續(xù)產(chǎn)生該事件,直到使用了Input屬性刪除了接收緩沖區(qū)中的數(shù)據(jù) ComEvCTS 3 CTS(Clear To Send)線改變 ComEvDSR 4 DSR(Data Set Ready)線改變。當(dāng)DSR從1到0改變時(shí),該事件發(fā)生 ComEvCD 5 CD(Carrier Detect)線改變ComEvRing6檢測(cè)到響鈴信號(hào)。一些URAT(Universal AsynchronousReciver- -Transmitters,通用異步收發(fā)器)不支持該事件 ComEvEOF 7 收到了EOF字符(ASCII字符26) Error消息(MSComm控件)下表列出了MSComm控件可捕獲的錯(cuò)誤消息: 常量 值 描述 ComInvalidPropertyValue 380 無效的屬性值 ComSetNotSupported 383 屬性只讀 ComGetNotSupported 394 屬性只讀 ComPortOpen 8000 端口打開時(shí)該存在無效 8001 超時(shí)設(shè)置必須比0值大 ComPortInvalid 8002 無效的端口號(hào) 8003 屬性只在運(yùn)行時(shí)有效 8004 屬性在運(yùn)行時(shí)是只讀的 ComPortAleadyOpen 8005 端口已經(jīng)打開 8006 設(shè)備標(biāo)識(shí)符無效或不支持 8007 不支持設(shè)備的波特率 8008 指定的字節(jié)大小無效 8009 缺省參數(shù)錯(cuò)誤 8010 硬件不可用(被其他設(shè)備鎖住) 8011 函數(shù)不能分配隊(duì)列 ComNoOpen 8012 設(shè)備沒有打開 8013 設(shè)備已經(jīng)打開 8014 不能使用通信通知 ComSetCommStateFailed 8015 不能設(shè)置通信狀態(tài) 8016 不能設(shè)置通信事件屏蔽 ComPortNotOpen 8018 該存在只在端口打開是有效 8019 設(shè)備忙 ComReadError 8020 通信設(shè)備讀錯(cuò)誤 ComDCBError 8021 檢取端口設(shè)備控制塊時(shí)出現(xiàn)內(nèi)部錯(cuò)誤 搞清楚以上基本屬性后,就可以開始編寫通信許程序了。在VB5.0/6.0中新建一個(gè)工程文件。添加Microsoft Comm Control 5.0組 件,在簡(jiǎn)體Form1中加入Command命令按鈕并取名為CmdTest,MSComm控件取名為MSComm1,加入如下程序代碼。 Private Sub cmdTestClick ( ) ‘打開串口 MSComml.CommPort =2 ‘設(shè)定Com2 If MSComml.PortOpen = False Then MSComm1.Settings = "9600,n,8,1" ‘9600波特率,無校驗(yàn),8位數(shù)據(jù)位,1位停止位 MSComm1.PortOpen = True ‘打開串口 End if MSComm1.OutBufferCount = 0 ‘清空發(fā)送緩沖區(qū) MSComm1.InBufferCount = 0 ‘滑空接收緩沖區(qū) ‘發(fā)送字符數(shù)據(jù)時(shí)注意必須用回車符(vbcr)結(jié)束 MSComm1.Output="This is a qood book ! " &vbCr ‘潑打電話號(hào)碼或發(fā)送AT命令 MSComm1.Output = "ATDT 05778191898 , & vbCr ‘發(fā)送字符數(shù)組數(shù)據(jù)時(shí)注意ByteArray必須事先定義賦值 Dim ByteArray as byte( ) ‘定義動(dòng)態(tài)數(shù)組 ReDim ByteArray(1) ‘重定義數(shù)組大小 ByteArray ( 0 ) =0 ByteArray ( 1 ) = 1 MSComm1.Output = ByteArray End Sub private Sub MScommEvent( ) Select Case MSComm1.CommEvent Case comEvReceive Dim Buffer As Variant MSComm1.InputLen = 0 ‘接收二進(jìn)制數(shù)據(jù) MSComm1.InputMode= ComInputModeBinary Buffer=MSComm1.Input ‘接收字符數(shù)據(jù) MSComm1.InputMode=comInputModeText Buffer = MSComml.Input Case else End Select End sub ( 程序1) 二、中文Win 95/98下的通信問題與解決方法 1.接收的數(shù)據(jù)少于發(fā)送的數(shù)據(jù) 如果通過MSComm控件一次性傳送較多的二進(jìn)制數(shù)據(jù),那么,很可能收到的數(shù)據(jù)不足。例如在設(shè)置為24oobps傳輸率的情況下, 一次性可以傳輸2048個(gè)字符數(shù)據(jù) 那么在大多數(shù)情況下。一次只能收到1200個(gè)字符左右,這址出為新版的MSComm32.OCX中存在一 個(gè)影響傳輸二進(jìn)制數(shù)據(jù)的臭蟲(bug).注意這不是特性。 32位Windows API函數(shù)(以下簡(jiǎn)稱API)使用了幾個(gè)用COMMTIMEOUTS結(jié)構(gòu)表示的限時(shí)變量,WriteTotalTimeOutConstant 即是其 中的一個(gè),它被Windows內(nèi)部設(shè)定為5000(即5秒),這個(gè)常量決定了在通信驅(qū)動(dòng)程序停止傳輸之前花費(fèi)在發(fā)送緩沖區(qū)中數(shù)據(jù)的時(shí)間 的長(zhǎng)短,5秒鐘意味著通信速度為1200bps情況下僅能發(fā)送600個(gè)字符,24oobps情況下僅能發(fā)送1200個(gè)左右的字符。事實(shí)上,在一個(gè) 緩沖區(qū)內(nèi)一次性發(fā)送更多的數(shù)據(jù)是非??赡艿?。這個(gè)bug同樣也能引發(fā)問題,甚至在高速串口門通信情況下,即使系統(tǒng)在使用流控 制,無論叢軟件流(Xon/XofI)還是硬件流(CTS/RTS)。假如數(shù)據(jù)在發(fā)送緩沖區(qū)中時(shí),流控制停止了傳輸,如果停止時(shí)間超過5 秒鐘.則數(shù)據(jù)就會(huì)丟失。在某些環(huán)境下,5秒鐘可能相當(dāng)短.不過也不必?fù)?dān)心, VB 5.0/6.0版本的MSComm控件有一個(gè)新增的重要的 屬性稱為CommID, CommID指的是當(dāng)串口被打開時(shí),被API所調(diào)用的串口句柄或稱標(biāo)志,這也意味著能利用API接口函數(shù)去修改這個(gè) 常量。每次串口關(guān)閉后,Windows會(huì)自動(dòng)將之恢復(fù)為5000,所以,每次打開串口后需要重?cái)卦O(shè)定以下API聲明,其代碼見下程序。 Type COMMTIMEOUTS ReadIntervalTimeout As Long ReadTotalTimeoutMultiplier As Long ReadTotalTimeoutConstant As Long WriteTotalTimeoutMultiplier As Long WriteTotalTimeoutConstant As Long End Type Declare Function SetCommTimeouts Lib "Kernel32" (BYVal hFile As Long, lpComm TimeoutsAs COMMTIMEOUTS) As Long Declare Function GetCommTimeouts Lib "Kernel32" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long Dim timeouts As COMMEOUTS Dim Ret As Long If Comm1.PortOpen = False Then Comm1.PortOpen = True End if Ret=GetCommTimeouts ( Comm1.CommID , timeouts ) ‘Set some default timeouts timeOuts.ReadIntervalTimeout = 1 timeouts.ReadTotalTimeoutMultiplier =1 timeouts.ReadTotalTimeoutConstant =1 timeouts.WriteTotalTimeoutMultiplier =1 timeouts.WriteTotalTimeoutConstant= ( Comm1.OutBufferSize\Val(Comm1.Settings))*10000+1000 Ret=SetCommTimeouts( Comm1.CommID , timeouts ) ( 程序2) 2.如何發(fā)送大于128的字符數(shù)據(jù) 在通信程序中,以單字符方式逐個(gè)發(fā)送數(shù)據(jù)時(shí),每一個(gè)數(shù)據(jù)范圍 0-255(即十六進(jìn)制的00-FF)。在單字符版本的英文Win95或 DOS版的BASIC程序中,只需要將相應(yīng)的數(shù)據(jù)轉(zhuǎn)換成相應(yīng)的字符發(fā)送到通信端口即可。但在中文Win95/98下卻行不通,假設(shè)在中文 Win95/98下運(yùn)行以下程序: Dim i For i=0 to 255 MSComm1.Output=chr(i) Next i 希望在接收端得到預(yù)期的0-255之間的數(shù)據(jù),結(jié)果卻是:前129個(gè)數(shù)據(jù)接收正確,為0-128,后面127個(gè)數(shù)據(jù)為126個(gè)0和一個(gè)255, 造成這種給果的原因在于中文Windows使用的是雙字節(jié)字符集(DBCS)系統(tǒng)。DBCS系統(tǒng)使用0-128之間的數(shù)字表示ASCII字符,大于 128的數(shù)字僅作為前導(dǎo)字符,它只是顯示是一個(gè)非拉丁語系的字符,而并不代表實(shí)際意義。上述程序在調(diào)用CHR()函數(shù)時(shí)用到了 DBCS字符集,岡此產(chǎn)生了此類錯(cuò)誤。那么,如何發(fā)送人于128的數(shù)據(jù)呢?答案是使用字符數(shù)組,將以上程序改為: Dim cc(255) As Byte For i = 0 To 255 cc(i) = i Next i MSComm1.Output = cc Do DoEvents Loop Until MSComm1.OutBufferCount = 0 ‘接收過程 MSComm1_OnComm() Select Case MSComm1.CommEvent Case comEvReceive Dim Buffer As Variant, b1,i MSComm1.InputMode=comInputModeBinery MSComm1.InputLen = 0 Buffer = MSComm1.Input For i=LBound (Buffer) To UBound (Buffer ) Debug.Print Buffer ( i ) ; Next i Case . . . . . 3.如何發(fā)送0字符(00H,NULL) 在VisuaI C++中使用串口控件發(fā)送0字符有些麻煩,但在VB5.0/6.0中只要注意以下兩點(diǎn)即可: (1)設(shè)置MSComm控件的屬性 NullDiscard=False;。 (2)使用二進(jìn)制接收,即用 MSComm1.InputMode=ComInputModeBinary便可以解決問題; 4.如何發(fā)送遞中文字符串(DBcS字符) VB5.0/6.0的各種參考書上均指明MSComm通信控件不能發(fā)送或接收雙字節(jié)字符集系統(tǒng)DBCS)的二進(jìn)制數(shù)據(jù),這對(duì)于我國(guó)及亞洲一些 使用DBCS字符集的國(guó)家不能不說是一大人遺憾。但是我在實(shí)踐中發(fā)現(xiàn),用MSComm控件也可以發(fā)送中文字符,具體方法有以下兩種: (1)直接發(fā)送 直接發(fā)送即把中文字符等同于英文字符。如:MSComm1.Intput= " 這是一行中文數(shù)據(jù)!" ,但這種方法發(fā)送的中文數(shù)據(jù)不能太 長(zhǎng),發(fā)送緩沖區(qū)和接收緩沖區(qū)的大小需設(shè)定為中文字符的兩倍以上,而且發(fā)送與接收系統(tǒng)所處的操作系統(tǒng)版本最好要一致,否則會(huì) 出現(xiàn)接收或發(fā)送緩沖區(qū)溢出之類的錯(cuò)誤。這種方法時(shí)用于一般要求不太高的場(chǎng)合。 (2)間接發(fā)送 在發(fā)送端將漢字或字符轉(zhuǎn)換為機(jī)器內(nèi)碼或區(qū)位碼數(shù)據(jù)數(shù)組,然后將詠轉(zhuǎn)換后的數(shù)據(jù)發(fā)送到串口,在接收端接收到數(shù)據(jù)后,按照 相反的順序得到的數(shù)據(jù)轉(zhuǎn)換為相應(yīng)的漢字或字符,在轉(zhuǎn)換過程中.要用到位運(yùn)算,如取得漢字的內(nèi)碼后需要將高字節(jié)和低字節(jié)分開, 而VB5.0/6.0中并沒有提供此類函數(shù),以下是求整數(shù)高、低字節(jié)的函數(shù)。 Public Function HiByte(a As Integer ) Dim b b= a And &HFF00 b = b / 256 If b<0 Then b = b + 256 HiByte = b End Function Public Function LowByte(a As Integ`er) Dim b b = a And &HFF LowByte = b End Function 5.如何用單機(jī)進(jìn)行通信測(cè)試 通常在寫好了通信程序后需要兩臺(tái)PC或一臺(tái)Pc、一臺(tái)單片機(jī).將通信口連接后進(jìn)行測(cè)試,但很多時(shí)侯因條件限制僅有單臺(tái)PC機(jī), 測(cè)試項(xiàng)目很簡(jiǎn)單,那么能否測(cè)試呢?當(dāng)然可以,而且方法也很簡(jiǎn)單。對(duì)于九針的串口,找一個(gè)廢棄的串口鼠標(biāo),剝外鼠標(biāo)線,將連 接2、3針的線對(duì)接即可;對(duì)于25針的串口,找一枚曲別針(最好有塑料外套的)將它扯直,剝削去兩頭的塑料后在兩頭各彎一個(gè)圓 圈,中間對(duì)忻后直接套接在串口的2、3針上即可。如果但心不夠安全,則可以將5針按地。 ‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘‘ 關(guān)于mscomm的用法,提高篇......[mgwmj]? MSCOMM控件是個(gè)好東西,如果您能夠充分了解他,他會(huì)為您衷心的效勞。 大致看了一下下午有關(guān)討論MSCOMM的話題,覺得有必要說說我的心得,我一般只做硬件,沒有系統(tǒng)的學(xué)過軟件,只是業(yè)余時(shí)間 學(xué)學(xué)用用,多少掌握了一點(diǎn),也在此拿出來玩玩,不知有錯(cuò)沒有,我可是以為我已經(jīng)做的很好了^_^ 這是一個(gè)VB通用串口事件驅(qū)動(dòng)接收程序。一次性接收一個(gè)數(shù)據(jù)包,數(shù)據(jù)包可以為任意字節(jié),保證不會(huì)丟失一個(gè)數(shù)據(jù)! Private Sub MSComm_OnComm() Dim S() As Byte Dim SS(1024) As Byte Static N As Long Static T As Variant If (MSComm.CommEvent = comEvReceive) Then S = MSComm.Input ‘只要有數(shù)據(jù)就收進(jìn)來,哪怕只是一個(gè) If (Timer - T > 0.01) Then ‘間隔10MS以上就認(rèn)為是一個(gè)新的包 text1="" ‘text1用于搜集和顯示接收(HEX格式) N = 0 End If T = Timer For i = 0 To UBound(S) ‘一個(gè)數(shù)據(jù)包可能產(chǎn)生若干個(gè)oncomm事件 Text1.Text = Text1.Text & Right("0" & Hex(S(i)) & "H", 3) + " " SS(N+i)=S(i) ‘接收數(shù)據(jù)包緩存于SS() N=N+UBound(S) Next i End If End Sub |
|
|