串口API通信函數(shù)編程
10年積累的成都做網(wǎng)站、成都網(wǎng)站制作經(jīng)驗,可以快速應對客戶對網(wǎng)站的新想法和需求。提供各種問題對應的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡服務。我雖然不認識你,你也不認識我。但先建設網(wǎng)站后付款的網(wǎng)站建設流程,更有郴州免費網(wǎng)站建設讓你可以放心的選擇與我們合作。
16位串口應用程序中,使用的16位的Windows API通信函數(shù):
①OpenComm()打開串口資源,并指定輸入、輸出緩沖區(qū)的大?。ㄒ宰止?jié)計)
CloseComm() 關閉串口;
例:int idComDev;
idComDev = OpenComm("COM1", 1024, 128);
CloseComm(idComDev);
②BuildCommDCB() 、setCommState()填寫設備控制塊DCB,然后對已打開的串口進行參數(shù)配置; 例:DCB dcb;
BuildCommDCB("COM1:2400,n,8,1", dcb);
SetCommState(dcb);
③ ReadComm 、WriteComm()對串口進行讀寫操作,即數(shù)據(jù)的接收和發(fā)送.
例:char *m_pRecieve; int count;
ReadComm(idComDev,m_pRecieve,count);
Char wr[30]; int count2;
WriteComm(idComDev,wr,count2);
16位下的串口通信程序最大的特點就在于:串口等外部設備的操作有自己特有的API函數(shù);而32位程序則把串口操作(以及并口等)和文件操作統(tǒng)一起來了,使用類似的操作。
在MFC下的32位串口應用程序
32位下串口通信程序可以用兩種方法實現(xiàn):利用ActiveX控件;使用API 通信函數(shù)。
使用ActiveX控件,程序實現(xiàn)非常簡單,結構清晰,缺點是欠靈活;使用API 通信函數(shù)的優(yōu)缺點則基本上相反。
使用ActiveX控件:
VC++ 6.0提供的MSComm控件通過串行端口發(fā)送和接收數(shù)據(jù),為應用程序提供串行通信功能。使用非常方便,但可惜的是,很少有介紹MSComm控件的資料。
⑴.在當前的Workspace中插入MSComm控件。
Project菜單------Add to Project----Components and Controls-----Registered
ActiveX Controls---選擇Components: Microsoft Communications Control,
version 6.0 插入到當前的Workspace中。
結果添加了類CMSComm(及相應文件:mscomm.h和mscomm.cpp )。
⑵.在MainFrm.h中加入MSComm控件。
protected:
CMSComm m_ComPort;
在Mainfrm.cpp::OnCreare()中:
DWORD style=WS_VISIBLE|WS_CHILD;
if (!m_ComPort.Create(NULL,style,CRect(0,0,0,0),this,ID_COMMCTRL)){
TRACE0("Failed to create OLE Communications Control\n");
return -1; // fail to create
}
⑶.初始化串口
m_ComPort.SetCommPort(1); //選擇COM?
m_ComPort. SetInBufferSize(1024); //設置輸入緩沖區(qū)的大小,Bytes
m_ComPort. SetOutBufferSize(512); //設置輸入緩沖區(qū)的大小,Bytes//
if(!m_ComPort.GetPortOpen()) //打開串口
m_ComPort.SetPortOpen(TRUE);
m_ComPort.SetInputMode(1); //設置輸入方式為二進制方式
m_ComPort.SetSettings("9600,n,8,1"); //設置波特率等參數(shù)
m_ComPort.SetRThreshold(1); //為1表示有一個字符引發(fā)一個事件
m_ComPort.SetInputLen(0);
⑷.捕捉串口事項。MSComm控件可以采用輪詢或事件驅動的方法從端口獲取數(shù)據(jù)。我們介紹比較使用的事件驅動方法:有事件(如接收到數(shù)據(jù))時通知程序。在程序中需要捕獲并處理這些通訊事件。
在MainFrm.h中:
protected:
afx_msg void OnCommMscomm();
DECLARE_EVENTSINK_MAP()
在MainFrm.cpp中:
BEGIN_EVENTSINK_MAP(CMainFrame,CFrameWnd )
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE) //映射ActiveX控件事件
END_EVENTSINK_MAP()
⑸.串口讀寫. 完成讀寫的函數(shù)的確很簡單,GetInput()和SetOutput()就可。兩個函數(shù)的原型是:
VARIANT GetInput();及 void SetOutput(const VARIANT newValue);都要使用VARIANT類型(所有Idispatch::Invoke的參數(shù)和返回值在內部都是作為VARIANT對象處理的)。
無論是在PC機讀取上傳數(shù)據(jù)時還是在PC機發(fā)送下行命令時,我們都習慣于使用字符串的形式(也可以說是數(shù)組形式)。查閱VARIANT文檔知道,可以用BSTR表示字符串,但遺憾的是所有的BSTR都是包含寬字符,即使我們沒有定義_UNICODE_UNICODE也是這樣! WinNT支持寬字符, 而Win95并不支持。為解決上述問題,我們在實際工作中使用CbyteArray,給出相應的部分程序如下:
void CMainFrame::OnCommMscomm(){
VARIANT vResponse; int k;
if(m_commCtrl.GetCommEvent()==2) {
k=m_commCtrl.GetInBufferCount(); //接收到的字符數(shù)目
if(k0) {
vResponse=m_commCtrl.GetInput(); //read
SaveData(k,(unsigned char*) vResponse.parray-pvData);
} // 接收到字符,MSComm控件發(fā)送事件 }
。。。。。 // 處理其他MSComm控件
}
void CMainFrame::OnCommSend() {
。。。。。。。。 // 準備需要發(fā)送的命令,放在TxData[]中
CByteArray array;
array.RemoveAll();
array.SetSize(Count);
for(i=0;iCount;i++)
array.SetAt(i, TxData[i]);
m_ComPort.SetOutput(COleVariant(array)); // 發(fā)送數(shù)據(jù) }
二 使用32位的API 通信函數(shù):
⑴.在中MainFrm.cpp定義全局變量
HANDLE hCom; // 準備打開的串口的句柄
HANDLE hCommWatchThread ;//輔助線程的全局函數(shù)
⑵.打開串口,設置串口
hCom =CreateFile( "COM2", GENERIC_READ | GENERIC_WRITE, // 允許讀寫
0, // 此項必須為0
NULL, // no security attrs
OPEN_EXISTING, //設置產生方式
FILE_FLAG_OVERLAPPED, // 我們準備使用異步通信
NULL );
我使用了FILE_FLAG_OVERLAPPED結構。這正是使用API實現(xiàn)非阻塞通信的關鍵所在。
ASSERT(hCom!=INVALID_HANDLE_VALUE); //檢測打開串口操作是否成功
SetCommMask(hCom, EV_RXCHAR|EV_TXEMPTY );//設置事件驅動的類型
SetupComm( hCom, 1024,512) ; //設置輸入、輸出緩沖區(qū)的大小
PurgeComm( hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
| PURGE_RXCLEAR ); //清干凈輸入、輸出緩沖區(qū)
COMMTIMEOUTS CommTimeOuts ; //定義超時結構,并填寫該結構
…………
SetCommTimeouts( hCom, CommTimeOuts ) ;//設置讀寫操作所允許的超時
DCB dcb ; // 定義數(shù)據(jù)控制塊結構
GetCommState(hCom, dcb ) ; //讀串口原來的參數(shù)設置
dcb.BaudRate =9600; dcb.ByteSize =8; dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT ;dcb.fBinary = TRUE ;dcb.fParity = FALSE;
SetCommState(hCom, dcb ) ; //串口參數(shù)配置
上述的COMMTIMEOUTS結構和DCB都很重要,實際工作中需要仔細選擇參數(shù)。
⑶啟動一個輔助線程,用于串口事件的處理。
Windows提供了兩種線程,輔助線程和用戶界面線程。輔助線程沒有窗口,所以它沒有自己的消息循環(huán)。但是輔助線程很容易編程,通常也很有用。
在次,我們使用輔助線程。主要用它來監(jiān)視串口狀態(tài),看有無數(shù)據(jù)到達、通信有無錯誤;而主線程則可專心進行數(shù)據(jù)處理、提供友好的用戶界面等重要的工作。
hCommWatchThread=
CreateThread( (LPSECURITY_ATTRIBUTES) NULL, //安全屬性
0,//初始化線程棧的大小,缺省為與主線程大小相同
(LPTHREAD_START_ROUTINE)CommWatchProc, //線程的全局函數(shù)
GetSafeHwnd(), //此處傳入了主框架的句柄
0, dwThreadID );
ASSERT(hCommWatchThread!=NULL);
⑷為輔助線程寫一個全局函數(shù),主要完成數(shù)據(jù)接收的工作。請注意OVERLAPPED結構的使用,以及怎樣實現(xiàn)了非阻塞通信。
UINT CommWatchProc(HWND hSendWnd){
DWORD dwEvtMask=0 ;
SetCommMask( hCom, EV_RXCHAR|EV_TXEMPTY );//有哪些串口事件需要監(jiān)視?
WaitCommEvent( hCom, dwEvtMask, os );// 等待串口通信事件的發(fā)生
檢測返回的dwEvtMask,知道發(fā)生了什么串口事件:
if ((dwEvtMask EV_RXCHAR) == EV_RXCHAR){ // 緩沖區(qū)中有數(shù)據(jù)到達
COMSTAT ComStat ; DWORD dwLength;
ClearCommError(hCom, dwErrorFlags, ComStat ) ;
dwLength = ComStat.cbInQue ; //輸入緩沖區(qū)有多少數(shù)據(jù)?
if (dwLength 0) { BOOL fReadStat ;
fReadStat = ReadFile( hCom, lpBuffer,dwLength, dwBytesRead,READ_OS( npTTYInfo ) ); //讀數(shù)據(jù)
注:我們在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現(xiàn)在ReadFile()也必須使用
LPOVERLAPPED結構.否則,函數(shù)會不正確地報告讀操作已完成了.
使用LPOVERLAPPED結構, ReadFile()立即返回,不必等待讀操作完成,實現(xiàn)非阻塞
通信.此時, ReadFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
if (!fReadStat){
if (GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom,READ_OS( npTTYInfo ), dwBytesRead, TRUE )){
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE) continue;//緩沖區(qū)數(shù)據(jù)沒有讀完,繼續(xù)
…… ……
::PostMessage((HWND)hSendWnd,WM_NOTIFYPROCESS,0,0);//通知主線程,串口收到數(shù)據(jù)}
所謂的非阻塞通信,也即異步通信。是指在進行需要花費大量時間的數(shù)據(jù)讀寫操作(不僅僅是指串行通信操作)時,一旦調用ReadFile()、WriteFile(), 就能立即返回,而讓實際的讀寫操作在后臺運行;相反,如使用阻塞通信,則必須在讀或寫操作全部完成后才能返回。由于操作可能需要任意長的時間才能完成,于是問題就出現(xiàn)了。
非常阻塞操作還允許讀、寫操作能同時進行(即重疊操作?),在實際工作中非常有用。
要使用非阻塞通信,首先在CreateFile()時必須使用FILE_FLAG_OVERLAPPED;然后在 ReadFile()時lpOverlapped參數(shù)一定不能為NULL,接著檢查函數(shù)調用的返回值,調用GetLastError(),看是否返回ERROR_IO_PENDING。如是,最后調用GetOverlappedResult()返回重疊操作(overlapped operation)的結果;WriteFile()的使用類似。
⑸.在主線程中發(fā)送下行命令。
BOOL fWriteStat ; char szBuffer[count];
…………//準備好發(fā)送的數(shù)據(jù),放在szBuffer[]中
fWriteStat = WriteFile(hCom, szBuffer, dwBytesToWrite,
dwBytesWritten, WRITE_OS( npTTYInfo ) ); //寫數(shù)據(jù)
//我在CreareFile()時使用了FILE_FLAG_OVERLAPPED,現(xiàn)在WriteFile()也必須使用LPOVERLAPPED結構.否則,函數(shù)會不正確地報告寫操作已完成了.
使用LPOVERLAPPED結構,WriteFile()立即返回,不必等待寫操作完成,實現(xiàn)非阻塞 通信.此時, WriteFile()返回FALSE, GetLastError()返回ERROR_IO_PENDING.
int err=GetLastError();
if (!fWriteStat) {
if(GetLastError() == ERROR_IO_PENDING){
while(!GetOverlappedResult(hCom, WRITE_OS( npTTYInfo ),
dwBytesWritten, TRUE )) {
dwError = GetLastError();
if(dwError == ERROR_IO_INCOMPLETE){// normal result if not finished
dwBytesSent += dwBytesWritten; continue; }
......................
//我使用了多線程技術,在輔助線程中監(jiān)視串口,有數(shù)據(jù)到達時依靠事件驅動,讀入數(shù)據(jù)并向主線程報告(發(fā)送數(shù)據(jù)在主線程中,相對說來,下行命令的數(shù)據(jù)總是少得多);并且,WaitCommEvent()、ReadFile()、WriteFile()都使用了非阻塞通信技術,依靠重疊(overlapped)讀寫操作,讓串口讀寫操作在后臺運行。
用 vb.net socket通信
Dim th As Threading.Thread
2 Dim tcpl As System.Net.Sockets.TcpListener
3
4 Private Sub Form1_Load()Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
5 th = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf MyListen))
6 th.Start()
7 End Sub
8
9 Public Sub SendMessage()Sub SendMessage(ByVal IP As String, ByVal SendMsg As String)
10 Try
11 If IP "" Then
12 Dim tcpc As New System.Net.Sockets.TcpClient(IP, 5656)
13 Dim tcpStream As Net.Sockets.NetworkStream = tcpc.GetStream
14 Dim reqStream As New IO.StreamWriter(tcpStream)
15 reqStream.Write(SendMsg)
16 reqStream.Flush()
17 tcpStream.Close()
18 tcpc.Close()
19 End If
20 Catch ex As Exception
21 MsgBox(ex.Message.ToString)
22 End Try
23 End Sub
24 Private Sub MyListen()Sub MyListen()
25 Try
26 Dim ipAddress As System.Net.IPAddress = System.Net.Dns.Resolve(System.Net.Dns.GetHostName).AddressList(0)
27 tcpl = New System.Net.Sockets.TcpListener(ipAddress, 5656)
28 tcpl.Start()
29 While True
30 Dim s As System.Net.Sockets.Socket = tcpl.AcceptSocket()
31 Dim MyBuffer(1024) As Byte
32 Dim i As Integer
33 i = s.Receive(MyBuffer)
34 If i 0 Then
35 Dim lstrRec As String
36 Dim j As Integer
37 For j = 0 To i - 1
38 TextBox1.Text += Chr(MyBuffer(j)) ","
39 Next
40 End If
41 End While
42 Catch ex As Exception
43 MsgBox(ex.Message.ToString)
44 End Try
45 End Sub
46
47 Private Sub Button1_Click()Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
48 SendMessage("192.168.0.61", TextBox2.Text)
49 End Sub
可以用動態(tài)數(shù)據(jù)交換(Dynamic Data Exchange,DDE)實現(xiàn)進程之間的通信。
發(fā)送端:
添加Command1,Text1控件,Text1的內容用于發(fā)送。
代碼如下:
Private?Sub?Command1_Click()
Dim?t?As?Long
Text1.LinkMode?=?0
Text1.LinkTopic?=?"Child|FrmChild"?'注意此處一定與接收的程序名稱和連接的標題相同否則連接不成功。
Text1.LinkMode?=?2
Text1.LinkExecute?Text1.Text
t?=?Text1.LinkTimeout
Text1.LinkTimeout?=?1
Text1.LinkMode?=?0
Text1.LinkTimeout?=?t
End?Sub
接收端:
添加Text1控件,Text1的內容用于接收。設置工程名稱為Child(與發(fā)送的程序名對應),設置窗體的LinkMode屬性為1,LinkTopic為FrmChild(對應上面發(fā)送的標題)。
Private?Sub?Form_LinkExecute(CmdStr?As?String,?Cancel?As?Integer)
Text1?=?CmdStr
Cancel?=?0
End?Sub
編譯后,先運行接收端,然后運行發(fā)送端,輸入內容,發(fā)送即可成功。
用VB5 Winsock控件創(chuàng)建TCP/IP通訊程序 隨著Windows 95中文版和Windows NT Server 4.0中文版的流行, Microsoft公司推出了相應平臺上的開發(fā)軟件: Visual Basic 5.0 中文企業(yè) 版。它為Windows環(huán)境下的網(wǎng)絡開發(fā)提供了強大的工具,Winsock控件就是其中之一。 Winsock控件建立在TCP、UDP協(xié)議的基礎上,完成與遠程計算機的通信。即使對TCP/IP不太熟悉的用戶,使用該控件也可以在十幾分鐘內創(chuàng)建一個簡單的客戶機/服務器程序。下面我們對Winsock控件的事件、方法、屬性按其在程序中出現(xiàn)的順序分別作詳細的介紹,以便更好地理解程序源代碼。