深化浅出Win32多线程程序计划综合实例[VC/C++编程]
本文“深化浅出Win32多线程程序计划综合实例[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
本章我们将以产业掌握和嵌入式系统中应用极其遍及的串口通信为例报告多线程的典型利用.
而网络通信也是多线程利用最遍及的范畴之一,所以本章的最后一节也将对多线程网络通信举行简短的描写.
1.串口通信
在产业掌握系统中,工控机(普通都基于PC Windows平台)常常需求与单片机通过串口举行通信.因此,操作和利用PC的串口成为大大都单片机、嵌入式系统范畴工程师必须具有的本领.
串口的利用需求通过三个步骤来完成的:
(1) 翻开通信端口;
(2) 初始化串口,设置波特率、数据位、终止位、奇偶校验等参数.为了给读者一个直观的印象,下图从Windows的"掌握面板->系统->设备管理器->通信端口(COM1)"翻开COM的设置窗口:
(3) 读写串口.
在WIN32平台下,对通信端口举行操作跟基本的文件操作一样.
成立/翻开COM资源
下列函数假如调用成功,则返回一个标识通信端口的句柄,不然返回-1:
HADLE CreateFile(PCTSTR lpFileName, //通信端口名,如"COM1"
WORD dwDesiredAccess, //对资源的拜候范例
WORD dwShareMode, //指定同享情势,COM不能同享,该参数为0
PSECURITY_ATTRIBUTES lpSecurityAttributes,
//安全描写符指针,可为NULL
WORD dwCreationDisposition, //成立方法
WORD dwFlagsAndAttributes, //文件属性,可为NULL
HANDLE hTemplateFile //模板文件句柄,置为NULL
);
得到/设置COM属性
下列函数可以得到COM口的设备掌握块,从而得到相关参数:
BOOL WINAPI GetCommState(
HANDLE hFile, //标识通信端口的句柄
LPDCB lpDCB //指向一个设备掌握块(DCB构造)的指针
);
假如要调整通信端口的参数,则需求重新配置设备掌握块,再用WIN32 API SetCommState()函数举行设置:
BOOL SetCommState(
HANDLE hFile, //标识通信端口的句柄
LPDCB lpDCB //指向一个设备掌握块(DCB构造)的指针
);
DCB构造包含了串口的各项参数设置,以下:
typedef struct _DCB
{
// dcb
DWORD DCBlength; // sizeof(DCB)
DWORD BaudRate; // current baud rate
DWORD fBinary: 1; // binary mode, no EOF check
DWORD fParity: 1; // enable parity checking
DWORD fOutxCtsFlow: 1; // CTS output flow control
DWORD fOutxDsrFlow: 1; // DSR output flow control
DWORD fDtrControl: 2; // DTR flow control type
DWORD fDsrSensitivity: 1; // DSR sensitivity
DWORD fTXContinueOnXoff: 1; // XOFF continues Tx
DWORD fOutX: 1; // XON/XOFF out flow control
DWORD fInX: 1; // XON/XOFF in flow control
DWORD fErrorChar: 1; // enable error replacement
DWORD fNull: 1; // enable null stripping
DWORD fRtsControl: 2; // RTS flow control
DWORD fAbortOnError: 1; // abort reads/writes on error
DWORD fDummy2: 17; // reserved
WORD wReserved; // not currently used
WORD XonLim; // transmit XON threshold
WORD XoffLim; // transmit XOFF threshold
BYTE ByteSize; // number of bits/byte, 4-8
BYTE Parity; // 0-4=no,odd,even,mark,space
BYTE StopBits; // 0,1,2 = 1, 1.5, 2
char XonChar; // Tx and Rx XON character
char XoffChar; // Tx and Rx XOFF character
char ErrorChar; // error replacement character
char EofChar; // end of input character
char EvtChar; // received event character
WORD wReserved1; // reserved; do not use
} DCB;
读写串口
在读写串口之前,还要用PurgeComm()函数清空缓冲区,并用SetCommMask ()函数设置事件掩模来监督指定通信端口上的事件,其原型为:
BOOL SetCommMask(
HANDLE hFile, //标识通信端口的句柄
DWORD dwEvtMask //可以使能的通信事件
);
串口上大概发生的事件以下表所示:
值 | 事件描写 |
EV_BREAK | A break was detected on input. |
EV_CTS | The CTS (clear-to-send) signal changed state. |
EV_DSR | The DSR(data-set-ready) signal changed state. |
EV_ERR | A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. |
EV_RING | A ring indicator was detected. |
EV_RLSD | The RLSD (receive-line-signal-detect) signal changed state. |
EV_RXCHAR | A character was received and placed in the input buffer. |
EV_RXFLAG | The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function. |
EV_TXEMPTY | The last character in the output buffer was sent. |
在设置功德件掩模后,我们便可以操纵WaitCommEvent()函数来等候串口上发闹事件,其函数原型为:
BOOL WaitCommEvent(
HANDLE hFile, //标识通信端口的句柄
LPDWORD lpEvtMask, //指向存放事件标识变量的指针
LPOVERLAPPED lpOverlapped, // 指向overlapped构造
);
我们可以在发闹事件后,按照呼应的事件范例,举行串口的读写操作:
BOOL ReadFile(HANDLE hFile, //标识通信端口的句柄
LPVOID lpBuffer, //输入数据Buffer指针
DWORD nNumberOfBytesToRead, // 需求读取的字节数
LPDWORD lpNumberOfBytesRead, //实际读取的字节数指针
LPOVERLAPPED lpOverlapped //指向overlapped构造
);
BOOL WriteFile(HANDLE hFile, //标识通信端口的句柄
LPCVOID lpBuffer, //输出数据Buffer指针
DWORD nNumberOfBytesToWrite, //需求写的字节数
LPDWORD lpNumberOfBytesWritten, //实际写入的字节数指针
LPOVERLAPPED lpOverlapped //指向overlapped构造
);
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;
以上是“深化浅出Win32多线程程序计划综合实例[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |