当前位置:首页 >> 电子/电路 >>

VC(MFC)编写串口调试助手


VC(MFC)编写串口调试助手
1. 序

确定基本功能: 1.自动寻找串口,并自动添加到下拉框中共选择; 2.有波特率、数据位、停止位、校验位的选择设置; 3.串口打开控制按钮; 4.发送、清除按钮; 5.接收是自动实现的; 6.有定时自动发送功能; 7.有传送文件功能; 8.有状态栏显示,指示串口状态,设置参数和发送接收显示。 下面就一步步实现,本人纯业余,只是记录下来这个学习过程,请勿拍砖。 开发平台 Visual C++6.0 英文版,电脑是 i7-2670Q 四核 8G 内存 1G 独显的笔记本, 装的 win7 64 位旗舰版,因此 VC6 兼容不是太好,有些小毛病,不过不影响编写。

2.

创建 MFC 项目

File -> New -> Projects 选择 MFC AppWizard(exe),项目名称 commassist

第 1 页 共 37 页

选择 OK

选中 Dialog based,点击 Next> 。

第 2 页 共 37 页

默认选项,点击 Next> ,

继续默认选项,点击 Next> ,如果选中 As a statically linked library,生产的 EXE 可直接在
第 3 页 共 37 页

没装 VC 的机器上运行。可以在项目中进行更改。

选择第二个 CCommassistDlg,点击 Finish

第 4 页 共 37 页

点击 OK。项目创建完毕,进入项目。

删除界面上确定和取消按钮以及静态文字。

第 5 页 共 37 页

3.

创建界面

保存后便可以开始创建界面了。

参考界面

仿照设计的界面,具体添加按钮或编辑框等的布局步骤就不用细说了。

4.

图标修改

在资源视图中选择 Icon 右键 InsertIcon 加入打开和关闭的 Icon 图标或自行绘制,如下图
第 6 页 共 37 页

IDR_MAINFRAME 原为 MFC 提供的图标,这里我直接改成自己的,生成 EXE 后将会显示 这个图标。下面将帮助页面图标也改为自绘图标。

在打开按钮旁边加入自绘的打开和关闭图标:先加入工具条中的 Picture,然后选中右键看 属性,并如图将 Image 选为默认的 IDI_ICON_CLOSE。如下图

第 7 页 共 37 页

5.

基本设置

下面对各个按钮及编辑框设置进行描述 右键串口对应的 Combo Box,ID 设置为 IDC_COMLIST,Type 设置为 Drop List,Sort 不选 择(我系统是 WIN7 64 位,不选中反而自动排序,至于 XP 得试试看了,以下的选择相同)。 右键波特率对应的 Combo Box,ID 设置为 IDC_BAUD,Type 及 Sort 同上。 右键数据位对应的 Combo Box,ID 设置为 IDC_BDATA,Type 及 Sort 同上。 右键停止位对应的 Combo Box,ID 设置为 IDC_BSTOP,Type 及 Sort 同上。 右键校验位对应的 Combo Box,ID 设置为 IDC_CAL,Type 及 Sort 同上。 每个下拉框要点击右边的小箭头,然后将其拉长,不然显示不出内容。

接收 EDIT 框 ID 设置为 IDC_EDIT_RX。 发送 EDIT 框 ID 设置为 IDC_EDIT_TX。 自动发送时间间隔的 EDIT 框 ID 设置为 IDC_EDIT_TIMER。 选择文件后面的 EDIT 框 ID 设置为 IDC_EDIT_FILEPATH。 接收区的十六进制显示的 Check Box 复选框 ID 设置为 IDC_CHECK_HEXRX。 发送区的十六进制发送的 Check Box 复选框 ID 设置为 IDC_CHECK_HEXTX。 按钮“打开串口”ID 设置为 IDC_COMCONTROL。 按钮“清空显示区”ID 设置为 IDC_BTN_CLRRX。 按钮“手动发送”ID 设置为 IDC_BTN_HANDSEND。 按钮“清空发送区”ID 设置为 IDC_BTN_CLRTX。 按钮“自动发送”ID 设置为 IDC_BTN_AUTOSEND。 按钮“选择文件”ID 设置为 IDC_BTN_SELCTFILE。 按钮“发送文件”ID 设置为 IDC_BTN_SENDFILE。

6. 开始写代码 6.1. 基本思路:
因为串口通信部分代码我可能用在以后的单片机上位机上,因此考虑单独形成 CPP 和 H 文 件,定义为 comm.cpp 和 comm.h。在 comm.cpp 中编写串口创建、打开、关闭以及串口监听 线程(用于自动接收)的代码,同时加入进制转换或显示的函数,这些在 comm.h 文件中申明, 在主对话框中包含 comm.h 即可。 想修改按钮样式,在网上搜了一圈,结果不轻松,最后确定创建新类来实现。

第 8 页 共 37 页

6.2. 创建自定义按钮类:
View -> Class Wizard 选择 Add Class -> New,名字 MyButton,基类选择 CButton。 在头文件 MyButton.h 中加入以下变量和函数定义: private: int m_Style; //按钮形状(0-正常,1-当前,2-按下,3-锁定) bool b_InRect; //鼠标进入标志 CString m_strText; //按钮文字 COLORREF m_ForeColor;//文本颜色 COLORREF m_MouseInColor;//鼠标进入时文本颜色 COLORREF m_BackColor;//背景颜色 COLORREF m_LockForeColor; //锁定按钮的文字颜色 CRect m_ButRect; //按钮尺寸 CFont* p_Font; //字体 void DrawButton(CDC *pDC); //画正常按钮 // 接口函数 public: MyButton(); void SetText(CString str); //设置文字 void SetForeColor(COLORREF color); //设置文本颜色 void SetBkColor(COLORREF color); //设置背景颜色 void SetTextFont(int FontHight,LPCTSTR FontName); //设置字体 在 MyButton.cpp 的构造函数中初始化变量: m_Style = 1; //m_Style = 0; //按钮形状风格 b_InRect = false; //鼠标进入标志 m_strText = _T(""); //按钮文字(使用默认文字) m_ForeColor = RGB(0,0,0); //文字颜色(黑色) m_MouseInColor = RGB(0,0,255); //鼠标进入时文字颜色(蓝色) m_BackColor = RGB(230,230,230); //m_BackColor = RGB(243,243,243); // 背 景 色(灰白色) m_LockForeColor = GetSysColor(COLOR_GRAYTEXT); //锁定按钮的文字颜色 p_Font = NULL; //字体指针

用 ClassWizard 添加下列消息函数: PreSubclassWindow(); DrawItem(); onMouseMove(); OnLButtonDown(); OnLButtonUp();

在各函数内加入代码:

第 9 页 共 37 页

void MyButton::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class ModifyStyle( 0, BS_OWNERDRAW ); //设置按钮属性为自画式 //PreSubclassWindow()在按钮创建前自动执行,所以我们可以在其中做一些初始工作。 //这里只做了一项工作,就是为按钮设置属性为“自绘”式,这样,用户在添加按钮后, 就不需设置“Owner draw”属性了。 CButton::PreSubclassWindow(); } void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TODO: Add your code to draw the specified item CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC ); m_ButRect = lpDrawItemStruct->rcItem; //获取按钮尺寸 if( m_strText.IsEmpty() ) GetWindowText( m_strText ); //获取按钮文本 int nSavedDC = pDC->SaveDC(); VERIFY( pDC ); DrawButton( pDC ); pDC->RestoreDC( nSavedDC );

//绘制按钮

} //DrawItem()函数是一个关键函数,按钮的绘制工作就在这里进行,它的作用相当于对话框 中的 OnPaint()函数和视图中的 OnDraw()函数。 //这里我做了三项工作:获取按钮尺寸、获取按钮文本、绘制按钮。其中绘制工作在自定义 函数 DrawButton()中完成。以下就是绘制过程: void MyButton::DrawButton(CDC *pDC) { //调整状态 if( m_Style==3 ) m_Style = 0; if( GetStyle() & WS_DISABLED ) m_Style = 3; //禁止状态 //根据状态调整边框颜色和文字颜色 COLORREF bColor, fColor; //bColor 为边框颜色,fColor 为文字颜色 switch( m_Style ) { case 0: bColor = RGB(192,192,192); fColor = m_ForeColor; break; //正常按钮 case 1: bColor = RGB(255,255,255); fColor = m_ForeColor; break; //鼠标进入时按钮 case 2: bColor = RGB(192,192,192); fColor = m_MouseInColor; break; //按下的按钮 case 3: bColor = m_BackColor; fColor = m_LockForeColor; break; //锁定的按钮
第 10 页 共 37 页

} //绘制按钮背景 CBrush Brush; Brush.CreateSolidBrush( m_BackColor ); //背景刷 pDC->SelectObject( &Brush ); CPen Pen; Pen.CreatePen(PS_SOLID, 3, bColor ); pDC->SelectObject( &Pen ); pDC->RoundRect(&m_ButRect,CPoint(10,10)); //画圆角矩形 //绘制按钮按下时的边框 if( m_Style!=2 ) { CRect Rect; Rect.SetRect( m_ButRect.left+1, m_ButRect.top+1, m_ButRect.right, m_ButRect.bottom ); pDC->DrawEdge( &Rect, BDR_RAISEDINNER, BF_RECT ); //画边框 } //绘制按钮文字 pDC->SetTextColor( fColor ); //画文字 pDC->SetBkMode( TRANSPARENT ); pDC->DrawText( m_strText, &m_ButRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS); //绘制拥有焦点按钮的虚线框 if( GetFocus()==this ) { CRect Rect; Rect.SetRect( m_ButRect.left+3, m_ButRect.top+2, m_ButRect.right-3, m_ButRect.bottom-2 ); pDC->DrawFocusRect( &Rect ); //画拥有焦点的虚线框 } } //变量 m_Style 表征当前按钮状态,它的取值为:0-正常,1-当前,2-按下,3-锁定。不同 状态下按钮的边框颜色和文字颜色有所不同。 //m_Style 的值在鼠标响应函数中进行修改。 //绘制工作主要利用 CDC 类的绘图函数完成,主要注意在 m_Style 不同取值下表现出来的 差别。 void MyButton::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default m_Style = 2; Invalidate(); //重绘按钮 CButton::OnLButtonDown(nFlags, point); }
第 11 页 共 37 页

//OnLButtonDown()函数是单击鼠标左键时的消息函数。这里只是重新绘制按钮,具体的单 击响应应该在拥有按钮的对话框或视图中进行。

void MyButton::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if( !b_InRect || GetCapture()!=this ) //鼠标进入按钮 { b_InRect = true; //设置进入标志 SetCapture(); //捕获鼠标 m_Style = 2; //m_Style = 1; //设置按钮状态 Invalidate(); //重绘按钮 } else { if ( !m_ButRect.PtInRect(point) ) //鼠标离开按钮 { b_InRect = false; //清除进入标志 ReleaseCapture(); //释放捕获的鼠标 m_Style = 1; //m_Style = 0; //设置按钮状态 Invalidate(); //重绘按钮 } } CButton::OnMouseMove(nFlags, point); } //onMouseMove()函数是鼠标移动消息函数, 用于判定当前鼠标指针是否在按钮上。b_InRect 是个标志,为 true 表示鼠标指针进入了按钮区域, //此时要捕获鼠标, 让鼠标命令传送给按钮。 当鼠标指针离开按钮时, 要清除 b_InRect 标志, 并且释放捕获的鼠标,让其它窗口可以接收鼠标命令。 //Invalidate()函数用于更新按钮,它会自动调用 DrawItem()函数重新绘制按钮。 //设置条件的目的是仅在鼠标指针进入按钮和离开按钮时更新按钮,这样可以防止鼠标在按 钮上移动时发生闪烁。 void MyButton::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default m_Style = 1; Invalidate(); //重绘按钮 CButton::OnLButtonUp(nFlags, point); } //OnLButtonUp()函数是单击鼠标左键后弹起时的消息函数。 这里也只是重绘按钮, 这样能使 按钮在按下和弹起时有所不同,使按钮看上去有动态效果。
第 12 页 共 37 页

//接口函数是用 CMyButton 类 定义的按钮修改颜色、字体和按钮文字的接口,由以下函数 组成: //设置按钮文本 void MyButton::SetText(CString str) { m_strText = _T(""); SetWindowText(str); } //设置文本颜色 void MyButton::SetForeColor(COLORREF color) { m_ForeColor = color; Invalidate(); } //设置背景颜色 void MyButton::SetBkColor(COLORREF color) { m_BackColor = color; Invalidate(); } //设置字体(字体高度、字体名) void MyButton::SetTextFont(int FontHight,LPCTSTR FontName) { if ( p_Font ) delete p_Font; //删除旧字体 p_Font = new CFont; p_Font->CreatePointFont( FontHight, FontName ); //创建新字体 SetFont( p_Font ); //设置字体 } ///由于新字体由 new 生成,必须显式回收,这项工作可以在 CMyButton 类 的析构函数中 进行: /*CMyButton::~CMyButton() { if ( p_Font ) delete p_Font; //删除字体 } */ //这样一个可设置颜色、字体的按钮类就做好了。使用时,先在对话框中放置好按钮,再用
第 13 页 共 37 页

ClassWizard 为按钮添加控制变量, //并且将变量的类型设置为 CMyButton。之后,可以用该变量调用接口函数设置按钮颜色和 字体。 OK,自定义按钮完成。

6.3. 实现过程及代码:
现在可以对按钮,EDIT 框等控件添加变量,文字描述麻烦,上图。

comm.cpp 编写内容如下 #include "stdafx.h" #include "commassist.h" #include "commassistDlg.h" #include "comm.h" char ConvertHexChar(char ch); HANDLE hCom; //串口句柄 CString strcomname; //串口名,如"COM1" bool ComIsOK; //串口打开状态标识,为真表示已打开,否则未打开 //============自动寻找串口函数================================= //函数功能:通过扫描注册表来找出当前所有物理串口
第 14 页 共 37 页

//输入参数:无 //返回类型:无 // 说 明:若搜索成功,则每搜到一个串口便发送消息通知主对话框,并将串口号以 WPARAM 传递 void FindComm() { //枚举当前系统中的串口 LONG result = 0; HKEY key = NULL; result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, //需要打开的主键的名称 "HARDWARE\\DEVICEMAP\\SERIALCOMM", // 需 要 打 开 的 子 键的名称,设备串口 0, //保留,必须设置为 0 KEY_READ, //安全访问标记,也就是权限 &key); //得到的将要打开键的句柄,当不再需要 句柄, //必须调用 RegCloseKey 关闭它 if( result ) { AfxMessageBox("无法获取串口,请确认是否安装并连接串口!"); return; } TCHAR portname[250]; //串口名 TCHAR data[250]; DWORD portnamelen = 0; //串口名长度 DWORD datalen = 0; int index = 0; while(1) //找完 COM 后跳出 { portnamelen = 255; datalen = 255; result = RegEnumValue(key, //Long,一个已打开项的句柄,或者指定一个 标准项名 index++, //Long,欲获取值的索引。注意第一个值 的索引编号为零 portname, //String, 用于装载位于指定索引处值名的一个 缓冲区 &portnamelen, //Long,用于装载 lpValueName 缓冲区长度的 一个变量。 //一旦返回,它会设为实际载入缓冲区的 字符数量 NULL, //Long,未用;设为零 NULL, //Long,用于装载值的类型代码的变量 (LPBYTE)data, //Byte,用于装载值数据的一个缓冲区
第 15 页 共 37 页

&datalen); 量。

//Long, 用于装载 lpData 缓冲区长度的一个变 //一旦返回,它会设为实际载入缓冲区的

字符数量 if( result ) break; // 发送消息 ,WM_USER+1 为自定义消息 , 即找到串口的 , 并将串口号 "COMx" 通过 WPARAM 参数传送给主对话框窗口 //::AfxGetMainWnd()->m_hWnd,获得主对话框句柄 //(WPARAM)(LPCTSTR)data,类型转换 ::SendMessage(::AfxGetMainWnd()->m_hWnd,WM_FOUNDCOMM,(WPARAM)(LPCTS TR)data,0); } RegCloseKey(key); //调用 RegCloseKey 关闭打开键的句柄 } //============自动寻找串口函数结束================== //==========串口打开函数=========================== //功 能:打开串口,将已打开的串口句柄赋值给 hCom,给出串口打开状态 ComIsOK,完成串 口状态设置 //输入参数:波特率,数据位,停止位,校验位 //返回类型:无 void OpenComm(int nBaud, int nData, int nStop, int nCal) { hCom = CreateFile ( strcomname, //串口号 GENERIC_READ | GENERIC_WRITE, //允许读或写 0, //独占方式 NULL, OPEN_EXISTING, //打开而不是创建 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,// 重叠方式,用于异步 通信 NULL ); if(hCom == INVALID_HANDLE_VALUE) { AfxMessageBox("打开 COM 失败,串口不存在或已被占用!"); ComIsOK = false; return; } ComIsOK = true; SetCommMask(hCom, EV_TXEMPTY | EV_RXCHAR ); // 设置事件掩码 , 暂时没用 上 SetupComm(hCom,1024,1024); COMMTIMEOUTS TimeOuts; //设置输入缓冲区和输出缓冲区的大小都是 1024

第 16 页 共 37 页

