//Logo Image
作者:楊哲彰(2005-10-02);推薦:徐業良(2005-10-04)

以串列式通訊介面接收音壓計數據VB程式範例

本文描述如何以Visual Basic設計簡易程式,經由RS-232串列式通訊介面,接收與計算來自音壓計的讀值。類似的概念與程式也可以應用在以RS232介面接收其他設備數據。

1.     噪音計與其通訊協定

本範例中所採用的音壓計由群特科技出品,型號為CENTER 321,可透過串列式RS-232通訊介面,由電腦端(或其他控制器)接收其數據。其串列式通訊協定主要內容列於表1,此通訊協定也適用於同系列的CENTER 322

1. CENTER 321 音壓計

1. CENTER 321 串列通訊協定

Baudrate: 9600

Parity: None

Data bits: 8

Stop bits: 1

Command Format: 8 characters (bytes), for example:

02 41 00 00 00 00 00 03

The first character is always 02, and the last 03. The second character is the command character.

Command List

Command

Function

Remarks

K(ASC 4BH)

Ask for model No.

Return 4 bytes

M(ASC 4DH)

MAX/MIN button

 

N(ASC 4EH)

Exit MAX/MIN mode

 

C(ASC 43H)

dBA/dBC button

 

A(ASC 41H)

Send encoded data

Return 15 encoded bytes

a(ASC 61H)

Send encoded data continuously

c(ASC 63H)

Stop sending encoded data

 

X(ASC 58H)

Response from PC when receive data

 

2.     Visual Basic程式設計

2.1 資料處理

Visual Basic進行RS-232傳輸,必須引用串列通訊埠的工具元件,由下拉式選單點選「專案è使用設定元件」後,從「控制項」頁面中勾選「Microsoft Comm Control 6.0」,引用後會在主程式左方的「工具箱」工具列中出現類似電話圖樣的元件,接著便可將之引用到設計表單中,引用至表單內之後,同樣會在其上顯示該電話圖樣,但當程式執行時,此圖樣並不會出現。

接著由屬性視窗設定MSComm1的屬性,其中「CommPort」可設為116不等,代表VB可同時處理至多16組串列式通訊埠,此處僅用到1組,因此設為1;「IputLen」代表從接收暫存區讀取的字元數(長度),此時設為15;「InputMode」為回傳資料的型態,此時設為“comInputModeBinary”,代表回傳資料以二進位方式來處理;「Settings」為通訊速度、同位檢查、資料位元數以及停止位元等。此處設為“9600,n,8,1”,分別代表傳輸速度為9600 bps、無同位檢查(None)、每筆有8個位元的資料與1個停止位元。接著在於表單程式碼的中鍵入表2設定,其中“MSComm1.OpenPort=True”表示開啟MSComm1

2. 程式內容(I)

Private Sub Form_Load()

MSComm1.CommPort = 1

MSComm1.Settings = "9600,N,8,1"

MSComm1.PortOpen = True

End Sub

接著開始撰寫主程式,從MSComm1的發送命令為8個位元組的資料,令其變數名稱為“Outbyte”,型態為8個元素(0 To 7) 、元素型態為位元組(byte)的矩陣,“indat”為接收來自音壓計資料的變數,型態為字串(string)。要得到來自音壓計完整的數據資料,須發送「Command A」,即“02 41 00 00 00 00 00 03”,各個位元組為16進位碼,因此「Commmand A」的發送命令可撰寫如下表3。由於音壓計每接收到一組命令,即刻回傳一組數據資料,因此若要得到連續的回傳資料,可藉由VB「工具箱」工具列中的Timer工具元件達成,Outbyte(0)Outbyte(7)分別設為Command A02H…03H16進位ASCII碼,各個位元組後面要加上“H”,代表採用16進位碼,再由MSComm1.Output = Outbyte將命令送出,Delayms(100)延遲0.1秒後[1],由indat = MSComm1.Input從暫存區接收回傳資料,指定至indat字串變數內。命令送出與接收資料之間最好有時間延遲,如本範例中使用的Delayms(100),此設定可避免或降低發生資料傳輸錯誤的情形。

