深入浅出串口编程(4)――基于控件的串口编程

简介:

深入浅出串口编程(4

――基于控件的串口编程

1. MSComm控件

Visual C++为我们提供了一种好用的ActiveX控件Microsoft Communications Control(MSComm)来支持应用程序对串口的访问,在应用程序中插入MSComm控件后就可以较为方便地实现对通过计算机串口收发数据。
要使用ActiveX控件MSComm,程序员必须将其添加入工程,其方法是:
1)单击主菜单project的子菜单Add To projectComponents and Controls选项;
2)在弹出的“Components and Controls Gallery”对话框中选择Registered ActiveX Controls文件夹中的“Microsoft Communications Control,version 6.0”选项,如下图:
单击其中的“Insert”按钮,MSComm控件就被增加到工程中了。与此同时,类CMSComm的相关文件mscomm.hmscomm.cpp也一并被加入ProjectHeader FilesSource Files中。当然,程序员可以自己修改文件名,如下图:
直接分析mscomm.h头文件就可以完备地获取这个控件的使用方法(主要是public类型的接口函数),下面我们摘取了头文件的主要代码并对其关键部分给出了注释:
#if !defined(AFX_MSCOMM_H__)
#define AFX_MSCOMM_H__
 
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Machine generated IDispatch wrapper class(es) created by Microsoft Visual C++
 
// NOTE: Do not modify the contents of this file.  If this class is regenerated by
//  Microsoft Visual C++, your modifications will be overwritten.
 
/////////////////////////////////////////////////////////////////////////////
// CMSComm wrapper class
 
class CMSComm : public CWnd
{
protected:
DECLARE_DYNCREATE(CMSComm)
public:
CLSID const& GetClsid()
{
        static CLSID const clsid
               = { 0x648a5600, 0x2c6e, 0x101b, { 0x82, 0xb6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14 } };
        return clsid;
}
virtual BOOL Create(LPCTSTR lpszClassName,
        LPCTSTR lpszWindowName, DWORD dwStyle,
        const RECT& rect,
        CWnd* pParentWnd, UINT nID,
        CCreateContext* pContext = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID); }
 
    BOOL Create(LPCTSTR lpszWindowName, DWORD dwStyle,
        const RECT& rect, CWnd* pParentWnd, UINT nID,
        CFile* pPersist = NULL, BOOL bStorage = FALSE,
        BSTR bstrLicKey = NULL)
{ return CreateControl(GetClsid(), lpszWindowName, dwStyle, rect, pParentWnd, nID,
        pPersist, bStorage, bstrLicKey); }
 
// Attributes
public:
 
// Operations
public:
void SetCDHolding(BOOL bNewValue);
BOOL GetCDHolding();
void SetCommID(long nNewValue);
long GetCommID();
void SetCommPort(short nNewValue);
// 设置端口号,如nNewValue =1表示COM1
short GetCommPort();
void SetCTSHolding(BOOL bNewValue);
BOOL GetCTSHolding();
void SetDSRHolding(BOOL bNewValue);
BOOL GetDSRHolding();
void SetDTREnable(BOOL bNewValue);
BOOL GetDTREnable();
void SetHandshaking(long nNewValue);
long GetHandshaking();
void SetInBufferSize(short nNewValue);
short GetInBufferSize();
void SetInBufferCount(short nNewValue);
short GetInBufferCount();
void SetBreak(BOOL bNewValue);
BOOL GetBreak();
void SetInputLen(short nNewValue);
short GetInputLen();
void SetNullDiscard(BOOL bNewValue);
BOOL GetNullDiscard();
void SetOutBufferSize(short nNewValue);
short GetOutBufferSize();
void SetOutBufferCount(short nNewValue);
short GetOutBufferCount();
void SetParityReplace(LPCTSTR lpszNewValue);
CString GetParityReplace();
void SetPortOpen(BOOL bNewValue);
// 打开或关闭串口,TRUE:打开,FALSE:关闭
BOOL GetPortOpen();
// 串口是否已打开,TRUE:打开,FALSE:关闭
void SetRThreshold(short nNewValue);
// 如果设置为1,表示一接收到字符就发送2号事件
short GetRThreshold();
void SetRTSEnable(BOOL bNewValue);
// 硬件握手使能?
BOOL GetRTSEnable();
void SetSettings(LPCTSTR lpszNewValue);
//Settings 4部分组成,其格式为:“BBBB,P,D,S”,即“波特率,是否奇偶校验,数据位  //个数,停止位”,如设置为:"9600,n,8,1"
CString GetSettings();
void SetSThreshold(short nNewValue);
// 如果保持缺省值0不变,则表示发送数据的过程中串口上不发生事件
short GetSThreshold();
void SetOutput(const VARIANT& newValue);
// 一个非常重要的函数,用于写串口,注意其接收的输入参数为VARIANT类型对象,
// 我们需要将字符串转化为VARIANT类型对象
VARIANT GetOutput();
void SetInput(const VARIANT& newValue);
VARIANT GetInput();
// 一个非常重要的函数,用于读串口,注意其返回的是VARIANT类型对象,我们需要
// 将其转化为字符串
void SetCommEvent(short nNewValue);
short GetCommEvent();
// 一个非常重要的函数,获得串口上刚发生的事件(“事件”可以理解为软件意义上的
// “消息”或硬件意义上的“中断”),事件的发送会导致OnComm消息的诞生!
void SetEOFEnable(BOOL bNewValue);
BOOL GetEOFEnable();
void SetInputMode(long nNewValue);
long GetInputMode();
};
 
//`AFX_INSERT_LOCATION`
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif
分析上述源代码可知,基本上,MSComm的诸多接口可以分为如下几类:
1)打开与设置串口接口函数;
2)获得串口设置和串口状态接口函数;
3)设置串口发送数据方式、缓冲区接口及发送数据接口函数;
4)设置串口接收数据方式、缓冲区接口及接收数据接口函数;
5)设置与获取串口上发生的事件接口函数。

2.例程

程序的功能和界面(如下图)都与本文连载三中《基于WIN32 API的串口编程》相同,不同的只是连载三的串口通信以API实现,而本节的串口通信则以MSComm控件实现。
使用第1节的方法将控件添加入工程并添加mscomm.hmscomm.cpp文件后,为了使用控件,我们将控件拖入对话框内任意一个位置(运行时“电话”图标会隐藏),其操作如下图:
有趣而极富人性化的是我们可以直接右键单击这个“电话”,来设置串口的属性,如下图:
接着,我们需要为控件添加一个对应的成员变量m_mscom,其对应的变量类型为CMSComm,如下图:
这样就建立了m_mscomIDC_MSCOMM1控件的相互映射:
void CSerialPortActivexDlg::DoDataExchange(CDataExchange* pDX)
{
       CDialog::DoDataExchange(pDX);
       //{{AFX_DATA_MAP(CSerialPortActivexDlg)
       DDX_Text(pDX, IDC_RECV_EDIT, m_recv);
       DDX_Text(pDX, IDC_SEND_EDIT, m_send);
       DDX_Control(pDX, IDC_MSCOMM1, m_mscom);
       //}}AFX_DATA_MAP
}
同时,在对话框的头文件也会由“MFC类向导”自动定义CSerialPortActivexDlg类的CMSComm型成员变量m_mscom
CMSComm    m_mscom;
在对话框初始化时(即在CSerialPortActivexDlg::OnInitDialog函数中)打开串口1
BOOL CSerialPortActivexDlg::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
       m_mscom.SetCommPort(1);  //串口1
       m_mscom.SetInBufferSize(1024); //设置输入缓冲区的大小,Bytes     
       m_mscom.SetOutBufferSize(512); //设置输入缓冲区的大小,Bytes     
       if(!m_mscom.GetPortOpen())    //打开串口
       {
              m_mscom.SetPortOpen(true);
       }
       m_mscom.SetInputMode(1); //设置输入方式为二进制方式
       m_mscom.SetSettings("9600,n,8,1"); //设置波特率等参数
       m_mscom.SetRThreshold(1); //1表示有一个字符即引发事件
       m_mscom.SetInputLen(0);
      
       return TRUE;  // return TRUE  unless you set the focus to a control
}
最核心的发送串口数据函数(“发送”按钮单击事件)如下:
void CSerialPortActivexDlg::OnSendButton()
{
       // TODO: Add your control notification handler code here
       UpdateData(true);
      
       CByteArray sendArr;    
       WORD wLength;
 
       wLength = m_send.GetLength();
       sendArr.SetSize(wLength);
       for(int i =0; i<wLength; i++)
       {
              sendArr.SetAt(i, m_send.GetAt(i));
       }
 
       m_mscom.SetOutput(COleVariant(sendArr));
}
为了处理接收事件,我们需要为MScomm控件添加对应的消息处理函数。如下图,我们通过“MFC类向导”添加了CSerialPortActivexDlg 类的成员函数OnCommMscomm1()
这样,在对话框的头文件中就会自动增加下面两句:
       afx_msg void OnCommMscomm1();//函数声明
       DECLARE_EVENTSINK_MAP()
来自AFX_MSG部分:
       // Generated message map functions
       //{{AFX_MSG(CSerialPortActivexDlg)
       virtual BOOL OnInitDialog();
       afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
       afx_msg void OnPaint();
       afx_msg HCURSOR OnQueryDragIcon();
       afx_msg void OnClearButton();
       afx_msg void OnSendButton();
       afx_msg void OnCommMscomm1();
       DECLARE_EVENTSINK_MAP()
       //}}AFX_MSG
同时在对话框的.cpp文件中会增加下列代码实现串口消息映射:
BEGIN_EVENTSINK_MAP(CSerialPortActivexDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CSerialPortActivexDlg)
       ON_EVENT(CSerialPortActivexDlg, IDC_MSCOMM1, 1 /* OnComm */,
       OnCommMscomm1, VTS_NONE)
       //}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
我们定义CSerialPortActivexDlg::OnCommMscomm1()函数主要处理数据的接收,其源代码为:
void CSerialPortActivexDlg::OnCommMscomm1()
{
       // TODO: Add your control notification handler code here
       UpdateData(true);
 
       VARIANT variant_inp;
       COleSafeArray safearray_inp;
      
       long i = 0;
       int len;
       char rxdata[1000];
       CString tmp;
       switch (m_mscom.GetCommEvent())
       {
    case 2:
              //表示接收缓冲区内有字符
              {
                     variant_inp = m_mscom.GetInput();
                     safearray_inp = variant_inp;
                     len = safearray_inp.GetOneDimSize();
                     for (i = 0; i < len; i++)
                     {
                            safearray_inp.GetElement(&i, &rxdata[i]);                       
                     }    
                     rxdata[i] = '\0';
              }
              m_recv += rxdata;
              UpdateData(false);
              break;
       default:
              break;
       }
}
最后,与连载三类似,再次借助“串口调试助手”以实例验证了本程序的正确性,如下图:
最后,需要特别提示的是:如果要在基于“文档/视图”的框架结构程序而非对话框程序中使用串口控件,我们不能轻松地使用“MFC类向导”,这时候必须手动地添加相关代码。
MainFrm.h头文件中加入:
afx_msg void OnCommMscomm();
DECLARE_EVENTSINK_MAP()
并定义CMSComm成员变量:
CMSComm m_ComPort;
MainFrm.cpp文件中添加
BEGIN_EVENTSINK_MAP(CMainFrame, CFrameWnd)
ON_EVENT(CMainFrame,ID_COMMCTRL,1,OnCommMscomm,VTS_NONE)
//映射ACTIVEX控件的事件
END_EVENTSINK_MAP()
MainFrm.cpp文件的OnCreate(LPCREATESTRUCT lpCreateStruct)函数中添加:
     ComPort.Create(NULL, WS_VISIBLE | WS_CHILD, CRect(0,0,0,0),
this, ID_COMMCTRL);
以创建CMSComm控件。
       此后,我们就可以在CMainFrame类的函数中使用串口控件对应的ComPort控件成员变量。





 本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120285,如需转载请自行联系原作者

相关文章
|
5月前
|
API 开发工具 C++
游戏编程之十五 DirectDraw 的基本知识
游戏编程之十五 DirectDraw 的基本知识
64 0
|
25天前
|
监控 Linux 定位技术
Linux应用开发基础知识——串口应用编程(十一)
Linux应用开发基础知识——串口应用编程(十一)
32 0
Linux应用开发基础知识——串口应用编程(十一)
|
3月前
|
人工智能 机器人 C#
Windows编程课设(C#)——基于WPF和.net的即时通讯系统(仿微信)
一款参考QQ、微信的即时通讯软件。采用CS结构,客户端基于.Net与WPF开发,服务端使用Java开发。
|
11月前
|
负载均衡 安全 网络协议
网络编程懒人入门(十五):外行也能读懂的网络硬件设备功能原理速成
本文是《网络编程懒人入门》系列文章的第15篇,本篇将继续以通俗易懂的文字,帮你无脑理解各种基础网络硬件设备的功能原理。
88 0
|
安全 Android开发
21天打卡Andoid学到的一些小知识-第十九二十二十一天
今天我们学习打卡的内容是:android 10.0 去掉未知来源弹窗 默认授予安装未知来源权限
60 0
|
数据采集 API C#
“Unity开发也能跟硬件联动”--快来看看吧(串口通信)
简单的串口通信工具,基于C#应用程序WinFrom实现 串口介绍 串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。(至于再详细,自己百度) 串口应用 工业领域使用较多,比如:数据采集,设备控制等等,好多都是用串口通信来实现!你要是细心的话,你会发现,目前家用国网智能电能表就具备RS485通信总线(串行总线的一种)与RS232可以相互转化(当然一般,非专业的谁也不会闲的蛋疼,趴电表上瞎看,最多也就看看走了多少度电)
|
索引
一起谈.NET技术,Silverlight+WCF 新手实例 象棋 主界面-棋谱-回放-结局(四十)
查看本系列其他相关文章请点击:Silverlight+WCF 新手实例象棋专题索引 在线演示地址:Silverlight+WCF 新手实例 象棋 在线演示 在Silverlight+WCF 新手实例 象棋 主界面-棋谱-回放(三十九)中,我们实现了用户的棋谱回放,在文章的下面,我们曾留下了两...
915 0