//设定读超时 TimeOuts.ReadIntervalTimeout = MAXDWORD; TimeOuts.ReadTotalTimeoutConstant = 0; TimeOuts.ReadTotalTimeoutMultiplier = 0; //设定写超时 TimeOuts.WriteTotalTimeoutConstant = 500; TimeOuts.WriteTotalTimeoutMultiplier = 100; if(SetCommTimeouts(hCom,&TimeOuts) == false) { CloseHandle(hCom); ComIsOK = false; return; } //串口属性配置 DCB dcb; GetCommState(hCom,&dcb); dcb.BaudRate=nBaud; //dcb.BaudRate=9600; //波特率为 9600 dcb.ByteSize=nData; //dcb.ByteSize=8; //每个字节为 8 位 dcb.StopBits=nStop; //dcb.StopBits=ONESTOPBIT; //1 位停止位 dcb.Parity=nCal; //dcb.Parity=NOPARITY; //无奇偶检验位 SetCommState(hCom,&dcb); PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR); if(SetCommState(hCom,&dcb) == false) { CloseHandle(hCom); ComIsOK = false; return; } return; } //==========串口打开函数结束=====================

//==========串口关闭控制函数===================== void CloseComm() { CloseHandle(hCom); hCom = NULL; ComIsOK = false; } //==========串口关闭控制函数结束================== //==========串口监听线程函数======================
第 17 页 共 37 页

UINT ThreadFunc(LPVOID pParam) { // CCommassistDlg* pdlg = (CCommassistDlg*)pParam; //定义指针指向主对话框 COMSTAT ComStat; DWORD dwErrorFlags; while(ComIsOK) { DWORD dwBytesRead = 100; ClearCommError(hCom,&dwErrorFlags,&ComStat); dwBytesRead = min(dwBytesRead,(DWORD)ComStat.cbInQue); if(!dwBytesRead) { Sleep(10);//continue;//使用 continue 时,打开串口后 CPU 占用率非常高 } else ::SendMessage(::AfxGetMainWnd()->m_hWnd,WM_READCOMM,1,0); // 发 送 消息,已读到 } return 0; } //==========串口监听线程函数结束================ //=================字符串转 16 进制显示========== //字符串转 16 进制显示的函数 //传入参数 Data 为字符串 //Blank_allow 为空格允许标志,为真则代表允许加入空格 //函数返回为 CString 的结果 sResult CString DisplayCString2Hex(CString Data, bool Blank_allow) { CString sResult; CString sTemp; int Data_Length; Data_Length = Data.GetLength(); if (Data_Length == 0) return ""; char *pchar = new char[Data_Length]; //用了 new 分配内存空间,要记得释放 strncpy(pchar,Data,Data_Length); for(int i=0; i<Data_Length; i++) { sTemp.Format("%02X",pchar[i]); if(Blank_allow) { if(i == Data_Length -1) sResult = sResult + sTemp; //去掉最后一个空格 else sResult = sResult + sTemp+" "; } else sResult = sResult + sTemp;
第 18 页 共 37 页

} delete pchar; return sResult;

//释放内存空间

} //===============函数结束============================ //=================16 进制转字符串====================== //16 进制转字符串,输入 16 进制的字符串,输出转换为 16 进制码 //传入参数 str 为字符串,判断输入是否按照 16 进制格式输入 int ConvertHexC2String(CString str, CByteArray &senddata) { //先判断输入字符串是否 2 个字符一组 int str_Length,iLength; int hexdata, l_data; char hstr,lstr; char cTemp; str_Length = str.GetLength(); iLength = 0; senddata.SetSize(str_Length/2); //预先设置数组长度,不设置时,允许有错 char *ppchar = new char[str_Length]; strncpy(ppchar,str,str_Length); for(int i=0; i<str_Length; ) { cTemp = ppchar[i]; if(cTemp == ' ') { //iLength--; i++; continue; //如检测到空格则跳过,继续下一次循环 } else { hstr = ppchar[i]; //取出字符作为 16 进制高位 i++; lstr = ppchar[i]; //取出下一个字符作为 16 进制低位 if(lstr == ' ') //若取出的低位为空格,则不符合 16 进制 2 个一组的 格式,终止循环 { AfxMessageBox(" 请 按 照 16 进 制 每 2 个 字 符 一 组 的 方 式 输 入 ",MB_ICONERROR); break; } else {
第 19 页 共 37 页

hexdata = ConvertHexChar(hstr); //高位转换为相应的 0 进制 l_data = ConvertHexChar(lstr); //低位转换为相应的 10 进制 if( (hexdata == -1) || (l_data == -1) ) { AfxMessageBox("请按照 16 进制字符要求输入",MB_ICONERROR); break; } else hexdata = hexdata*16 + l_data; //安装 16 进制方式高位低位合并 senddata[iLength] = (char)hexdata; //int 整型数转换为 char 字符型,并 存入数组 senddata[] i++; //进入下一次循环 iLength++; //成功转换一组(2 个)字符,记录长度加 1 } } } senddata.SetSize(iLength); delete ppchar; return iLength; } //===============函数结束=========================== char ConvertHexChar(char ch) { //将一个字符转换为相应的十六进制 if ((ch >= '0') && (ch <= '9')) return ch - 48;//0x30; else if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10; else if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; else return (-1); }

//=================16 进制转字符串显示===================== //16 进制转字符串显示的函数 //传入参数 Data 为 16 进制的字符串 //函数返回为 CString 的结果 sResult CString DisplayHex2CString(CString Data) { CString sResult; CString sTemp;
第 20 页 共 37 页

int Data_Length; Data_Length = Data.GetLength(); if (Data_Length == 0) return ""; char *pchar = new char[Data_Length]; strncpy(pchar,Data,Data_Length); for(int i=0; i<Data_Length; i++) { sTemp.Format("%02X",pchar[i]); sResult = sResult + sTemp; } delete pchar; //释放内存空间 return sResult;

//用了 new 分配内存空间,要记得释放

} //===============函数结束=============================

comm.h 编写如下: #ifndef comm_H #define comm_H

