明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

使用Visual Basic 完成無(wú)線通訊

[摘要]作者: 姜棣昭(中科院空間中心)   一、 概述Visual Basic 是Mcrosoft公司推出的強(qiáng)有力的系列開(kāi)發(fā)軟件之一,而且以其實(shí)用、方便、快捷、開(kāi)發(fā)周期短、廣泛而強(qiáng)大的功能越來(lái)越被廣大編程人員所親賴(lài),廣為流傳,似乎有些專(zhuān)業(yè)的編程人員放棄了Visaul C++ 而改用了Visaul B...
作者: 姜棣昭(中科院空間中心)  
   
一、 概述

Visual Basic 是Mcrosoft公司推出的強(qiáng)有力的系列開(kāi)發(fā)軟件之一,而且以其實(shí)用、方便、快捷、開(kāi)發(fā)周期短、廣泛而強(qiáng)大的功能越來(lái)越被廣大編程人員所親賴(lài),廣為流傳,似乎有些專(zhuān)業(yè)的編程人員放棄了Visaul C++ 而改用了Visaul Basic之類(lèi)的RAD編程工具,盡管Visaul C++在靈活性、代碼緊湊、運(yùn)行速度快及底層開(kāi)發(fā)等方面Visaul Basic無(wú)法比擬的,但編寫(xiě)C++應(yīng)用程序過(guò)長(zhǎng)的調(diào)試和開(kāi)發(fā)周期確實(shí)有點(diǎn)不適應(yīng)當(dāng)今的應(yīng)用程序開(kāi)發(fā)環(huán)境。

在Visaul Basic開(kāi)發(fā)工具中提供了大量的控件(或稱(chēng)控制、組件)供編程人員使用,可以方便的利用這些組件中的屬性、方法、語(yǔ)言等以事件驅(qū)動(dòng)方式開(kāi)發(fā)應(yīng)用程序,還可以利用WINDOWS SDK中的API中的應(yīng)用程序接口等工具開(kāi)發(fā)應(yīng)用程序。VB還自帶一個(gè)控件開(kāi)發(fā)軟件包CDK,利用CDK可以開(kāi)發(fā)自己需要的Controls 。在通訊問(wèn)題中我們可以使用VB提供的通訊控件或調(diào)用WINDOWS API通訊函數(shù)。

二、 VB串行通訊

利用VB開(kāi)發(fā)通信程序主要的方法有兩種,一是利用VB本身提供的控件(CONTRALS),另一種方法是利用WINDOWS API應(yīng)用程序接口,WINDOWS API 主要提供了三個(gè)動(dòng)態(tài)連接庫(kù)KERNEL.EXE、USER.EXE、GDI.EXE供開(kāi)發(fā)人員調(diào)用,其中KERNEL.EXE 主要包括一些底層操作函數(shù),完成一些資源管理、任務(wù)、內(nèi)存等操作,USER.EXE包含了一些與WINDOWS管理有關(guān)的函數(shù),如通訊、菜單、消息、光標(biāo)、插入符、計(jì)時(shí)器以及絕大多數(shù)非顯示函數(shù),GDI.EXE圖形設(shè)備接口庫(kù),主要內(nèi)容為與設(shè)備輸出有關(guān)的函數(shù)。和串口通訊有關(guān)系的函數(shù)BuilidCommDCB、ClearCommBreak、SetCommBreak、FlushComm、GetCommError、GetCommState、WriteComm、ReadComm、SetCommState、CloseComm等均在 \Windows\system 子目錄下的USER.EXE動(dòng)態(tài)連接庫(kù)中,在VB調(diào)用之前應(yīng)該先在全局變量定義處聲明API通訊函數(shù)、定義常量。

在我們的實(shí)踐中,用VB 控件實(shí)現(xiàn)通訊的方法比調(diào)用SDK的API動(dòng)態(tài)連接庫(kù)的方法更加方便、快捷,而且用較少的代碼可以實(shí)現(xiàn)相同的功能,這就是用VB 控件實(shí)現(xiàn)通訊的優(yōu)點(diǎn)所在,下面主要介紹一下利用VB 控件實(shí)現(xiàn)無(wú)線通訊的問(wèn)題。

在VB的控件工具箱中,提供了一個(gè)使用非常方便的串行通訊控件MSComm,它全面的提供了使用RS-232串行通訊上層開(kāi)發(fā)的所有細(xì)則,它既可以使用查詢(xún)方式又可以使用事件驅(qū)動(dòng)方式來(lái)完成串行通訊。

在MSCOMM控件中提供了一系列的編程要素,這些編程要素有屬性、事件和函數(shù),利用這些要素編程,可以實(shí)現(xiàn)幾乎全部的串行通訊功能。在VB中提供了30多個(gè)屬性、一個(gè)事件和兩個(gè)函數(shù),其中主要的屬性為CommPort、Settings、PortOpen、InBufferSize、OutBufferSize、InBufferCount、OutBufferCount、Break、InputLen、Sthreshold、Rthreshold、RTSEnable、CommEvent、ParityReplace、NullDiscard等。

控件MSComm提供了一個(gè)事件OnComm,該事件可以截取串口的任何消息,轉(zhuǎn)入事件處理程序。WINDOWS操作系統(tǒng)的運(yùn)行機(jī)制為事件驅(qū)動(dòng),在VB編程中事件驅(qū)動(dòng)方式同樣是軟件運(yùn)行的主要方式之一,當(dāng)沒(méi)有事件發(fā)生時(shí)程序可能處于某一循環(huán)、等待或任務(wù)狀態(tài)當(dāng)事件發(fā)生時(shí),程序轉(zhuǎn)入事件處理程序。每個(gè)控件下都有一些事件供程序員使用,MSCOMM控件中OnComm事件是唯一的,OnComm可以撲獲通訊時(shí)發(fā)生的串口事件和錯(cuò)誤信息,當(dāng)有串口事件或錯(cuò)誤發(fā)生時(shí),VB會(huì)立刻觸發(fā)一個(gè)OnComm事件,程序就會(huì)自動(dòng)轉(zhuǎn)入OnComm事件處理程序中。CommEvent屬性是OnComm事件的指示器,該屬性在設(shè)計(jì)時(shí)不能使用,在程序運(yùn)行時(shí)為只讀,CommEvent 屬性存有最近的事件或錯(cuò)誤的數(shù)值代碼,可以在程序中隨時(shí)讀取CommEvent 屬性值來(lái)了解通訊的狀況,OnComm事件是和CommEvent屬性密切相關(guān)、一起使用,當(dāng)任何一個(gè)OnComm 事件或錯(cuò)誤發(fā)生時(shí),都會(huì)使得CommEvent屬性值改變,在OnComm事件處理過(guò)程中,可以通過(guò)判斷CommEvent屬性值,對(duì)于不同的屬性值轉(zhuǎn)入不同的事件處理過(guò)程,一般采用的辦法是SELECT CASE…….END SELECT。由于在無(wú)線通訊中沒(méi)有使用有線MODEM,CommEvent 屬性涉及到的有線MODEM的屬性數(shù)值代碼和本項(xiàng)目無(wú)關(guān)。

三、 無(wú)線傳輸接口和協(xié)議

在我們的項(xiàng)目中用Intel 586/120的PC機(jī)為上位機(jī),通訊程序使用VB開(kāi)發(fā)的,用8031單片機(jī)做CPU、 AD574作數(shù)據(jù)采集的下位機(jī),上位機(jī)作數(shù)據(jù)接收和數(shù)據(jù)處理中心站,下位機(jī)實(shí)時(shí)采集數(shù)據(jù)之后,進(jìn)行簡(jiǎn)單的數(shù)據(jù)平均計(jì)算,當(dāng)收到上位機(jī)發(fā)來(lái)的發(fā)送指令之后,開(kāi)始向上位機(jī)發(fā)送數(shù)據(jù)。

上位機(jī)無(wú)線通訊接口使用的是一塊插在ISA擴(kuò)展槽中的無(wú)線MODEM ZX-02,無(wú)線MODEM與KENWOOD公司的TK-378無(wú)線對(duì)講機(jī)相連,數(shù)字信號(hào)通過(guò)無(wú)線MODEM調(diào)制成為音頻信號(hào)之后,送到TK-378無(wú)線對(duì)講機(jī)上的MIC口。