3. 程式內容(II)

接下來就要針對“indat”資料進行處理,在表單上如圖2所示,包含16text文字欄位,其中15個用來顯示“indat”的資料內容,1個用於顯示其字串長度。首先必須檢查該資料是否正確,每一筆回傳資料為ASCII碼,共含15個位元組,第1個位元組的ASCII碼一定是02”,最後一個位元組(第15個)則為03”,且字串長度為15個位元組,上述條件成立後,此資料才算正確。可使用LenB()函數顯示變數的位元組數(長度),AscB()函數顯示該組位元的ASCII值。下表4程式中,Text16.Text = LenB(indat)將在Text16文字欄中顯示“indat”變數的位元組數(長度),Text1.Text = AscB(RightB(indat,15))表示在Text1文字欄位中,顯示“indat”字串中,由最右邊數來第15個位元組的ASCII值,其餘同類項依此類推,圖2“indat”的資料內容。

2. 資料數據內容

4. 程式內容(II)

音壓計的主要系統狀態如取樣速度、加權模式等等,都可在“indat”的第2與第3位元組的ASCII碼中解讀,因此只要針對此兩組ASCII碼做分析,便可以得到完整的系統訊息。將這兩組ASCII碼以二進位方式轉換後,可分別得到如圖3所示的8位元長度的字串,各個字元位置分別代表不同的系統狀態。接下來在先前圖2表單中再增加兩個text文字欄位,用來分別顯示“indat”的第2組與第3ASCII碼的8位元字串。

3. 音壓計狀態資料

如圖3中第2位元組26”,其8位元的2進位碼為00011010”,其中6個位元代表了不同的系統狀態,可對照表3,例如第2個位元(即右邊數來第3個位元)代表「MAX/MIN」模式狀態,若為1”則代表音壓計正在「MAX/MIN」模式,0”則代表不在此模式,表3中其他項目如「dBA/dBC」、「Fast/Slow」等等可依此類推。

5. 2nd byte 二進位碼對應表

7

6

5

4

3

2

1

0

over

under

Low Bat

Fast/Slow

dBA/dBC

MAX/MIN

 

 

1over

0not over

1under

0not under

1:low

0:normal

1fast

0slow

1dBA

0dBC

1:in mode

0:not in mode

 

 

3位元組的ASCII碼,如圖3中的224”,其8位元二進位碼為11100000”,可描述系統的量測範圍(range),「MAX/MIN」模式,第5與第6的位元描述CENTER 322音壓計的數據紀錄狀態,但於本範例機型CENTER 321中並不會使用到。

6. 3rd byte 二進位值對應表

7

6

5

4

3

2

1

0

 

*

*

 

MAX/MIN

Range

Always 1

*

*

unused

00:not in the mode

01: display Maximum reading

10:display Minimum reading

11:display real-time reading and calculate MAX/MIN values

11:auto

10:30-130

01:50-100

00:30-80

若要取得音壓計的即時音壓讀值,取出“indat”字串中的第6與第7位元組,取其ASCII碼後進行計算,即可取得音壓計即時音壓數值,如圖3中第6與第7位元組分別為1”252”,即代表dB。第8與第9位元組可表示「MAX/MIN」模式下最大音壓讀值,其最小音壓值同樣可由第10與第11位元組計算後取得,皆為同樣計算方式。

2.2 系統整合

4為在VB上整合所有操作功能的介面設計,可顯示即時讀值、最大值、最小值、取樣速度、加權模式、量測範圍、型號詢問等功能。所有文字與數字皆採用Label標籤顯示,共計17Label標籤(Reading Value部分×3Max/Min Values×3Range範圍×4UNDER/OVER×2Fast/Slow×2dBA/dBA×2、型號×1),共有6個按鈕(Command_Click)