#define WM_FOUNDCOMM WM_USER + 1 //自定义消息 WM_FOUNDCOMM, 收到 该消息表示串口已经找到 #define WM_READCOMM WM_USER + 2 //自定义消息 WM_READCOMM,收 到该消息缓冲区有数据,可以读取 extern void FindComm(); //申明为外部函数 extern void OpenComm(int nBaud, int nData, int nStop, int nCal); extern void CloseComm(); extern UINT ThreadFunc(LPVOID pParam); //申明全局线程处理函数 extern CString DisplayCString2Hex(CString Data, bool Blank_allow); extern CString DisplayHex2CString(CString Data); extern int ConvertHexC2String(CString str, CByteArray &senddata); extern bool ComIsOK; //申明为外部变量 extern HANDLE hCom; extern CString strcomname;

#endif 在 commassistDlg.h 中包含 MyButton.h 文件 在#endif // _MSC_VER > 1000 后加入 #include "MyButton.h" 在// Construction 后加入变量及函数
第 21 页 共 37 页

CWinThread* pReceiveThread; void ShowStatus(); int m_intTxCnt; int m_intRxCnt; BOOL m_bAutoSend; DWORD ReadComm();

在 commassistDlg.cpp 中 加入头文件 comm.h #include "comm.h" 加入全局变量 CString strIn; CString strOut; CString m_strFile;

变量初始化如 ///////////////////////////////////////////////////////////////////////////// // CCommassistDlg dialog CCommassistDlg::CCommassistDlg(CWnd* pParent /*=NULL*/) : CDialog(CCommassistDlg::IDD, pParent) { //{{AFX_DATA_INIT(CCommassistDlg) m_strTimer = _T(""); m_strFilePath = _T(""); m_strOut = _T(""); m_strStatus = _T(""); m_check_hexrx = FALSE; m_check_hextx = FALSE; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); //下面就是自己添加的变量初始化 m_strTimer = "1000"; m_strFilePath = "请选择要发送的文件"; m_intTxCnt = 0; m_intRxCnt = 0; m_bAutoSend = 0; strIn = ""; strOut = ""; }

第 22 页 共 37 页

在 OnInitDialog 函数中添加代码如下 // TODO: Add extra initialization here m_comcontrol.SetForeColor(RGB(255,0,0)); FindComm(); //调用自动找串口函数 m_comlist.SetCurSel(0); //设置串口号下拉框默认值为第一个 m_baud.SetCurSel(6); //设置波特率下拉框默认值为 9600 m_bdata.SetCurSel(3); //设置数据位下拉框默认值为 8 位 m_bstop.SetCurSel(0); //设置停止位下拉框默认值为 1 m_cal.SetCurSel(0); //设置校验位下拉框默认值为 None 无 GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(false); // 设 置手动发 送按钮不 可 用 GetDlgItem(IDC_BTN_AUTOSEND)->EnableWindow(false); // 设 置自动发 送按钮不 可 用 GetDlgItem(IDC_EDIT_TIMER)->EnableWindow(false); //设置发送间隔按钮不可用 GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false); // 设 置 选 择 文 件 按 钮 不可用 GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(false); // 设 置 发 送 文 件 按 钮 不可用 ShowStatus(); //下面语句用于解决程序运行后初始化 EDIT 框内容被默认自动选中状态 //返回值需更改为 FALSE GetFocus(); //获取焦点 SetFocus(); //设置焦点 m_CEditStatus.SetSel(-1,-1,FALSE); //设置 // PostMessage(EM_SETSEL,-1,0); return FALSE; // return TRUE unless you set the focus to a control

用 ClassWizard 添加 WindowProc 函数来处理自定义消息

第 23 页 共 37 页

函数内容如下: //=====================消息处理函数=========================== //功能:处理自定义消息========================================= LRESULT CCommassistDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // TODO: Add your specialized code here and/or call the base class switch(message) { case WM_FOUNDCOMM : //已找到串口,串口号以字符串形式由 wParam 传递 m_comlist.AddString((LPCTSTR)wParam); //用 AddString 添加一个字符串即 COM 号 到 m_comlist 列表框中 break; case WM_READCOMM : //读串口消息 ReadComm(); break; } return CDialog::WindowProc(message, wParam, lParam); } //=====================消息处理函数结束===================== 双击“打开串口”按钮编辑代码如下: void CCommassistDlg::OnComcontrol() { // TODO: Add your control notification handler code here int nBaud,nData,nStop,nCal,nTemp; CString sTemp,siTemp; //波特率下拉框设置================= nTemp=m_baud.GetCurSel(); switch(nTemp) { case 0: nBaud = CBR_110; break; case 1: nBaud = CBR_300; break; case 2: nBaud = CBR_600; break; case 3: nBaud = CBR_1200; break; case 4: nBaud = CBR_2400; break; case 5: nBaud = CBR_4800; break; case 6: nBaud = CBR_9600; break;
第 24 页 共 37 页

case 7: nBaud = CBR_14400; break; case 8: nBaud = CBR_19200; break; case 9: nBaud = CBR_38400; break; case 10: nBaud = CBR_56000; break; case 11: nBaud = CBR_57600; break; case 12: nBaud = CBR_115200; break; case 13: nBaud = CBR_128000; break; case 14: nBaud = CBR_256000; break; } //数据位下拉框设置================= nTemp=m_bdata.GetCurSel(); switch(nTemp) { case 0: nData = 5; break; case 1: nData = 6; break; case 2: nData = 7; break; case 3: nData = 8; break; } //停止位下拉框设置================= nTemp=m_bstop.GetCurSel(); switch(nTemp) { case 0: nStop = ONESTOPBIT; break; case 1: nStop = ONE5STOPBITS; break; case 2: nStop = TWOSTOPBITS; break; } //校验位下拉框设置================= nTemp=m_cal.GetCurSel(); switch(nTemp)
第 25 页 共 37 页

{ case 0: nCal = NOPARITY; case 1: nCal = ODDPARITY; case 2: nCal = EVENPARITY; case 3: nCal = MARKPARITY; case 4: nCal = SPACEPARITY; }

break; break; break; break; break;