下位機(jī)有一臺(tái)外置無(wú)線MODEM ZX-01,單片機(jī)的數(shù)字信號(hào)經(jīng)過(guò)串口送入無(wú)線MODEM,MODEM對(duì)信號(hào)進(jìn)行調(diào)制后送入KENWOOD TK-378無(wú)線對(duì)講機(jī)上,接收數(shù)據(jù)的方式與上述相同,由TK-378收到信號(hào)后,經(jīng)無(wú)線MODEM將音頻信號(hào)解調(diào)為數(shù)字信號(hào)進(jìn)入計(jì)算機(jī)或單片機(jī)處理。

由于在我們的通訊網(wǎng)絡(luò)中,并非點(diǎn)對(duì)點(diǎn)的通訊,而是一點(diǎn)對(duì)多點(diǎn)的廣播式的通訊方式,因此,我們?cè)谕ㄓ崊f(xié)議中曾加站點(diǎn)識(shí)別碼,每個(gè)站有自己特定的識(shí)別碼,給下位機(jī)編碼可以保證網(wǎng)絡(luò)通訊的有序性。

由于無(wú)線通訊可能會(huì)有空間的燥聲干擾,因此,我們采取了多項(xiàng)抗干擾措施,首先是包頭識(shí)別碼,在發(fā)送了傳輸命令之后,下位機(jī)開(kāi)始以打包的形式傳輸數(shù)據(jù),每一包都有一個(gè)包頭和包尾識(shí)別碼,假如識(shí)別碼有誤,這一次的傳輸為不正常數(shù)據(jù)處理。打包發(fā)送另外一個(gè)原因是TK-270對(duì)講機(jī)連續(xù)發(fā)送數(shù)據(jù)的時(shí)間不能超過(guò)一分鐘,超過(guò)一分鐘就會(huì)自動(dòng)中斷發(fā)送,因此,當(dāng)數(shù)據(jù)較多時(shí)不打包連續(xù)發(fā)送的時(shí)間就會(huì)超過(guò)一分鐘,發(fā)送數(shù)據(jù)中斷。

在下位機(jī)中有32KB的NVRAM,可以保存32KB的數(shù)據(jù)該數(shù)據(jù)可以由上位機(jī)發(fā)送清除命令的方法清楚掉,當(dāng)32KB滿(mǎn)了以后,最早放入的數(shù)據(jù)就會(huì)丟掉,由于我們的采樣速率不高,在慢采的情況下,兩天的時(shí)間才能存滿(mǎn),這樣不論上位機(jī)或下位機(jī)出現(xiàn)斷電、死機(jī)等問(wèn)題,數(shù)據(jù)不會(huì)丟失掉。

在發(fā)送過(guò)程中,由于干擾的原因數(shù)據(jù)傳輸出現(xiàn)錯(cuò)誤,上位機(jī)不給下位機(jī)發(fā)送清楚命令,數(shù)據(jù)保存在NVRAM中,下次上位機(jī)發(fā)送傳輸命令之后,這些數(shù)據(jù)還會(huì)重新發(fā)送到上位機(jī)來(lái),這樣可以避免了線路帶來(lái)的數(shù)據(jù)損失,
從下位機(jī)向上位機(jī)傳送的代碼有ASCII碼和BCD碼,測(cè)量數(shù)據(jù)部分用的是BCD碼主要是為了節(jié)省資源,由上位機(jī)向下位機(jī)發(fā)送的命令均為ASCII碼。

我們還對(duì)字段長(zhǎng)度和包的長(zhǎng)度作了規(guī)定,一個(gè)字段有多少個(gè)字節(jié),一包有多少個(gè)字段組成,如果數(shù)據(jù)最后不夠一整包,也按照整字段的格式作為半包發(fā)送過(guò)來(lái)。

考慮到下位機(jī)的分散性,可能固定在偏遠(yuǎn)、不宜接近的地帶,用上位機(jī)對(duì)下位機(jī)發(fā)送校時(shí)命令并校時(shí)的方法來(lái)統(tǒng)一網(wǎng)絡(luò)時(shí)間。

由于我們使用的無(wú)線MODEM 所限,傳輸速率只能達(dá)到2400BPS,而在我們的使用中1200BPS、N PARITY、8 DATA、1 STOP為較穩(wěn)定狀態(tài)。

上位機(jī)向下位機(jī)發(fā)送的命令有校時(shí)命令并校時(shí)、請(qǐng)內(nèi)存命令、發(fā)送數(shù)據(jù)命令、快采命令和慢采命令等。發(fā)送命令有兩種發(fā)送方式,即手動(dòng)方式和自動(dòng)方式,自動(dòng)方式是由定時(shí)器來(lái)完成的。

在無(wú)線通訊過(guò)程中,除了規(guī)定合理的協(xié)議之外,為了保證通訊的正確性,在數(shù)據(jù)發(fā)送時(shí)適當(dāng)?shù)脑黾友訒r(shí)是必要的,當(dāng)速度較慢的計(jì)算機(jī)向速度較快的計(jì)算機(jī)發(fā)送數(shù)據(jù)時(shí)應(yīng)適當(dāng)?shù)脑黾友訒r(shí)。

四、 應(yīng)用實(shí)例

由于該項(xiàng)目的軟件源代碼較長(zhǎng),我們只拿出和串口通訊有關(guān)的程序片段來(lái)供大家參考。在我們的工作中實(shí)踐了三種通訊方式,即查詢(xún)方式、事件驅(qū)動(dòng)方式、事件驅(qū)動(dòng)轉(zhuǎn)查詢(xún)方式,這三種方式各有利敝,查詢(xún)方式有方便可靠的特點(diǎn),可利用協(xié)議或設(shè)定時(shí)鐘進(jìn)入和退出查詢(xún)狀態(tài),但不是資源的有效利用方式,事件觸發(fā)方式對(duì)于定長(zhǎng)通訊非常有效,但定長(zhǎng)通訊在有些場(chǎng)合不實(shí)用,事件驅(qū)動(dòng)轉(zhuǎn)查詢(xún)方式既有事件驅(qū)動(dòng)的特點(diǎn)又有轉(zhuǎn)查詢(xún)方式特點(diǎn),可以說(shuō)是集二者之長(zhǎng),有效利用資源。下面著重介紹事件驅(qū)動(dòng)轉(zhuǎn)查詢(xún)方式。

首先在公共模塊中定義和ONCOMM有關(guān)的參數(shù):

Global Const MSCOMM_EV_RECEIVE = 2‘收到 Rthreshold 個(gè)字符。該事件將持續(xù)產(chǎn)生直到用 Input 屬性從接收緩沖區(qū)中刪除數(shù)據(jù)。
Global Const MSCOMM_ER_RXOVER = 1008‘接受緩沖區(qū)溢出。接收緩沖區(qū)沒(méi)有空間。
Global Const MSCOMM_ER_TXFULL = 1010 ‘傳輸緩沖區(qū)已滿(mǎn)。傳輸字符時(shí)傳輸緩沖區(qū)已滿(mǎn)