l          接收可開始讀取音壓值,即令Timer1啟動(Timer1.Enabled=True,初始預設為關閉(False)

l          斷線則中止接收,即令Timer1關閉(Timer1.Enabled=False)

l          Model No.:可詢問音壓計型號,發送表1中的“Command K”,其命令字串為02H 4BH 00 00 00 00 00 03H,音壓計會回傳一個含4個位元組的字串,其ASCII碼為33 32 31 13,其對應字元即為“3 2 1”,以及一個非印列字元“CR(carriage return)”。若回傳值為33 32 31 13,則將圖4中「CENTER 321Label標籤顯示出來,2秒後隱藏。

l          dBA/dBC:切換加權模式,發送表1中的“Command C”,其命令字串為02H 43H 00 00 00 00 00 03H

l          Max/Min:啟動「Max/Min」模式,發送表1中的“Command M”,其命令字串為02 4DH 00 00 00 00 00 03H,此時「Max/Min Values」欄位內會同時顯示最大值與最小值。

l          Exit Max/Min:離開「Max/Min」模式,發送表1中的“Command N”,其命令字串為02 4EH 00 00 00 00 00 03H,此時不再顯示最大最小值,並隱藏最大值與最小值Label標籤。

在「Reading Value部分」,顯示的讀值可由判斷式決定標籤中字型的顏色,如本範例中,低於75時以綠色顯示(Labelxx.ForeColor = RGB(0, 160, 0))7590為籃色(RGB(0, 0, 255)),超過90則為紅色(RGB(255, 0, 0)),表單也可顯示量測範圍,如圖4中的「30dB-130dB(AUTO)」,當音壓值超出該量測範圍時,會顯示紅色「UNDER」或「OVERLabel標籤。

此外,使用串列式通訊時須注意,不可同時傳送命令,例如本範例中,點選接收後由Timer1重複執行命令送出與資料接收的動作,若此時再點選其他動作如dBA/dBC切換等,送出的不同命令會相衝突而發生錯誤。因此本範例程式設計中,欲執行不同功能(即送出不同的命令)之前,必須先將Timer1關閉之後,再個別發送功能命令,這些注意措施可參見附錄A內各個Command_Click的程式碼撰寫內容。

4. 介面設計

5. 軟體介面執行情形

附錄A完整程式碼

Dim Outbyte(0 To 7) As Byte

Dim Outbyte_ac(0 To 7) As Byte

Dim UH As Integer

Dim LH As Integer

Dim indat As String

Dim N As Double

Dim x1 As Integer

Dim x2 As Integer

Dim y1 As String

Dim y2 As String

Dim FastSlow As Integer

Dim AC As Integer

Dim maxmin As Integer

Dim range As Integer

Dim max1 As Integer

Dim max2 As Integer

Dim min1 As Integer

Dim min2 As Integer

Dim under As Integer

Dim over As Integer

Dim model As String

Dim noise As Double

 

Public Sub Delayms(ByVal ms As Long)

Dim t As Long

t = GetTickCount()

While (GetTickCount() - t) < ms

DoEvents

Wend

End Sub

 

Private Sub Command1_Click()

Timer1.Enabled = True

End Sub

 

Private Sub Command2_Click()

Timer1.Enabled = False

Delayms (500)

Label22.Caption = ""

Label29.Caption = ""

Label30.Caption = ""

 

Text1.Text = ""

Text2.Text = ""

Text3.Text = ""

Text4.Text = ""

Text5.Text = ""

Text6.Text = ""

Text7.Text = ""

Text8.Text = ""

Text9.Text = ""

Text10.Text = ""

Text11.Text = ""

Text12.Text = ""

Text13.Text = ""

Text14.Text = ""

Text15.Text = ""

Text18.Text = ""

Text19.Text = ""

End Sub

 

Private Sub Command3_Click()

Timer1.Enabled = False

Delayms (200)

Outbyte_ac(0) = &H2                        

Outbyte_ac(1) = &H4B

Outbyte_ac(2) = &H0

Outbyte_ac(3) = &H0

Outbyte_ac(4) = &H0

Outbyte_ac(5) = &H0

Outbyte_ac(6) = &H0

Outbyte_ac(7) = &H3

MSComm1.InputLen = 4

MSComm1.Output = Outbyte_ac               

Delayms (100)                           

indat = MSComm1.Input

                

If AscB(RightB(indat, 4)) = 51 And AscB(RightB(indat, 3)) = 50 And AscB(RightB(indat, 2)) = 49 Then

Label33.Visible = True

End If

MSComm1.InputLen = 15

MSComm1.OutBufferCount = 0

 

If Text1.Text = "2" Then

Timer1.Enabled = True

End If

 

Delayms (2000)

Label33.Visible = False

 

End Sub

 

Private Sub Command4_Click()

Timer1.Enabled = False

Delayms (100)

Outbyte_ac(0) = &H2                        

Outbyte_ac(1) = &H43

Outbyte_ac(2) = &H0

Outbyte_ac(3) = &H0

Outbyte_ac(4) = &H0

Outbyte_ac(5) = &H0

Outbyte_ac(6) = &H0

Outbyte_ac(7) = &H3

MSComm1.Output = Outbyte_ac               

Delayms (100)                           

MSComm1.InputLen = 15

MSComm1.OutBufferCount = 0

MSComm1.InBufferCount = 0

 

If Text1.Text = "2" Then

Timer1.Enabled = True

End If

End Sub

 

Private Sub Command5_Click()

Timer1.Enabled = False

Delayms (200)

Outbyte_ac(0) = &H2                        

Outbyte_ac(1) = &H4D

Outbyte_ac(2) = &H0

Outbyte_ac(3) = &H0

Outbyte_ac(4) = &H0

Outbyte_ac(5) = &H0

Outbyte_ac(6) = &H0

Outbyte_ac(7) = &H3

MSComm1.Output = Outbyte_ac              

Delayms (100)                                          

MSComm1.InputLen = 15

MSComm1.OutBufferCount = 0

MSComm1.InBufferCount = 0

If Text1.Text = "2" Then

Timer1.Enabled = True

End If

End Sub

 

Private Sub Command6_Click()

Timer1.Enabled = False

Delayms (200)

Outbyte_ac(0) = &H2     

Outbyte_ac(1) = &H4E

Outbyte_ac(2) = &H0

Outbyte_ac(3) = &H0

Outbyte_ac(4) = &H0

Outbyte_ac(5) = &H0

Outbyte_ac(6) = &H0

Outbyte_ac(7) = &H3

MSComm1.Output = Outbyte_ac               

Delayms (100)                           

MSComm1.InputLen = 15

MSComm1.OutBufferCount = 0

MSComm1.InBufferCount = 0

If Text1.Text = "2" Then

Timer1.Enabled = True

End If

End Sub

 

Private Sub Form_Load()

MSComm1.CommPort = 1

MSComm1.Settings = "9600,N,8,1"

MSComm1.PortOpen = True

End Sub

 

Private Sub Timer1_Timer()             

Outbyte(0) = &H2                        

Outbyte(1) = &H41

Outbyte(2) = &H0

Outbyte(3) = &H0

Outbyte(4) = &H0

Outbyte(5) = &H0

Outbyte(6) = &H0

Outbyte(7) = &H3

MSComm1.Output = Outbyte               

Delayms (200)                           

indat = MSComm1.Input                   

UH = AscB(RightB(indat, 10))            

LH = AscB(RightB(indat, 9))            

max1 = AscB(RightB(indat, 8))

max2 = AscB(RightB(indat, 7))

min1 = AscB(RightB(indat, 6))

min2 = AscB(RightB(indat, 5))

 

If LenB(indat) = 15 And AscB(RightB(indat, 15)) And AscB(RightB(indat, 1)) Then

    noise = ((256 * UH) + LH) / 10

Text15.Text = AscB(RightB(indat, 1))

Text14.Text = AscB(RightB(indat, 2))

Text13.Text = AscB(RightB(indat, 3))

Text12.Text = AscB(RightB(indat, 4))

Text11.Text = AscB(RightB(indat, 5))

Text10.Text = AscB(RightB(indat, 6))

Text9.Text = AscB(RightB(indat, 7))

Text8.Text = AscB(RightB(indat, 8))

Text7.Text = AscB(RightB(indat, 9))

Text6.Text = AscB(RightB(indat, 10))

Text5.Text = AscB(RightB(indat, 11))

Text4.Text = AscB(RightB(indat, 12))

Text3.Text = AscB(RightB(indat, 13))

Text2.Text = AscB(RightB(indat, 14))

Text1.Text = AscB(RightB(indat, 15))

Text16.Text = LenB(indat)

 

  If (UH * 256 + LH) / 10 < 75 Then

  Label22.ForeColor = RGB(0, 160, 0)

  End If

 

 If (UH * 256 + LH) / 10 > 75 And (UH * 256 + LH) / 10 < 90 Then

 Label22.ForeColor = RGB(0, 0, 255)

 End If

 

  If (UH * 256 + LH) / 10 > 90 Then

  Label22.ForeColor = RGB(255, 0, 0)

  End If

 

  Label22.Caption = (UH * 256 + LH) / 10

  Label29.Caption = (max1 * 256 + max2) / 10

  Label30.Caption = (min1 * 256 + min2) / 10

  x1 = AscB(RightB(indat, 14))

 

  For N = 0 To 7

  If x1 And 2 ^ N Then y1 = "1" & y1 Else y1 = "0" & y1

  Next

 

  Text18.Text = y1

  FastSlow = Mid(y1, 4, 1)

  maxmin = Mid(y1, 6, 1)

  AC = Mid(y1, 5, 1)

  under = Mid(y1, 2, 1)

  over = Mid(y1, 1, 1)

  y1 = ""

   

  If FastSlow = 1 Then

  Label21.Visible = False

  Label18.Visible = True

  End If

 

  If FastSlow = 0 Then

  Label18.Visible = False

  Label21.Visible = True

  End If

 

  If AC = 1 Then

  Label19.Visible = True

  Label20.Visible = False

  End If

 

  If AC = 0 Then

  Label19.Visible = False

  Label20.Visible = True

  End If

 

  If maxmin = 1 Then

  Label23.Visible = True

  Label29.Visible = True

  Label30.Visible = True

  Label29.ForeColor = RGB(0, 0, 0)

  Label30.ForeColor = RGB(0, 0, 0)

  End If

  If maxmin = 0 Then

  Label23.Visible = False

  Label29.ForeColor = RGB(192, 192, 192)

  Label30.ForeColor = RGB(192, 192, 192)

  End If

 

  If under = 1 Then

  Label31.Visible = False

  Label32.Visible = True

  End If

 

  If over = 1 Then

  Label31.Visible = True

  Label32.Visible = False

  End If

 

  If under = 0 And over = 0 Then

  Label31.Visible = False

  Label32.Visible = False

  End If

  x2 = AscB(RightB(indat, 13))

 

  For N = 0 To 7

      If x2 And 2 ^ N Then y2 = "1" & y2 Else y2 = "0" & y2

  Next

 

  Text19.Text = y2

  range = Mid(y2, 7, 1) & Mid(y2, 8, 1)

  y2 = ""

 

  If range = 11 Then

  Label25.Visible = False

  Label26.Visible = False

  Label27.Visible = False

  Label28.Visible = True

  End If

 

  If range = 10 Then

  Label25.Visible = False

  Label26.Visible = False

  Label27.Visible = True

  Label28.Visible = False

  End If

 

  If range = 1 Then

  Label25.Visible = False

  Label26.Visible = True

  Label27.Visible = False

  Label28.Visible = False

  End If

 

  If range = 0 Then

  Label25.Visible = True

  Label26.Visible = False

  Label27.Visible = False

  Label28.Visible = False

  End If

End If

End Sub

附錄B時間延遲Delayms()函數

Form表單主程式

Public Sub Delayms(ByVal ms As Long)

Dim t As Long

t = GetTickCount()

While (GetTickCount() - t) < ms

DoEvents

Wend

End Sub

模組程式

 

Public Declare Function GetTickCount Lib "kernel32" () As Long

 

 



[1] VB中並無內建時間延遲的函數,因此需要自行撰寫函數程式,本範例中使用Delayms()為時間延遲函數,撰寫範例列於附錄A