int commnum_buf; commnum_buf = m_comlist.GetCurSel(); if(commnum_buf < 0) { MessageBox("获取串口错误","错误",MB_ICONERROR); ComIsOK = FALSE; return; } m_comlist.GetLBText(commnum_buf,strcomname); if(!ComIsOK) { OpenComm(nBaud,nData,nStop,nCal); //调用打开串口函数 OpenComm() if(ComIsOK) pReceiveThread=AfxBeginThread(ThreadFunc,this,THREAD_PRIORITY_LOWEST); // 启动接收线程 ShowStatus(); if(!ComIsOK) m_comcontrol.SetWindowText("打开串口"); else { m_comcontrol.SetText("关闭串口"); //按钮显示状态改变 m_comcontrol.SetForeColor(RGB(0,155,0)); //串口打开后文本颜色变绿 m_ctrlIcon.SetIcon((HICON)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON_OPEN), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0)); //显示打开 icon m_comlist.EnableWindow(false); //设置串口号下拉框不可用 m_baud.EnableWindow(false); //设置波特率下拉框不可用 m_bdata.EnableWindow(false); //设置数据位下拉框不可用 m_bstop.EnableWindow(false); //设置停止位下拉框不可用
第 26 页 共 37 页

m_cal.EnableWindow(false); //设置校验位下拉框不可用 GetDlgItem(IDC_BTN_HANDSEND)-> EnableWindow(true); 发送按钮不可用 GetDlgItem(IDC_BTN_AUTOSEND)-> EnableWindow(true); 发送按钮可用 GetDlgItem(IDC_EDIT_TIMER) -> EnableWindow(true); 间隔按钮可用 GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(true); 文件按钮可用 GetDlgItem(IDC_BTN_SENDFILE)-> EnableWindow(true); 文件按钮可用 } return; } else {

// 设 置 手 动 // 设 置 自 动 // 设 置 发 送 // 设 置 选 择 // 设 置 发 送

CloseComm(); //调用关闭串口函数 CloseComm() // TerminateThread(pReceiveThread,0); ShowStatus(); m_comcontrol.SetText("打开串口"); m_comcontrol.SetForeColor(RGB(255,0,0)); m_ctrlIcon.SetIcon((HICON)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON_CLOSE), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0)); //显示关闭 icon m_comlist.EnableWindow(true); //设置串口号下拉框可用 m_baud.EnableWindow(true); //设置波特率下拉框可用 m_bdata.EnableWindow(true); //设置数据位下拉框可用 m_bstop.EnableWindow(true); //设置停止位下拉框可用 m_cal.EnableWindow(true); //设置校验位下拉框可用 GetDlgItem(IDC_BTN_HANDSEND)-> EnableWindow(false); // 设 置 手 动 发 送 按钮不可用 GetDlgItem(IDC_BTN_AUTOSEND)-> EnableWindow(false); // 设 置 自 动 发 送 按钮不可用 GetDlgItem(IDC_EDIT_TIMER) -> EnableWindow(false); // 设 置 发 送 间 隔 按钮不可用 GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false); // 设 置 选 择 文 件 按钮不可用 GetDlgItem(IDC_BTN_SENDFILE)-> EnableWindow(false); // 设 置 发 送 文 件 按钮不可用 return;
第 27 页 共 37 页

} } 添加读串口函数,代码如下: DWORD CCommassistDlg::ReadComm() { CString strTemp; OVERLAPPED m_osRead; memset(&m_osRead,0,sizeof(OVERLAPPED)); m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); char lpInBuffer[1024]; DWORD dwBytesRead=1024; BOOL bReadStatus; bReadStatus=ReadFile(hCom,lpInBuffer,dwBytesRead,&dwBytesRead,&m_osRead); if(!bReadStatus) //如果 ReadFile 函数返回 FALSE { if(GetLastError()==ERROR_IO_PENDING) //GetLastError()函数返回 ERROR_IO_PENDING,表明串口正在进行读操作 { WaitForSingleObject(m_osRead.hEvent,2000); //使用 WaitForSingleObject 函数等待,直到读操作完成或延时已达到 2000ms //当串口读操作进行完毕后,m_osRead 的 hEvent 事件会变为有信号 PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); return dwBytesRead; } return 0; } lpInBuffer[dwBytesRead] = NULL; strTemp = lpInBuffer; m_intRxCnt += strTemp.GetLength(); //接收到字节数统计 // GetDlgItemText(IDC_EDIT_RX,strIn); strIn += strTemp; OnCheckHexrx(); ShowStatus(); return 1; } 添加 OnClose 函数

第 28 页 共 37 页

