屈武江
(大連海洋大學(xué) 應(yīng)用技術(shù)學(xué)院,遼寧 大連 116300)
數(shù)據(jù)通信也稱為數(shù)據(jù)獲取和數(shù)據(jù)交換,是將從系統(tǒng)外部采集的數(shù)據(jù)通過一種通信設(shè)備傳送到系統(tǒng)內(nèi)部的一個接口[1]。數(shù)據(jù)通信不僅包括計(jì)算機(jī)與計(jì)算機(jī)之間的數(shù)據(jù)傳輸,也包括計(jì)算機(jī)與其他外部設(shè)備之間的數(shù)據(jù)傳輸。目前數(shù)據(jù)通信技術(shù)在實(shí)時監(jiān)控系統(tǒng)和工業(yè)自動化系統(tǒng)中得到了廣泛應(yīng)用。
串口數(shù)據(jù)通信是指通過計(jì)算機(jī)上的串行接口使計(jì)算機(jī)與計(jì)算機(jī)之間或者計(jì)算機(jī)與外部設(shè)備之間進(jìn)行數(shù)據(jù)傳輸?shù)囊环N數(shù)據(jù)獲取方式[2]。串行接口通常是指計(jì)算機(jī)上的COM接口,是一種通用的設(shè)備通信接口[3]。一般來說串口主要用于連接鼠標(biāo)等外部設(shè)備,同時串口也是用于外部設(shè)備與計(jì)算機(jī)系統(tǒng)之間傳輸數(shù)據(jù)的通道。如果計(jì)算機(jī)上沒有串行接口,用戶可以使用USB轉(zhuǎn)接器將USB接口轉(zhuǎn)換為串行接口。串口數(shù)據(jù)通信由于使用設(shè)備簡單,同時通過實(shí)現(xiàn)超遠(yuǎn)距離數(shù)據(jù)傳送,因此在工業(yè)和自動化系統(tǒng)中經(jīng)常使用串行接口進(jìn)行數(shù)據(jù)通信[4]。
應(yīng)用串行接口實(shí)現(xiàn)數(shù)據(jù)通信的過程:首先通過命令設(shè)置串口名稱、波特率、數(shù)據(jù)位、停止位和奇偶位檢驗(yàn)等串口參數(shù),然后打開串口等待接口數(shù)據(jù)[5]。接著進(jìn)行數(shù)據(jù)通信,也就是讀取串口數(shù)據(jù)或者向串口傳遞數(shù)據(jù)[3-6]。最后數(shù)據(jù)通信完畢關(guān)閉串口,釋放資源。
VB6.0提供了MSComm串口通訊控件,MSComm控件隱藏了串口通信的運(yùn)行過程,用戶只需要編寫少量的程序代碼就可以實(shí)現(xiàn)數(shù)據(jù)通信[7]。MSComm串口通訊控件提供了事件驅(qū)動通信和查詢檢測通信兩種數(shù)據(jù)通信的方法,事件驅(qū)動通信指當(dāng)CD線或RTS線上有字符到達(dá)或發(fā)生改變時,OnComm事件捕獲或處理這些通訊事件和處理通訊中的錯誤。查詢檢測通信指可以在每個重要的程序功能之后檢查CommEvent屬性的值來檢測事件和通信錯誤。對于數(shù)據(jù)通訊量不大和對通信要求不高的數(shù)據(jù)通信環(huán)境下建議采取這種方式進(jìn)行數(shù)據(jù)通信[8]。但是應(yīng)用MSComm控件進(jìn)行數(shù)據(jù)通信時,每一個MSComm控件對應(yīng)一個串口,如果在程序中需要提供多個串口,則必須要添加多個MSComm控件。
VS2012是Microsoft Visual Studio 2012的簡稱,是微軟公司開發(fā)的目前最流行的Windows平臺應(yīng)用程序的集成開發(fā)環(huán)境,提供了軟件開發(fā)過程中所需要的各種工具[3,9]。其集成架構(gòu)Framework2.0以上類庫提供了SerialPort類可以取代MSComm控件實(shí)現(xiàn)串口通信。其執(zhí)行過程如下:
(1)創(chuàng)建數(shù)據(jù)通信類對象,設(shè)置通信串口的主要參數(shù):BaudRate(波特率)、PortName(串口名稱)、ReceivedBytesThreshold(觸發(fā)數(shù)據(jù)接收事件DataReceived)、WriteBufferSize/ReadBufferSize(寫緩沖區(qū)和讀緩沖區(qū)大小),并執(zhí)行數(shù)據(jù)通信類對象的Open方法打開新的串行口。
(2)執(zhí)行Read或者ReadLine方法從新打開的串行口中讀取數(shù)據(jù),通過執(zhí)行Write或者WriteLine方法將數(shù)據(jù)寫入新建的串行口,但要注意讀方法與寫方法必須要配對,即用Write方法寫數(shù)據(jù),則必須用Read方法讀取數(shù)據(jù)。
(3)通信完畢,調(diào)用Close方法關(guān)閉串口。
Windows操作系統(tǒng)提供了低層的API函數(shù)庫,API函數(shù)庫提供了多種函數(shù)可以實(shí)現(xiàn)串口數(shù)據(jù)通信。這種方法實(shí)現(xiàn)的數(shù)據(jù)通信效率高,主要適用于大型通信程序及通信質(zhì)量要求較高的場合[10]。
調(diào)用API函數(shù)來實(shí)現(xiàn)串口通信基本過程包括:
(1)執(zhí)行API函數(shù)庫的CreateFile()函數(shù)創(chuàng)建一個新的串口,通過執(zhí)行SetupComm()函數(shù)為新創(chuàng)建的串口分配輸入和輸出緩沖區(qū)大小,調(diào)用BuildCommDCB()或者SetCommState()函數(shù)設(shè)置串口通信的控制參數(shù)[11]。
(2)執(zhí)行API庫的WriteFile()庫函數(shù)將數(shù)據(jù)發(fā)送到新創(chuàng)建的串行口,同時將發(fā)送到串行口的數(shù)據(jù)寫入相應(yīng)的文件,也可以執(zhí)行ReadFile()庫函數(shù)從寫入的文件中讀取數(shù)據(jù)[12]。
(3)通信完畢,調(diào)用API庫函數(shù)CloseHandle()函數(shù)關(guān)閉串口。
文中以設(shè)計(jì)農(nóng)業(yè)生產(chǎn)中蔬菜大棚自動殺蟲燈為例,介紹基于API函數(shù)庫實(shí)現(xiàn)串口通信的設(shè)計(jì)與實(shí)現(xiàn)。
在農(nóng)業(yè)生產(chǎn)中農(nóng)戶經(jīng)常在蔬菜大棚安裝自動殺蟲燈并進(jìn)行實(shí)時監(jiān)控和統(tǒng)計(jì)顯示在計(jì)算機(jī)系統(tǒng)中。該系統(tǒng)的設(shè)計(jì)思路是當(dāng)昆蟲被殺死時,昆蟲落下觸發(fā)傳輸設(shè)備觸點(diǎn)并自動計(jì)數(shù),通過網(wǎng)絡(luò)4G在指定的時間間隔內(nèi)將計(jì)數(shù)數(shù)據(jù)傳輸?shù)浇邮斩藬?shù)據(jù)傳輸設(shè)備,數(shù)據(jù)傳輸設(shè)備將接收的數(shù)據(jù)通過串行接口傳輸數(shù)據(jù)通信軟件,而數(shù)據(jù)通信軟件通過串行接口接收數(shù)據(jù)并存儲到數(shù)據(jù)庫,同時軟件以圖表或其他方式顯示自動殺蟲燈的各月殺蟲情況。基于API函數(shù)庫串口數(shù)據(jù)通信軟件設(shè)計(jì)流程如圖1所示。
基于API函數(shù)庫的串口數(shù)據(jù)通信軟件采用VS2012集成開發(fā)環(huán)境,存儲數(shù)據(jù)庫采用Access 2013。在程序設(shè)計(jì)時首先設(shè)計(jì)串口數(shù)據(jù)通信通用類,旨在充分發(fā)揮面向?qū)ο蟪绦蛟O(shè)計(jì)的優(yōu)勢,提高程序的通用性和可移植性,以便實(shí)現(xiàn)串口通信接收文件的建立、打開串口和數(shù)據(jù)接收轉(zhuǎn)換。重要通用類方法的關(guān)鍵代碼如下:
(1)引入DLLImport屬性類。
[DllImport("kernel32.dll")]
Private atatic extern int CreaFile(
string sfnd //定義串口名稱
uint dwDesiredAccess //將串口的訪問方式設(shè)置為可讀可寫
int dwShareMode,lpSecurityAttributes,dwcd,dwFlagsAnd Attributes,hTemplateFile);
其中DllImport是System.Runtime.InteropServices名稱空間下的屬性類,用來提供從非托管DLL導(dǎo)出函數(shù)的必要調(diào)用信息。上述代碼導(dǎo)出函數(shù)CreateFile創(chuàng)建文件的調(diào)用信息,其中sfnd表示串口的名稱,dwDesiredAccess設(shè)置串口的訪問方式,DwShareMode指定串口的共享模式(0表示不共享),lpSecurityAttributes設(shè)置串口的安全屬性,dwcd設(shè)置串口的創(chuàng)建方式,dwFlagsAndAttributes設(shè)置串口屬性和標(biāo)志,hTemplateFile表示文件模板(串口通訊只能設(shè)置為0)。
圖1 基于API函數(shù)庫串口數(shù)據(jù)通信軟件設(shè)計(jì)流程
(2)打開串口方法。
打開串口方法名為open,實(shí)現(xiàn)過程是首先建立串口參數(shù)屬性及標(biāo)志設(shè)置結(jié)構(gòu)體變量dcbCommPort和超時結(jié)構(gòu)體變量ctoCommPort,然后調(diào)用外部方法CreateFile建立串口文件打開串口,如果返回的句柄無效,則提示錯誤信息,否則設(shè)置通信超時時間,調(diào)用外部方法GetCommState方法取得串口狀態(tài)參數(shù),最后調(diào)用SetCommState方法設(shè)置串口參數(shù),如果出現(xiàn)錯誤,則拋出異常,否則設(shè)置串口狀態(tài)為打開[13]。關(guān)鍵代碼如下:
…………
DCB dcbcp=new DCB();
COMMTIMEOUTS ctocp=new COMMTIMEOUTS();
hc=CreateFile(PortNum, GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
if(hc==INVALID_HANDLE_VALUE)
{throw (new ApplicationException("系統(tǒng)提示,無法打開串行端口!"));}
GetCommTimeouts(hComm, ref ctoCommPort);
SetCommTimeouts(hComm, ref ctoCommPort);
GetCommState(hComm, ref dcbCommPort);
…………
if(!SetCommState(hc, ref dcp)) //設(shè)置串口參數(shù)
{throw (new ApplicationException("系統(tǒng)提示,無法打開串行端口!")); }
Opened = true;
(3)數(shù)據(jù)接收方法。
數(shù)據(jù)接收方法主要實(shí)現(xiàn)從串行口中接收數(shù)據(jù)。實(shí)現(xiàn)過程是首先定義緩沖區(qū)字節(jié)變量數(shù)組,判斷句柄是否有效,如果返回句柄有效則調(diào)用ReadFile外部方法從串口的緩沖區(qū)中讀取數(shù)據(jù),然后使用數(shù)組復(fù)制的Copy方法將讀取的緩沖區(qū)數(shù)據(jù)復(fù)制到OutBytes數(shù)組并返回,如果返回句柄無效則拋出異常錯誤。
(4)關(guān)閉串口方法。
處理代碼判斷hComm是否為無效的句柄,如果是有效的句柄,執(zhí)行CloseHandle方法關(guān)閉串行口。
if (hc!=INVALID_HANDLE_VALUE) {CloseHandle(hComm);}
3.3 基于API函數(shù)庫串口數(shù)據(jù)通信分析軟件功能的實(shí)現(xiàn)
串口數(shù)據(jù)通信分析軟件的功能主要有串口參數(shù)設(shè)置、打開串口、接收數(shù)據(jù)并存儲文件,具體實(shí)現(xiàn)如下:
3.3.1 打開和關(guān)閉串口功能的實(shí)現(xiàn)
串口數(shù)據(jù)通信在接收數(shù)據(jù)前首先要打開串口,在程序設(shè)計(jì)的主窗體上添加“打開/關(guān)閉”按鈕,用于打開或關(guān)閉串口?!按蜷_/關(guān)閉”按鈕的代碼事件主要包括按鈕的Click單擊事件和打開串口的方法OpenComPort,在窗體級聲明串口通信通用類對象_myserialPort,進(jìn)行串口的打開、關(guān)閉、接收數(shù)據(jù)?!按蜷_/關(guān)閉”串口的Click單擊事件的處理代碼根據(jù)按鈕標(biāo)題判斷是打開還是關(guān)閉串口,根據(jù)標(biāo)題的不同分別調(diào)用不同的代碼進(jìn)行串口打開或關(guān)閉。關(guān)鍵代碼如下:
private static Com_IO serialPort1=new Com_IO();
private void btcom1_Click(object sender, EventArgs e)
{if (btcom1.Text== "關(guān)閉")
{serialPort1.Close();
btcom1.Text="打開";
return;}
serialPort1.PortNum=com_port.Text;
serialPort1.BaudRate=int.Parse(BaudRate.Text);
serialPort1.ByteSize=Convert.ToByte(ByteSize.Text);
…………
}
3.3.2 接收數(shù)據(jù)并存儲功能的實(shí)現(xiàn)
程序執(zhí)行時單擊主窗體上的“接收數(shù)據(jù)”按鈕,系統(tǒng)自動將Tick1定時器控件設(shè)置為工作狀態(tài),并根據(jù)控制設(shè)置的時間間隔自動調(diào)用Tick事件,Tick事件判斷串口的狀態(tài),如果串口處于打開狀態(tài),則調(diào)用通用類的讀取數(shù)據(jù)方法接收數(shù)據(jù),如果串口處于關(guān)閉狀態(tài),則給出提示信息。如果接收數(shù)據(jù)成功,則進(jìn)行數(shù)據(jù)轉(zhuǎn)換并存儲到數(shù)據(jù)庫中。關(guān)鍵代碼如下:
…………
byte[] text1=serialPort1.Read(40);//調(diào)用Read方法從串口讀取數(shù)據(jù)
tbcon1.Text=BitConverter.ToString(context);//數(shù)據(jù)類型轉(zhuǎn)換
string string1=tbcon1.Text;
string[] Arr1=string1.Split('-');
string string1="";
foreach (string i in Arr1)//將接收的數(shù)據(jù)轉(zhuǎn)換為字符串
string1=string1+i.ToString();
if (string1=="")
{MessageBox.Show("接收數(shù)據(jù)為空!", "分析系統(tǒng)提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
int lenage=string1.Length;
inta1=0;
string string2="";
for (a1=0;a1 {string2=string2+string1.Substring(a1,4)+",";} string2=string2.Substring(0, 19); string[] Arr2=Regex.Split(string2, ",", RegexOptions.IgnoreCase); string[] da=new string[4]; int l=0; foreach (string str_array in sArray1) { int b=Convert.ToInt32(Convert.ToInt32(str_array, 16)); da[l]=b.ToString(); l=l+1; } …………//向數(shù)據(jù)庫中添加獲取的數(shù)據(jù),代碼略 應(yīng)用VS2012集成開發(fā)工具通過調(diào)用API函數(shù)庫中的串口數(shù)據(jù)通信函數(shù)設(shè)計(jì)了農(nóng)業(yè)生產(chǎn)蔬菜大棚殺蟲燈數(shù)據(jù)采集應(yīng)用系統(tǒng)。通過系統(tǒng)的設(shè)計(jì)得出,調(diào)用API庫通信函數(shù)實(shí)現(xiàn)串口數(shù)據(jù)通信,由于涉及的硬件較少,傳輸速度較快,適用于要求通信質(zhì)量和遠(yuǎn)距離的數(shù)據(jù)通信需求,具有較強(qiáng)的實(shí)用性。4 結(jié)束語