一、创建智能MFC应用程序
打开vs2005,打开菜单“文件”“新建”“项目”,选中Visual C++智能设备,在右边的窗口选中MFC智能应用程序,输入项目名称,比如“Serial”。点击“确定”
点“下一步”
选中默认的Pocket PC 2003,点“下一步”
选中“基于对话框”选项,点击“完成”
二、程序解读
1、创建用户图形界面
在VS2005工作台上,切换到“资源视图”,双击Dialog文件夹的IDD_SERIAL_DIALOG对对话框进行编辑,请参照下图进行设定控件,相应的控件的属性如下:
请注意画红线的地方,只有这样设置才能正常的接收字符,否则edit control控件不会自动换行。ID设为IDC_REV
“发送区”设置如下:
把发送区的ID设为IDC_SEND,其它选项默认就行了
把“打开串口”、“关闭串口”、“发送按键”的ID分别设为IDC_OPEN_BUTTON、IDC_CLOSE_BUTTON、IDC_SEND_BUTTON,其它选项默认。
2、 添加代码
切换到“解决方案资源管理器”,在SerialDlg.h加入如下代码
添加与控件对应的变量
CButton ButClose; //对应“关闭串口”按键
CButton ButOpen; //对应“打开串口”按键
CButton ButSend; //对应“发送”按键
CString strSendEdit; //对应“发送区”的字符串变量
添加消息映射函数
afx_msg void OnOpenCom(); //响应“打开串口”按键
afx_msg void OnCloseCom(); //响应“关闭串口”按键
afx_msg void OnSend(); //响应“发送”按键
afx_msg void OnDestroy(); //应用程序结束时调用
添加必要的结构体和函数
DCB dcb; /* 串口参数结构体*/
HANDLE hComm; /* 串口操作句柄*/
HANDLE ExitThreadEvent; /* 串口接收线程退出事件*/
CString strRecDisp; /* 接收区显示字符*/
BOOL ClosePort(void); /* 关闭串口*/
// 打开串口
BOOL OpenPort(LPCTSTR Port, int BaudRate, int DataBits, int StopBits, int Parity);
// 串口接收线程
static DWORD ComRecvThread(LPVOID lparam);
// 串口接收数据成功回调函数
typedef void (CALLBACK *ONCOMMRECV)(CWnd* pWnd, char *buf, int buflen);
static void CALLBACK OnComRecv(CWnd* pWnd, char *buf, int buflen);
至此,必要的数据成员和成员函数已经添加完毕,打开SerialDlg.cpp,编写实现代码
在void CSerialDlg::DoDataExchange(CDataExchange* pDX)控件和相应变量的绑定,添加代码如下
CDialog::DoDataExchange(pDX);
DDX_Control(pDX,IDC_CLOSE_BUTTON ,ButClose);
DDX_Control(pDX,IDC_OPEN_BUTTON ,ButOpen);
DDX_Control(pDX,IDC_SEND_BUTTON ,ButSend);
DDX_Text(pDX,IDC_SEND ,strSendEdit);
添加消息处理程序
ON_BN_CLICKED(IDC_OPEN_BUTTON, OnOpenCom)
ON_BN_CLICKED(IDC_CLOSE_BUTTON, OnCloseCom)
ON_BN_CLICKED(IDC_SEND_BUTTON, OnSend)
ON_WM_DESTROY()
在BOOL CSerialDlg::OnInitDialog()添加如下初始化代码
strRecDisp=_T(""); /*接收字符串变量初始化*/
CenterWindow(GetDesktopWindow());
hComm = INVALID_HANDLE_VALUE; /* 串口操作句柄无效*/
ExitThreadEvent=NULL; /* 关闭线程事件句柄无效*/
ButClose.EnableWindow(FALSE); /* "关闭串口"按键无效*/
UpdateData(FALSE); /*把变量的值传给控件*/
添加打开串口函数代码BOOL CSerialDlg::OpenPort(LPCTSTR Port, int BaudRate, int DataBits, int StopBits, int Parity)
BOOL CSerialDlg::OpenPort(LPCTSTR Port, int BaudRate, int DataBits, int StopBits, int Parity)
{
COMMTIMEOUTS CommTimeOuts; //创建超时变量参数
DWORD error; //获得错误代码
//打开串口
hComm = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
if(hComm == INVALID_HANDLE_VALUE)
{
error=GetLastError();
MessageBox(_T("无法打开端口或端口已打开!请检查是否已被占用."));
return FALSE;
}
GetCommState(hComm, &dcb); /* 读取串口的DCB */
dcb.BaudRate= BaudRate; /*设置波特率*/
dcb.ByteSize = DataBits; /*设置数据位*/
dcb.Parity= Parity; /*设置校验位*/
dcb.StopBits= StopBits; /*设置停止位*/
dcb.fParity= FALSE; /* 禁止奇偶校验*/
dcb.fBinary= TRUE;
dcb.fOutX= 0;
dcb.fInX= 0;
//设置状态参数
SetCommMask(hComm, EV_RXCHAR); /* 串口事件:接收到一个字符*/
SetupComm(hComm, 16384, 16384); /* 设置接收与发送的缓冲区大小*/
SetCommState(hComm, &dcb);
BOOL isSucces=SetCommState(hComm, &dcb);/* 设置串口的DCB */
if(!isSucces)
{
error=GetLastError();
MessageBox(_T("无法按当前参数配置端口,请检查参数!"));
ClosePort();
return FALSE;
}
GetCommTimeouts(hComm, &CommTimeOuts); /*获得超时参数*/
//设置超时参数
CommTimeOuts.ReadIntervalTimeout = 100; /* 接收字符间最大时间间隔*/
CommTimeOuts.ReadTotalTimeoutMultiplier = 1;
CommTimeOuts.ReadTotalTimeoutConstant = 100; /* 读数据总超时常量*/
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
if(!SetCommTimeouts(hComm, &CommTimeOuts))
{
MessageBox(_T("无法设置超时参数!"));
ClosePort();
return FALSE;
}
PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR); /* 清除收/发缓冲区*/
return TRUE;
}
添加关闭串口函数代码BOOL CSerialDlg::ClosePort(void)
BOOL CSerialDlg::ClosePort(void)
{
if(hComm != INVALID_HANDLE_VALUE)
{
SetCommMask(hComm, 0); //通知线程退出
PurgeComm(hComm, PURGE_TXCLEAR | PURGE_RXCLEAR); /* 清除收/发缓冲*/
CloseHandle(hComm); /* 关闭串口操作句柄*/
hComm = INVALID_HANDLE_VALUE;
return TRUE;
}
return FALSE;
}
实现回调函数void CALLBACK CSerialDlg::OnComRecv(CWnd* pWnd, char *buf, int buflen)
void CALLBACK CSerialDlg::OnComRecv(CWnd* pWnd, char *buf, int buflen)
{
CString tmp;
CSerialDlg *pDlg = (CSerialDlg*)pWnd;
CEdit *pRecvStrEdit = (CEdit*)pDlg->GetDlgItem(IDC_REV); //获取接收区指针
for (int i = 0; i < buflen; i++, buf++)
{
tmp.Format(_T("%c"), *buf); /* 将字符转换为字符串*/
pDlg->strRecDisp += tmp;
}
pRecvStrEdit->SetWindowText(pDlg->strRecDisp); /* 显示在窗口上*/
}
实现串口线程DWORD CSerialDlg::ComRecvThread(LPVOID lparam)
DWORD CSerialDlg::ComRecvThread(LPVOID lparam)
{
DWORD dwLength;
char *recvBuf = new char[1024];
CSerialDlg *pDlg = (CSerialDlg*)lparam;
while(TRUE)
{
/* 等待线程退出事件*/
if (WaitForSingleObject(pDlg->ExitThreadEvent, 0) == WAIT_OBJECT_0)
break;
if (pDlg->hComm != INVALID_HANDLE_VALUE)
{ /* 从串口读取数据*/
BOOL fReadState = ReadFile(pDlg->hComm, recvBuf, 1024, &dwLength, NULL);
if(!fReadState)
{
//MessageBox(_T("无法从串口读取数据!"));
}
else
{
if(dwLength != 0)
OnComRecv(pDlg, recvBuf, dwLength-2); /* 接收成功调用回调函数*/
}
}
}
delete[] recvBuf;
return 0;
}
下面是消息处理函数实现
“打开串口”消息处理
void CSerialDlg::OnOpenCom()
{
DWORD IDThread;
HANDLE hRecvThread;
UpdateData(TRUE); /*把控件的值传给变量*/
CString strPort =_T("COM1:"); /* 设置串口号*/
DWORD baud = 115200; /* 设置波特率*/
DWORD databit = 8; /* 设置数据位*/
BYTE stopbit = ONESTOPBIT; /* 设置停止位*/
BYTE parity = NOPARITY; /* 设置校验位*/
BOOL ret = OpenPort(strPort, baud, databit, stopbit, parity); /* 打开串口*/
if (ret == FALSE)
return;
ExitThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* 创建串口接收线程退出事件*/
hRecvThread = CreateThread(0, 0, ComRecvThread, this, 0, &IDThread);
if (hRecvThread == NULL)
{
MessageBox(_T("创建接收线程失败!"));
return;
}
CloseHandle(hRecvThread);
ButOpen.EnableWindow(FALSE); /* 打开端口按键禁止*/
ButClose.EnableWindow(TRUE); /* 关闭端口按键使能*/
MessageBox(_T("打开串口COM1成功!"));
}
“关闭串口”消息处理
void CSerialDlg::OnCloseCom()
{
if (ExitThreadEvent != NULL)
{
SetEvent(ExitThreadEvent); /* 通知线程退出*/
Sleep(1000);
CloseHandle(ExitThreadEvent);
ExitThreadEvent = NULL;
}
ButOpen.EnableWindow(TRUE); /* 打开端口按键禁止*/
ButClose.EnableWindow(FALSE); /* 关闭端口按键使能*/
ClosePort();
}
“发送”消息处理
void CSerialDlg::OnSend()
{
DWORD dwactlen;
char *recvBuf = new char[1024];
if (hComm == INVALID_HANDLE_VALUE)
{
MessageBox(_T("串口未打开!"));
return;
}
UpdateData(TRUE);
int len = strSendEdit.GetLength(); /* 取得输入字符串长度*/
char *psendbuf = new char[len];
for(int i = 0; i < len;i++)
psendbuf[i] = (char)strSendEdit.GetAt(i); /* 转换为单字节字符*/
WriteFile(hComm, psendbuf, len, &dwactlen, NULL); /* 从串口发送数据*/
delete[] psendbuf;
}
程序结束调用的函数
void CSerialDlg::OnDestroy()
{
CDialog::OnDestroy();
if (ExitThreadEvent != NULL)
{
SetEvent(ExitThreadEvent); /* 通知串口接收线程退出*/
Sleep(1000);
CloseHandle(ExitThreadEvent); /* 关闭接收线程退出事件句柄*/
ExitThreadEvent = NULL;
}
ClosePort(); /* 关闭串口*/
}