编辑代码如下: void CCommassistDlg::OnClose() { // TODO: Add your message handler code here and/or call default TerminateThread(pReceiveThread,0); //程序退出时,关闭串口监听线程 WaitForSingleObject(pReceiveThread,INFINITE); CDialog::OnClose(); }

双击“手动发送”按钮,编辑代码如下: UINT CCommassistDlg::OnBtnHandsend() { // TODO: Add your control notification handler code here if(ComIsOK == FALSE) { MessageBox("请先打开串口","提示",MB_ICONINFORMATION); return 0; } BOOL bWriteStat; UpdateData(TRUE); CString str,sTemp; DWORD dwBytesWritten=1024; OVERLAPPED m_osWrite; memset(&m_osWrite,0,sizeof(OVERLAPPED)); m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); COMSTAT ComStat;
第 29 页 共 37 页

DWORD dwErrorFlags; // dwBytesWritten = OnCheckHextx(); GetDlgItem(IDC_EDIT_TX)-> GetWindowText(strOut); if(m_check_hextx) { int i,n; CString strTemp; CByteArray hexdata; // GetDlgItem(IDC_EDIT_TX)-> GetWindowText(strOut); dwBytesWritten = ConvertHexC2String(strOut,hexdata); n = hexdata.GetSize(); for(i=0;i<n;i++) { str.Format("%c", hexdata[i]); strTemp += str; } // SetDlgItemText(IDC_EDIT_TX,strTemp); strOut = strTemp; } else { GetDlgItem(IDC_EDIT_TX)-> GetWindowText(str); SetDlgItemText(IDC_EDIT_TX,""); sTemp = DisplayHex2CString(str); dwBytesWritten = str.GetLength(); SetDlgItemText(IDC_EDIT_TX,strOut); } UpdateData(); if(dwBytesWritten==0) { MessageBox(" 请 在 发 送 区 内 输 入 要 发 送 的 内 容 "," 提 示 ",MB_ICONINFORMATION); return 0; } m_intTxCnt += dwBytesWritten; ShowStatus(); ClearCommError(hCom,&dwErrorFlags,&ComStat); bWriteStat=WriteFile(hCom,strOut,dwBytesWritten,&dwBytesWritten,&m_osWrite); if(!bWriteStat) { if(GetLastError()==ERROR_IO_PENDING) { WaitForSingleObject(m_osWrite.hEvent,1000);
第 30 页 共 37 页

return dwBytesWritten; } return 0; } ShowStatus(); PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); return dwBytesWritten; } 编辑 ShowStatus 函数,代码如下: void CCommassistDlg::ShowStatus() { //状态栏显示状态 CString strTXcnt; CString strRXcnt; CString sTemp; CString comnum; CString strBaud,strStop,strData,strCal; UpdateData(true); if(ComIsOK) { m_comlist.GetLBText(m_comlist.GetCurSel(),sTemp); comnum = sTemp + "已打开"; } else comnum="未打开串口"; strTXcnt.Format("发送:%d", m_intTxCnt); strRXcnt.Format("接收:%d", m_intRxCnt); m_baud.GetLBText(m_baud.GetCurSel(),strBaud); m_bstop.GetLBText(m_bstop.GetCurSel(),strStop); m_bdata.GetLBText(m_bdata.GetCurSel(),strData); m_cal.GetLBText(m_cal.GetCurSel(),strCal); m_strStatus = "串口: " + comnum + " " +"状态: " + strTXcnt + ", " + strRXcnt + ", " + "波 特率: " + strBaud + ", " + "数据位: " + strData + ", " + "停止位: " + strStop + ", " + "校验位: " + strCal; UpdateData(FALSE); } 双击“清空显示区”按钮,编辑代码如下: void CCommassistDlg::OnBtnClrrx() { // TODO: Add your control notification handler code here GetDlgItem(IDC_EDIT_RX);
第 31 页 共 37 页

SetDlgItemText(IDC_EDIT_RX,""); m_intRxCnt = 0; m_intTxCnt = 0; strIn = ""; ShowStatus(); }

双击“清空发送区”按钮,编辑代码如下: void CCommassistDlg::OnBtnClrtx() { // TODO: Add your control notification handler code here GetDlgItem(IDC_EDIT_TX); SetDlgItemText(IDC_EDIT_TX,""); } 添加 WM_TIMER 定时器

OnTimer 函数代码如下: void CCommassistDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default if(nIDEvent == 1) OnBtnHandsend(); else if(nIDEvent == 2) return; CDialog::OnTimer(nIDEvent); }
第 32 页 共 37 页

