在VC++中有两种方法可以进行串口通讯。一种是利用Microsoft公司提供的ActiveX控件 Microsoft Communications Control。另一种是直接用VC++访问串口。下面将简述这两种方法。
一、Microsoft Communications Control
Microsoft公司在WINDOWS中提供了一个串口通讯控件,用它,我们可以很简单的利用串口进行通讯。在使用它之前,应将控件加在应用程序的对话框上。然后再用ClassWizard 生成相应的对象。现在我们可以使用它了。
该控件有很多自己的属性,你可以通过它的属性窗口来设置,也可以用程序设置。我推荐用程序设置,这样更灵活。
SetCommPort:指定使用的串口。
GetCommPort:得到当前使用的串口。
SetSettings:指定串口的参数。一般设为默认参数"9600,N,8,1"。这样方便与其他串口进行通讯。
GetSettings:取得串口参数。
SetPortOpen:打开或关闭串口,当一个程序打开串口时,另外的程序将无法使用该串口。
GetPortOpen:取得串口状态。
GetInBufferCount:输入缓冲区中接受到的字符数。
SetInPutLen:一次读取输入缓冲区的字符数。设置为0时,程序将读取缓冲区的全部字符。
GetInPut:读取输入缓冲区。
GetOutBufferCount:输出缓冲区中待发送的字符数。
SetOutPut:写入输出缓冲区。
一般而言,使用上述函数和属性就可以进行串口通讯了。以下是一个范例。
#define MESSAGELENGTH 100
class CMyDialog : public CDialog
{
protected:
VARIANT InBuffer;
VARIANT OutBuffer;
CMSComm m_Com;
public:
......
}
BOOL CMyDiaLog::OnInitDialog()
{
CDialog::OnInitDialog();
m_Com.SetCommPort(1);
if (!m_Com.GetPortOpen()) {
m_Com.SetSettings("57600,N,8,1");
m_Com.SetPortOpen(true);
m_Com.SetInBufferCount(0);
SetTimer(1,10,NULL);
InBuffer.bstrVal=new unsigned short[MESSAGELENGTH]; OutBuffer.bstrVal=new unsigned short[MESSAGELENGTH]; OutBuffer.vt=VT_BSTR;
}
return true;
}
void CMyDiaLog::OnTimer(UINT nIDEvent)
{
if (m_Com.GetInBufferCount()>=MESSAGELENGTH) { InBuffer=m_Com.GetInput();
// handle the InBuffer.
// Fill the OutBuffer.
m_Com.SetOutput(OutBuffer);
}
CDialog::OnTimer(nIDEvent);
}
用该控件传输的数据是UNICODE格式。关于UNICODE和ANSI的关系和转换请参看MSDN。
关于该控件的其他详细资料请查看MSDN关于COMM CONTROL部分。
二、直接用VC++访问串口。
在VC++中,串口和磁盘文件可以统一的方式来简单读写。这两者几乎没有什么不同,只是在WINDOWS 9X下磁盘文件只能做同步访问,而串口只能做异步访问。
CreateFile:用指定的方式打开指定的串口。通常的方式为
m_hCom = CreateFile( "COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
m_hCom为文件句柄。GENERIC_READ | GENERIC_WRITE指定可以对串口进行读写操作。第三个参数0表示串口为独占打开。OPEN_EXISTING表示当指定串口不存在时,程序将返回失败。 FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_OVERLAPPED则表示文件属性。当打开串口时,必须指定
FILE_FLAG_OVERLAPPED,它表示文件或设备不会维护访问指针,则在读写时,必须使用OVERLAPPED 结构指定访问的文件偏移量。
ReadFile:读取串口数据。
WriteFile:向串口写数据。
CloseHandle:关闭串口。
COMMTIMEOUTS:COMMTIMEOUTS主要用于串口超时参数设置。
COMMTIMEOUTS结构如下: typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
ReadIntervalTimeout:两字符之间最大的延时,当读取串口数据时,一旦两个字符传输的时间差超过该时间,读取函数将返回现有的数据。设置为0表示该参数不起作用。
ReadTotalTimeoutMultiplier:读取每字符间的超时。
ReadTotalTimeoutConstant:一次读取串口数据的固定超时。所以在一次读取串口的操作中,其超时为ReadTotalTimeoutMultiplier乘以读取的字节数再加上
ReadTotalTimeoutConstant。将ReadIntervalTimeout设置为MAXDWORD,并将ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant设置为0,表示读取操作将立即返回存放在输入缓冲区的字符。
WriteTotalTimeoutMultiplier:写入每字符间的超时。
WriteTotalTimeoutConstant:一次写入串口数据的固定超时。所以在一次写入串口的操作中,其超时为WriteTotalTimeoutMultiplier乘以写入的字节数再加上 WriteTotalTimeoutConstant。
SetCommTimeouts函数可以设置某设备句柄的超时参数,要得到某设备句柄的超时参数可以用GetCommTimeouts函数。
DCB:DCB结构主要用于串口参数设置。该结构太庞大,这里就不一一讲述了,有兴趣者可查看MSDN关于DCB的描述。其中下面两个是比较重要的属性。
BaudRate:串口的通讯速度。一般设置为9600。
ByteSize:字节位数。一般设置为8。
DCB结构可以用SetCommState函数来设置,并可以用GetCommState来得到现有串口的属性。
SetupComm:设置串口输入、输出缓冲区。
OVERLAPPED:保存串口异步通讯的信息。具体结构如下: typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
Internal,InternalHigh是保留给系统使用的,用户不需要设置。
Offset,OffsetHigh是读写串口的偏移量,一般设置OffsetHigh为NULL,可以支持2GB数据。
hEvent读写事件,因为串口是异步通讯,操作可能被其他进程堵塞,程序可以通过检查该时间来得知是否读写完毕。事件将在读写完成后,自动设置为有效。
通过以上这些函数和结构,我们就可以通过串口进行通讯了,现在我们具体看下面的实例:
BOOL CSerial::Open( int nPort, int nBaud )
{
if( m_bOpened ) return( TRUE );
char szPort[15];
DCB dcb;
wsprintf( szPort, "COM%d", nPort );
m_hComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_OVERLAPPED, NULL );
if( m_hComDev == NULL ) return( FALSE );
memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) );
memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) );
COMMTIMEOUTS CommTimeOuts;
CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 5000;
SetCommTimeouts( m_hComDev, &CommTimeOuts );
m_OverlappedRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
dcb.DCBlength = sizeof( DCB );
GetCommState( m_hComDev, &dcb );
dcb.BaudRate = nBaud;
dcb.ByteSize = 8;
if( !SetCommState( m_hComDev, &dcb ) ||
!SetupComm( m_hComDev, 10000, 10000 ) ||
m_OverlappedRead.hEvent == NULL ||
m_OverlappedWrite.hEvent == NULL ){
DWORD dwError = GetLastError();
if( m_OverlappedRead.hEvent != NULL )
CloseHandle( m_OverlappedRead.hEvent );
if( m_OverlappedWrite.hEvent != NULL )
CloseHandle( m_OverlappedWrite.hEvent );
CloseHandle( m_hComDev );
return FALSE;
}
m_bOpened = TRUE;
return m_bOpened;
}
int CSerial::InBufferCount( void )
{
if( !m_bOpened || m_hComDev == NULL ) return( 0 );
DWORD dwErrorFlags;
COMSTAT ComStat;
ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat );
return (int)ComStat.cbInQue;
}
DWORD CSerial::ReadData( void *buffer, DWORD dwBytesRead)
{
if( !m_bOpened || m_hComDev == NULL ) return 0;
BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;
ClearCommError( m_hComDev, &dwErrorFlags, &ComStat );
if( !ComStat.cbInQue ) return 0;
dwBytesRead = min(dwBytesRead,(DWORD) ComStat.cbInQue);
bReadStatus = ReadFile( m_hComDev, buffer, dwBytesRead, &dwBytesRead, &m_OverlappedRead );
if( !bReadStatus ){
if( GetLastError() == ERROR_IO_PENDING ){
WaitForSingleObject( m_OverlappedRead.hEvent, 2000 );
return dwBytesRead;
}
return 0;
}
return dwBytesRead;
}
DWORD CSerial::SendData( const char *buffer, DWORD dwBytesWritten) {
if( !m_bOpened || m_hComDev == NULL ) return( 0 );
BOOL bWriteStat;
bWriteStat = WriteFile( m_hComDev, buffer, dwBytesWritten, &dwBytesWritten, &m_OverlappedWrite );
if( !bWriteStat){
if ( GetLastError() == ERROR_IO_PENDING ) {
WaitForSingleObject( m_OverlappedWrite.hEvent, 1000 );
return dwBytesWritten;
}
return 0;
}
return dwBytesWritten;
}
上述函数基本实现串口的打开,读写操作。本文章略去该串口类的说明和关闭函数。读者应该能将这些内容写完。接下来,你就可以在你的程序中调用该串口类了。关于本文有任何疑问,请与作者联系。
第二篇:VC++串口通讯设计与分析
VC++串口通讯设计与分析
许朋举
2009-9-4
最近在做一个关于串口通讯控制的软件,在不断学习和总结的过程中,对于计算机串口通讯的设计有了一定的了解,现在把的学习心得与大家分享,水平有限,不足之处敬请谅解。为了便于读者理解,我们这里只介绍了最基本的串口通讯处理程序的设计和编写,如需加深了解,请查阅msdn有关内容。
1. 单线程串口处理程序。
基本步骤:
a) 打开串口:串口也是被windows当做文件设备来处理的,可以使用API函数
CreateFile打开指定串口,进行串口数据收发。
示例:
HANDLE hComm=CreateFile((LPCTSTR)strPort, //串口号字符串
GENERIC_READ|GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
//指定打开已存在设备 //必须设置文件为异步读写 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED //设置读写模式
,NULL);
b) 设置串口属性:涉及到两个数据结构:DCB,COMMTIMEOUTS,和几个API函数,
GetCommState(HANDLE hComm,LPDCB lpDCB)获得串口属性数据,
SetCommState(HANDLE hComm,LPDCB lpDCB),设置串口属性数据
SetupComm(HANDLE hComm,DWORD dwInQueue,DWORD dwOutQueeu)设置串口输入输出缓冲区大小。
SetCommMask(HANDLE hfile,DWORD dwEvtMask)设置串口屏蔽事件字。
SetCommTimeouts(HANDLE hfile,LPCOMMTIMEOUTS lpCommTimeouts)设置串口通讯超时。
PurgeComm(HANDLE hfile,DWORD dwFlags)清除串口缓冲区的数据或所处异常中断。 i. 设置串口通讯属性:通过填写DCB 结构来对串口通讯模式进行控制。
示例:
DCB dcb;
//获取默认串口DCB 结构的属性 GetCommState(m_hComm,&dcb);
dcb.BaudRate=atoi((LPCTSTR)m_strBaud);//修改串口的波特率,一般为9600等
dcb.ByteSize= atoi((LPCTSTR)m_strDatas);//修改串口的数据帧位长度,一般为8位
dcb.StopBits=m_cboStop.GetCurSel(); //修改串口的数据帧停止位长度,一般为1位
dcb.fParity=TRUE;
dcb.fNull=true; //修改是否采用检验 //设置串口是否传输NULL
dcb.Parity=m_cboParity.GetCurSel(); //修改通讯检验方式,一般为,none,odd,even SetCommState(m_hComm,&dcb);
ii.
iii. 其他DCB结构属性字段可以忽略,如感兴趣请参阅msdn有关介绍。 设置串口通讯缓冲区大小,通过SetupComm函数来实现。 示例:SetupComm(m_hComm,1024,1024);//将输入输出缓冲区大小设置位1024字节。 设置串口屏蔽事件字:可以通过SetCommState函数实现,屏蔽字的作用是用来
iv. 对串口收发数据的事件进行控制,我们这里采用的是“直接发送,监视接收”的通讯方式;故我们在这里可以只监视数据的接收,屏蔽字为 EV_RXCHAR。 etCommMask(m_hComm,EV_RXCHAR);示例:S 设置串口通讯超时:可以通过COMMTIMEOUTS 结构来达到目的,这个结构比较
简单,共有5个字段,一般可以设置为零等待发送,无限制接收,示例: COMMTIMEOUTS commtimeouts;
commtimeouts.ReadIntervalTimeout=0xFFFFFFFF;//设置读取操作数据间隔时间为最大 commtimeouts.ReadTotalTimeoutConstant=0;
commtimeouts.ReadTotalTimeoutMultiplier=0;
commtimeouts.WriteTotalTimeoutConstant=0;
SetCommTimeouts(m_hComm,&commtimeouts);
串口的收发数据的总超时=超时常量+超时的字节倍数*数据字节数。 //读取操作的超时常量时间 //读取操作的超时字节倍数。 //发送操作的超时常量 commtimeouts.WriteTotalTimeoutMultiplier=0; //发送操作的超时字节倍数。
v. 清除缓冲区数据:可以使用PurgeComm函数来实现,目的是清除串口以前操作
的影响,示例:
PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT);
c) 发送串口数据:即可用与文件相关函数WriteFile来实现.不过因为打开的设备文件
要进行异步操作,所以必需使用一个OVERLAPPED 结构来辅助函数实现数据发送到串口。对于这个结构,我们可以只关心我们用到的字段,其中最主要的是hEvent,这个事件句柄,在执行操作以前首先要对它进行创建,用来监视发送操作是否完成。先看下示例代码:
OVERLAPPED wOverlapped;
CString strBuf=strData; DWORD dwBytes; memset(&wOverlapped,0,sizeof(wOverlapped)); wOverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!WriteFile(m_hComm,(LPVOID)strData,strBuf.GetLength(),&dwBytes,&wOverlapped)) { } if(GetLastError()==ERROR_IO_PENDING) { } else { } return false; WaitForSingleObject(wOverlapped.hEvent,1000); CloseHandle(wOverlapped.hEvent);
对OVERLAPPED 结构,再使用以前必需进行清空操作,以避免对我们操作的影响。我们这里创建一个人工重置的事件,其实对于我们的简单程序,只需创建一个事件即可,无需关心太多细节。写入操作如果完成WriteFile将返还TRUE,dwBytes返还发送的数据长度;否则返还假,这时我们可以检查一下错误,看是否的io数据未发送完毕,如果是利用WaitForsingleObject函数来等待一秒,否则发送失败。 最后要记得将创建的事件句柄关闭。
d) 接收串口数据:接收数据和发送数据的基本操作结构相似,但是多出来两个检查接
收数据的事件的函数。那我们先看下示例代码:
DWORD dwEvt,dwErr,dwByte=0;
)
{ } else { CloseHandle(rOverlapped.hEvent); strBuf.ReleaseBufferSetLength(0); return false; strBuf.SetAt((int)dwByte,0); m_edtRecv.SetWindowTextA((LPCTSTR)strBuf); CloseHandle(rOverlapped.hEvent); strBuf.ReleaseBufferSetLength(0); } } } { if(GetLastError()==ERROR_IO_PENDING) { } WaitForSingleObject(rOverlapped.hEvent,1000); OVERLAPPED rOverlapped; CString strBuf; strBuf.GetBufferSetLength(1024); memset(&rOverlapped,0,sizeof(rOverlapped)); rOverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); WaitCommEvent(m_hComm,&dwEvt,&rOverlapped); if(WaitForSingleObject(rOverlapped.hEvent,100)==WAIT_OBJECT_0) { if(dwEvt==EV_RXCHAR) { COMSTAT comstat; memset(&comstat,0,sizeof(comstat)); ClearCommError(m_hComm,&dwErr,&comstat); if(comstat.cbInQue) { if(!ReadFile(m_hComm,(LPVOID)(LPCTSTR)strBuf,comstat.cbInQue,&dwByte,&rOverlapped) }if(dwByte)
}
这里我们用到了WaitCommEvent函数,它的作用主要是查询我们设置的屏蔽事件字的事件的发生,如果有事件发生,函数返还非零值,dwEvt返还屏蔽事件字,如上述的EV_RXCHAR ,即输入缓冲区接收到数据事件。接下来,我们这里引入了一个COMSTAT 的结构,它是一个用来反映串口状态属性的结构,可以使用
ClearCommError函数来实现得到串口的当前状态属性。对于接收数据来讲,
cbInQue属性反映当前输入缓冲区内数据的字节数。然后我们就可以使用ReadFile函数来读取输入缓冲区内的数据了,同样dwBytes返还读取的数据字节数。
e) 关闭串口:关闭串口比较简单,可以直接利用CloseHandle关闭串口资源句柄即可。 程序源代码:
// SerialPort2Dlg.h : 头文件
//
#pragma once
#include "afxwin.h"
// CSerialPort2Dlg 对话框
class CSerialPort2Dlg : public CDialog
{
// 构造
public:
// 对话框数据
// 实现
protected:
// 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() CComboBox m_cboPort; CComboBox m_cboBaud; HICON m_hIcon; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 enum { IDD = IDD_SERIALPORT2_DIALOG }; CSerialPort2Dlg(CWnd* pParent = NULL); // 标准构造函数 public:
CComboBox m_cboData; CComboBox m_cboStop; CComboBox m_cboParity; CButton m_btnOpen; CButton m_btnSend; CEdit m_edtRecv; CEdit m_edtSend; HANDLE m_hComm; BOOL bOpen;
public:
};
// SerialPort2Dlg.cpp : 实现文件
//
#include "stdafx.h"
#include "SerialPort2.h"
#include "SerialPort2Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 用于应用程序“关于”菜单项的CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
// 对话框数据 CAboutDlg(); afx_msg void OnBnClickedButtonOpen(); bool OpenComm(); bool CloseComm(); bool RecvData(); bool SendData(LPCTSTR strData); CString m_strPort; afx_msg void OnBnClickedButtonSend(); CString m_strData; CString m_strBaud; CString m_strDatas;
enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) {
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX) {
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CSerialPort2Dlg 对话框
CSerialPort2Dlg::CSerialPort2Dlg(CWnd* pParent /*=NULL*/)
{
}
void CSerialPort2Dlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO_PORT, m_cboPort); DDX_Control(pDX, IDC_COMBO_BAUD, m_cboBaud); DDX_Control(pDX, IDC_COMBO_DATA, m_cboData); m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); : CDialog(CSerialPort2Dlg::IDD, pParent) , m_strPort(_T("")) , m_strData(_T("")) , m_strBaud(_T("")) , m_strDatas(_T("")) CDialog::DoDataExchange(pDX); DECLARE_MESSAGE_MAP()
}
DDX_Control(pDX, IDC_COMBO_STOP, m_cboStop); DDX_Control(pDX, IDC_COMBO_PARITY, m_cboParity); DDX_Control(pDX, IDC_BUTTON_OPEN, m_btnOpen); DDX_Control(pDX, IDC_BUTTON_SEND, m_btnSend); DDX_Control(pDX, IDC_EDIT_RECV, m_edtRecv); DDX_Control(pDX, IDC_EDIT_SEND, m_edtSend); DDX_CBString(pDX, IDC_COMBO_PORT, m_strPort); DDX_Text(pDX, IDC_EDIT_SEND, m_strData); DDX_CBString(pDX, IDC_COMBO_BAUD, m_strBaud); DDX_CBString(pDX, IDC_COMBO_DATA, m_strDatas);
BEGIN_MESSAGE_MAP(CSerialPort2Dlg, CDialog)
// CSerialPort2Dlg 消息处理程序
BOOL CSerialPort2Dlg::OnInitDialog() {
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); // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); // 将“关于...”菜单项添加到系统菜单中。 CDialog::OnInitDialog(); ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_BUTTON_OPEN, &CSerialPort2Dlg::OnBnClickedButtonOpen) ON_BN_CLICKED(IDC_BUTTON_SEND, &CSerialPort2Dlg::OnBnClickedButtonSend) END_MESSAGE_MAP()
}
} // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 // 设置小图标 SetIcon(m_hIcon, FALSE); // TODO: 在此添加额外的初始化代码 m_cboPort.SetCurSel(0); m_cboBaud.SetCurSel(3); m_cboData.SetCurSel(3); m_cboStop.SetCurSel(0); m_cboParity.SetCurSel(0); bOpen=FALSE; return TRUE; // 除非将焦点设置到控件,否则返回TRUE
void CSerialPort2Dlg::OnSysCommand(UINT nID, LPARAM lParam) {
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的MFC 应用程序, // 这将由框架自动完成。
void CSerialPort2Dlg::OnPaint()
{
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 if ((nID & 0xFFF0) == IDM_ABOUTBOX) { } else { } CDialog::OnSysCommand(nID, lParam); CAboutDlg dlgAbout; dlgAbout.DoModal();
}
} // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); else { } CDialog::OnPaint();
//当用户拖动最小化窗口时系统调用此函数取得光标 //显示。
HCURSOR CSerialPort2Dlg::OnQueryDragIcon() {
}
void CSerialPort2Dlg::OnBnClickedButtonOpen() {
// TODO: 在此添加控件通知处理程序代码 if(bOpen) { } else { if(OpenComm()) { } else { bOpen=FALSE; bOpen=TRUE; m_btnOpen.SetWindowText(_T("断开")); CloseComm(); bOpen=FALSE; m_btnOpen.SetWindowText(_T("连接")); return static_cast<HCURSOR>(m_hIcon);
}
} } m_btnOpen.SetWindowText(_T("连接"));
bool CSerialPort2Dlg::OpenComm() {
PurgeComm(m_hComm,PURGE_RXCLEAR|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_TXABORT); COMMTIMEOUTS commtimeouts; commtimeouts.ReadIntervalTimeout=0xFFFFFFFF; commtimeouts.ReadTotalTimeoutConstant=0; commtimeouts.ReadTotalTimeoutMultiplier=0; commtimeouts.WriteTotalTimeoutConstant=0; commtimeouts.WriteTotalTimeoutMultiplier=100; SetCommTimeouts(m_hComm,&commtimeouts); SetCommMask(m_hComm,EV_RXCHAR); SetupComm(m_hComm,1024,1024); if(bOpen)return false; UpdateData(); m_hComm=CreateFile(m_strPort,GENERIC_READ|GENERIC_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATif(m_hComm==INVALID_HANDLE_VALUE) { } DCB dcb; GetCommState(m_hComm,&dcb); dcb.BaudRate=atoi((LPCTSTR)m_strBaud); dcb.ByteSize=atoi((LPCTSTR)m_strDatas); dcb.StopBits=m_cboStop.GetCurSel(); dcb.fParity=TRUE; dcb.fNull=true; dcb.Parity=m_cboParity.GetCurSel(); SetCommState(m_hComm,&dcb); MessageBox(_T("打开串口失败!"),_T("提示"),MB_OK); return false; TRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
}
bOpen=true; return true;
bool CSerialPort2Dlg::CloseComm() {
}
bool CSerialPort2Dlg::RecvData() {
} if(dwByte) { } } } DWORD dwEvt,dwErr,dwByte=0; OVERLAPPED rOverlapped; CString strBuf; strBuf.GetBufferSetLength(1024); memset(&rOverlapped,0,sizeof(rOverlapped)); rOverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); WaitCommEvent(m_hComm,&dwEvt,&rOverlapped); if(WaitForSingleObject(rOverlapped.hEvent,100)==WAIT_OBJECT_0) { if(dwEvt==EV_RXCHAR) { { if(GetLastError()==ERROR_IO_PENDING) { } WaitForSingleObject(rOverlapped.hEvent,1000); COMSTAT comstat; memset(&comstat,0,sizeof(comstat)); ClearCommError(m_hComm,&dwErr,&comstat); if(comstat.cbInQue) { if(!bOpen)return false; CloseHandle(m_hComm); return true; if(!ReadFile(m_hComm,(LPVOID)(LPCTSTR)strBuf,comstat.cbInQue,&dwByte,&rOverlapped))
}
} strBuf.SetAt((int)dwByte,0); m_edtRecv.SetWindowTextA((LPCTSTR)strBuf); CloseHandle(rOverlapped.hEvent); strBuf.ReleaseBufferSetLength(0); else { } return true; CloseHandle(rOverlapped.hEvent); strBuf.ReleaseBufferSetLength(0); return false;
bool CSerialPort2Dlg::SendData(LPCTSTR strData) {
}
void CSerialPort2Dlg::OnBnClickedButtonSend() {
// TODO: 在此添加控件通知处理程序代码 UpdateData(); if(m_strData.IsEmpty()) return; OVERLAPPED wOverlapped; CString strBuf=strData; DWORD dwBytes; memset(&wOverlapped,0,sizeof(wOverlapped)); wOverlapped.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!WriteFile(m_hComm,(LPVOID)strData,strBuf.GetLength(),&dwBytes,&wOverlapped)) { } CloseHandle(wOverlapped.hEvent); return true; if(GetLastError()==ERROR_IO_PENDING) { } else { } return false; WaitForSingleObject(wOverlapped.hEvent,1000);
} if(SendData((LPCTSTR)m_strData)) { } else { } Sleep(1000); if(!RecvData()) { } MessageBox("未收到应答!","提示",MB_OK); return;
(未完待续)
2. 双线程串口处理程序。