在啟動(dòng)過(guò)程中對(duì)串口和輸入輸出緩沖區(qū)初始化:
Sub Form_Load ()
comm1.Settings = "1200,n,8,1" 設(shè)定波特率1200bps,無(wú)校驗(yàn),8位數(shù)據(jù)位,1位停止位
comm1.CommPort = 1 串口1
comm1.InputLen = 1 一次從輸入緩沖區(qū)中讀取一個(gè)字符
comm1.InBufferSize = 512 定義輸入緩沖區(qū)為512字節(jié)(bytes)
comm1.InBufferCount = 0 清空輸入緩沖區(qū)
comm1.OutBufferCount = 0 清空輸出緩沖區(qū)
comm1.PortOpen = True 啟動(dòng)串口
End Sub
下面是發(fā)送數(shù)傳命令的子過(guò)程,啟動(dòng)該過(guò)程由一個(gè)定時(shí)器控制:
Sub sample_data ()
comm1.RTSEnable = True 將Modem的PTT置高,同時(shí)打開(kāi)對(duì)講機(jī)
Call time_delay 適當(dāng)延時(shí)
comm1.Output = "*TRNS" + Chr$(13) 發(fā)送命令
Do 該循環(huán)用來(lái)檢測(cè)命令是否全部發(fā)送完畢
Loop Until comm1.OutBufferCount = 0
Call time_delay 適當(dāng)延時(shí)
comm1.RTSEnable = False 將Modem的PTT置低,將對(duì)講機(jī)改為接受狀態(tài)
comm1.InBufferCount = 0 清空接收緩沖區(qū)
comm1.Rthreshold = 1 設(shè)定Rthreshold = 1,等待出發(fā)OnComm事件
End Sub
在OnComm編寫(xiě)接收和處理代碼:
Sub Comm1_OnComm ()
Select Case comm1.CommEvent
Case MSCOMM_ER_RXOVER 接收緩沖區(qū)溢出。可插入相應(yīng)的代碼
Case MSCOMM_ER_TXFULL 傳輸緩沖區(qū)已滿(mǎn)?刹迦胩幚泶a
Case MSCOMM_ER_RECEIVE 收到1個(gè)字符,可進(jìn)入以下處理過(guò)程
comm1.Rthreshold = 0 不再響應(yīng)OnComm事件,轉(zhuǎn)入查尋方式接收
ii = 0
iii = 0: sinn = Chr$(42): sinn1 = Chr$(42) 初始化變量
Do
duration = Timer + .2 設(shè)定超時(shí)退出的時(shí)間值
iii = iii + 1
sinn1 = sinn
Do 該循環(huán)判斷輸入緩沖區(qū)是否有數(shù)據(jù)或是否超時(shí)
''dummy% = DoEvents()
Loop Until comm1.InBufferCount >= 1 Or Timer >= duration
If Timer >= duration Then
iii = iii - 1
overtime = True 確定超時(shí)退出,并非正常退出
Exit Do
End If
sinn = comm1.Input 正常退出,取值付給sinn
If header_er = 0 Then
GoTo test_header
End If
If Asc(sinn) > 175 Or Asc(sinn) < 160 Then 判斷包頭是否正確
iii = 0
GoTo station_number_er 包頭不正確,丟掉該數(shù)據(jù)
End If
test_header: header_er = 0 包頭正確,接受到包頭,header_er=0
ssin(iii) = sinn''----put date into string dimantion
station_number = Asc(ssin(1))
package_number = Asc(ssin(2))
sentence_number = Asc(ssin(3))
last_character = Asc(ssin(iii))
station_number_er: Loop Until sinn1 = Chr$(10) and sinn = Chr$(13) 當(dāng)收到正確的包尾0A,0D后退出
If overtime = True Then 如果超時(shí)非正常退出
overtime = False
comm1.Rthreshold = 1 重新啟動(dòng)OnComm事件,等待接收下一包
timer6_wait.Enabled = True
GoTo endsub
End If
以下是接收數(shù)據(jù)正確以后的數(shù)據(jù)處理程序,包括解碼和計(jì)算這里從略。
If package_number = 1 Then 如果接收數(shù)據(jù)正確,而且是最后一包
timer2_sample.Enabled = True 啟動(dòng)控制數(shù)傳命令定時(shí)器
comm1.RTSEnable = True 置PTT為高
comm1.OutBufferCount = 0 清空輸出緩沖區(qū)
Call time_delay 適當(dāng)延時(shí)
comm1.Output = "*MACK" + Chr$(13) 發(fā)送清內(nèi)存命令
Do 檢測(cè)是否發(fā)送完畢
Loop Until comm1.OutBufferCount = 0
Call time_delay 適當(dāng)延時(shí)
comm1.RTSEnable = False 置PTT為低
End If
If package_number > 1 Then 如果不是最后一包
comm1.Rthreshold = 1 啟動(dòng)OnComm準(zhǔn)備接收下一包
timer6_wait.Enabled = True
End If
endsub: header_er = 1: comm1.InBufferCount = 0 ''--empty inbuffer protect from bed data on the inbuffer
End Select
End Sub

五、 結(jié)論

根據(jù)我們實(shí)踐工作的經(jīng)驗(yàn),在本項(xiàng)目中無(wú)線通訊和有線通訊的主要區(qū)別在于用MSCOMM控件的RTSEnable屬性對(duì)無(wú)線Modem的PTT操作以及適當(dāng)?shù)难訒r(shí),延時(shí)的長(zhǎng)短和使用的計(jì)算機(jī)有關(guān)。在開(kāi)發(fā)的過(guò)程中,把握好上述兩條,設(shè)定一個(gè)合理的通信協(xié)議,選擇合適的硬件是至關(guān)重要的。