2.工程实例
下面我们用第1节所述API实现一个多线程的串口通信程序。这个例子工程(工程名为MultiThreadCom)的界面很简单,如下图所示:
它是一个多线程的应用程序,包括两个工作者线程,分别处理串口1和串口2。为了简化问题,我们让连接两个串口的电缆只包含RX、TX两根连线(即不以硬件控制RS-232,串口上只会发生EV_TXEMPTY、EV_RXCHAR事件)。
在工程实例的BOOL
CMultiThreadComApp::InitInstance()函数中,启动并设置COM1和COM2,其源代码为:
BOOL
CMultiThreadComApp::InitInstance() { AfxEnableControlContainer(); //打开并设置COM1 hComm1=CreateFile("COM1",
GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL); if
(hComm1==(HANDLE)-1) { AfxMessageBox("打开COM1失败"); return
false; } else { DCB wdcb; GetCommState
(hComm1,&wdcb); wdcb.BaudRate=9600; SetCommState
(hComm1,&wdcb); PurgeComm(hComm1,PURGE_TXCLEAR); } //打开并设置COM2 hComm2=CreateFile("COM2",
GENERIC_READ|GENERIC_WRITE, 0, NULL ,OPEN_EXISTING, 0,NULL); if
(hComm2==(HANDLE)-1) { AfxMessageBox("打开COM2失败"); return
false; } else { DCB wdcb; GetCommState
(hComm2,&wdcb); wdcb.BaudRate=9600; SetCommState
(hComm2,&wdcb); PurgeComm(hComm2,PURGE_TXCLEAR); }
CMultiThreadComDlg
dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if
(nResponse == IDOK) { // TODO: Place code here to handle when the
dialog is // dismissed with OK } else if (nResponse ==
IDCANCEL) { // TODO: Place code here to handle when the dialog
is // dismissed with Cancel } return
FALSE; } | 此后我们在对话框CMultiThreadComDlg的初始化函数OnInitDialog中启动两个分别处理COM1和COM2的线程:
BOOL
CMultiThreadComDlg::OnInitDialog() { CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX
must be in the system command range. ASSERT((IDM_ABOUTBOX &
0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX <
0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu
!= NULL) { CString
strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if
(!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING,
IDM_ABOUTBOX, strAboutMenu); } }
// Set the icon for this
dialog. The framework does this automatically // when the
application's main window is not a dialog SetIcon(m_hIcon, TRUE); //
Set big icon SetIcon(m_hIcon, FALSE); // Set small icon
//
TODO: Add extra initialization here //启动串口1处理线程 DWORD
nThreadId1; hCommThread1 = ::CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0, (LPTHREAD_START_ROUTINE)Com1ThreadProcess,
AfxGetMainWnd()->m_hWnd, 0, &nThreadId1); if (hCommThread1 ==
NULL) { AfxMessageBox("创建串口1处理线程失败"); return
false; } //启动串口2处理线程 DWORD nThreadId2; hCommThread2 =
::CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0, (LPTHREAD_START_ROUTINE)Com2ThreadProcess,
AfxGetMainWnd()->m_hWnd, 0, &nThreadId2); if (hCommThread2 ==
NULL) { AfxMessageBox("创建串口2处理线程失败"); return
false; }
return TRUE; // return TRUE unless you set the focus
to a
control } | 两个串口COM1和COM2对应的线程处理函数等待串口上发生事件,并根据事件类型和自身缓冲区是否有数据要发送进行相应的处理,其源代码为:
DWORD WINAPI Com1ThreadProcess(HWND hWnd//主窗口句柄) { DWORD
wEven; char str[10]; //读入数据 SetCommMask(hComm1, EV_RXCHAR |
EV_TXEMPTY); while (TRUE) { WaitCommEvent(hComm1, &wEven,
NULL); if(wEven =
0) { CloseHandle(hCommThread1); hCommThread1 =
NULL; ExitThread(0); } else { switch
(wEven) { case EV_TXEMPTY: if (wTxPos <
wTxLen) { //在串口1写入数据 DWORD wCount;
//写入的字节数 WriteFile(hComm1, com1Data.TxBuf[wTxPos], 1,
&wCount,
NULL); com1Data.wTxPos++; } break; case
EV_RXCHAR: if (com1Data.wRxPos <
com1Data.wRxLen) { //读取串口数据, 处理收到的数据 DWORD
wCount; //读取的字节数 ReadFile(hComm1, com1Data.RxBuf[wRxPos], 1,
&wCount,
NULL); com1Data.wRxPos++; if(com1Data.wRxPos==
com1Data.wRxLen); ::PostMessage(hWnd, COM_SENDCHAR, 0,
1); } break; } } } } return
TRUE; }
DWORD WINAPI Com2ThreadProcess(HWND hWnd
//主窗口句柄) { DWORD wEven; char str[10];
//读入数据 SetCommMask(hComm2, EV_RXCHAR | EV_TXEMPTY); while
(TRUE) { WaitCommEvent(hComm2, &wEven, NULL); if (wEven
= 0) { CloseHandle(hCommThread2); hCommThread2 =
NULL; ExitThread(0); } else { switch
(wEven) { case EV_TXEMPTY: if (wTxPos <
wTxLen) { //在串口2写入数据 DWORD wCount;
//写入的字节数 WriteFile(hComm2, com2Data.TxBuf[wTxPos], 1,
&wCount,
NULL); com2Data.wTxPos++; } break; case
EV_RXCHAR: if (com2Data.wRxPos <
com2Data.wRxLen) { //读取串口数据, 处理收到的数据 DWORD
wCount; //读取的字节数 ReadFile(hComm2, com2Data.RxBuf[wRxPos], 1,
&wCount,
NULL); com2Data.wRxPos++; if(com2Data.wRxPos==
com2Data.wRxLen); ::PostMessage(hWnd, COM_SENDCHAR, 0,
1); } break; } } } return
TRUE; } | 线程控制函数中所操作的com1Data和com2Data是与串口对应的数据结构struct
tagSerialPort的实例,这个数据结构是:
typedef struct tagSerialPort { BYTE
RxBuf[SPRX_BUFLEN];//接收Buffer WORD wRxPos; //当前接收字节位置 WORD wRxLen;
//要接收的字节数 BYTE TxBuf[SPTX_BUFLEN];//发送Buffer WORD wTxPos;
//当前发送字节位置 WORD wTxLen; //要发送的字节数 }SerialPort, *
LPSerialPort; |
|