双击“自动发送”按钮,编辑代码如下: void CCommassistDlg::OnBtnAutosend() { // TODO: Add your control notification handler code here UpdateData(TRUE); m_bAutoSend = !m_bAutoSend; if(!m_strOut.GetLength()) { MessageBox("请先输入要发送的内容","提示",MB_ICONINFORMATION); m_bAutoSend = !m_bAutoSend; } else { if (m_bAutoSend) { SetTimer(1, atoi(m_strTimer.GetBuffer(m_strTimer.GetLength())), NULL); 设置定时 m_autosend.SetText("停止"); GetDlgItem(IDC_COMCONTROL) -> EnableWindow(false); GetDlgItem(IDC_BTN_CLRTX) -> EnableWindow(false); GetDlgItem(IDC_BTN_CLRRX) -> EnableWindow(false); GetDlgItem(IDC_BTN_HANDSEND)-> EnableWindow(false); GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false); GetDlgItem(IDC_BTN_SENDFILE)-> EnableWindow(false); } else { KillTimer(1); m_autosend.SetText("自动发送"); GetDlgItem(IDC_COMCONTROL) -> EnableWindow(true); GetDlgItem(IDC_BTN_CLRTX) -> EnableWindow(true); GetDlgItem(IDC_BTN_CLRRX) -> EnableWindow(true); GetDlgItem(IDC_BTN_HANDSEND)-> EnableWindow(true); GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(true); GetDlgItem(IDC_BTN_SENDFILE)-> EnableWindow(true); } } }

//

双击“十六进制显示”复选框,编辑代码如下:
第 33 页 共 37 页

void CCommassistDlg::OnCheckHexrx() { // TODO: Add your control notification handler code here UpdateData(TRUE); CString hexIn; CString sTemp; if(m_check_hexrx) { hexIn=DisplayCString2Hex(strIn,true); GetDlgItem(IDC_EDIT_RX)-> SetWindowText(hexIn); IDC_EDIT_RX 框内,即为显示转换 } else GetDlgItem(IDC_EDIT_RX)-> SetWindowText(strIn); IDC_EDIT_RX 框内,即为显示不转换 return; }

// 将 hexIn 内 容 放 入

// 将

strIn

内 容 放 入

双击“十六进制发送”复选框,编辑代码如下: void CCommassistDlg::OnCheckHextx() { // TODO: Add your control notification handler code here UpdateData(); CString hexOut; CString str,sTemp; GetDlgItem(IDC_EDIT_TX)-> GetWindowText(strOut); if(m_check_hextx) { str = DisplayCString2Hex(strOut,true); strOut = str; } else { int i,n; CString strTemp; CByteArray hexdata; ConvertHexC2String(strOut,hexdata); n = hexdata.GetSize(); for(i=0;i<n;i++) { str.Format("%c", hexdata[i]); strTemp += str; } strOut = strTemp;
第 34 页 共 37 页

} GetDlgItem(IDC_EDIT_TX)-> SetWindowText(strOut); IDC_EDIT_TX 框内,即为不转换 UpdateData(); return; }

// 将

strOut

内 容 放 入

双击“选择文件”按钮,编辑代码如下: void CCommassistDlg::OnBtnSelctfile() { // TODO: Add your control notification handler code here CFile file; m_strFile.Empty(); CFileDialog FileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY OFN_OVERWRITEPROMPT, "文本文件(*.txt)|*.txt||"); if (FileDlg.DoModal() == IDOK) //打开文件对话框 m_strFilePath = FileDlg.GetPathName(); //得到文件路经 else return; file.Open(m_strFilePath, CFile::modeRead | CFile::typeBinary); //打开这个文件 file.Read(m_strFile.GetBuffer(file.GetLength()), file.GetLength()); //读文件 m_strFile.ReleaseBuffer(); if(m_strFile.GetLength() >= 2048) { AfxMessageBox("文件的长度超过 2k 字节!",MB_ICONINFORMATION); } else { m_strOut += m_strFile; //文件内容加入发送框变量内 UpdateData(false); //更新发送框内容 } file.Close(); }

|

双击“发送文件”按钮,编辑代码如下: void CCommassistDlg::OnBtnSendfile() { // TODO: Add your control notification handler code here COMSTAT state; DWORD errors; CString sTemp; int iTemp;
第 35 页 共 37 页

ClearCommError(hCom,&errors,&state); //清除串口错误、得到当前状态 iTemp=m_strOut.GetLength(); //写入串口的字符串长度,由 EDIT 控件内字符 串数决定 iTemp += iTemp; OnBtnHandsend(); //调用发送函数 GetDlgItem(IDC_EDIT_FILEPATH)->SetDlgItemText(IDC_EDIT_FILEPATH,""); UpdateData(false); //更新发送框内容; } 最后修改一下程序框的背景色或编辑框的文字颜色等,需要用到 OnCtlColor 函数,用 Class Wizard 添加函数

编辑代码如下: HBRUSH CCommassistDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Change any attributes of the DC here if(nCtlColor==CTLCOLOR_DLG) //所有对话框 { HBRUSH brush=CreateSolidBrush(RGB(220,250,250)); return brush; } if(nCtlColor == CTLCOLOR_STATIC) { pDC->SetTextColor(RGB(50,50,50)); //pDC->SetBkColor(RGB(128,128,128));//设置文本背景色 //pDC->SetTextColor(RGB(55,55,66));
第 36 页 共 37 页

pDC->SetBkMode(TRANSPARENT);//设置背景透明 } switch (pWnd->GetDlgCtrlID()) { //针对 ID 为 IDC_CTL1、IDC_CTL2 和 IDC_CTL3 的控件进行同样的设置 case IDC_EDIT_RX: { pDC->SetBkMode(TRANSPARENT);//背景色透明 pDC->SetTextColor(RGB(250,0,0));// 设置字体颜色为红色 } break; case IDC_EDIT_TX: { pDC->SetBkMode(TRANSPARENT);//背景色透明 pDC->SetTextColor(RGB(0,0,255));// 设置字体颜色为红色 } } // TODO: Return a different brush if the default is not desired return hbr; } 至此,大功告成。

6.4. 运行效果果

第 37 页 共 37 页


赞助商链接
相关文章:
串口调试助手VC源程序及编程详细过程
串口调试助手VC源程序及编程详细过程_计算机软件及应用_IT/计算机_专业资料。串口...mfc42.dll 拷到 Windows 目录下的 System 子目录中(win2000 为 System32) ...
VC编写串口调试助手(含VC6工程源文件)
VC编写串口调试助手(含VC6工程源文件)_计算机软件及应用_IT/计算机_专业资料。纯业余者用 VC(MFC)编写串口调试助手 1. 序 毕业到现在,转眼就做射频开发 10 年...
VS2010之MFC串口通信的编写教程_图文
VS2010之MFC串口通信的编写教程_计算机软件及应用_IT/计算机_专业资料。VS2010 ...调试我们写好的串口工具 我们下面用 51 单片机调试一下,结果也是成功的; m_...
VC编写串口程序
VC编写串口程序 - VC 控件 MSComm 编写串口通信程序(转) 博客分类: ? C++ VC++编程数据结构 MFCMicrosoft 在众多网友的支持下, 串口调试助手从 200...
VC上位机MFC利用串口控件发送接收数据
VC上位机MFC利用串口控件发送接收数据_计算机软件及应用_IT/计算机_专业资料。VC...将串口线接好后(不会接?去看看我写的串口接线基本 方法),打开串口调试助手,...
VS2010之MFC串口通信的编写教程
VS2010之MFC串口通信的编写教程_计算机软件及应用_IT/计算机_专业资料。VS2010 ...调试我们写好的串口工具 我们下面用 51 单片机调试一下,结果也是成功的; m_...
C++实习案例1 串口调试助手
C++实习案例1 串口调试助手_计算机软件及应用_IT/计算机_专业资料。1. 安装好 VS2010,建立“基于对话框”的 MFC 工程,命名为 ComTest,应用程序类型选 择“基于...
串口调试助手源程序及编程详细过程
串口调试助手源程序及编程详细过程作者:龚建伟 1.建立项目 建立项目:打开 VC++6.0,建立一个基于对话框的 MFC 应用程序 SCommTest(与我源 建立项目 代码一致,等会...
VC上位机MFC利用串口控件发送接收数据程序代码
VC上位机MFC利用串口控件发送接收数据程序代码_计算机软件及应用_IT/计算机_专业资料...函数就会执行,我们在 OnOnCommMscomm1函数加入相应的处理 代码就能实现自已想要...
VC++串口上位机简单例程(源码及详细步骤
程序的编写: 1、 打开 VC++6.0 建立基于对话框的 MFC 应用程序 Test, 2、...(false); } 首先, 在对话框中创建通信控件, Control 工具栏中缺少该控件, ...
更多相关标签: