上位机MFC串口通讯视频教程与源代码
上位机MFC串口通讯视频教程与源代码
上位机MFC串口通讯视频教程与源代码
上位机MFC串口通讯视频教程与源代码
上位机MFC串口通讯视频教程与源代码
01 作者介绍:
陈贵发
Grief Chen
机电一体化专业,07年毕业后一直从事自动化行业。
刚毕业从自动化设备维修,组装,调试,到接触器系统设计,
PLC程序,C++上位机程序系统设计,再到视觉程序设计,一步步走来。
大大小小项目设计无数。也深知从事工控的心酸与不易。
当今社会牛人都创业,人人都分享,作者也录制与分享工控视频与项目源代码程序,
发布于 工控编程吧,让大家在工控学习上少走弯路,项目开发上缩短时间。
02 教程介绍:
串口通讯在工业领域中是极为常用的通讯方式,不管是PLC,触摸屏还是其他智能仪表,
都支持串口通讯控制。教程中的使用的串口集成类适合RS232串口,RS485并口,并在众多项目中稳定
运行。部分项目源代码已发布于论坛:
三菱电阻测量仪MCP T700通讯源代码与视频讲解
三菱PLC串口通讯方法源代码与手册
三菱电阻测量仪MCP T700通讯源代码与视频讲解
三菱电阻测量仪SM7110 SM7120项目源代码
honeywell霍尼韦尔扫描枪3310G串口通讯源代码与驱动手册配置工具
教程结合串口类在一简单项目中的使用(扫描枪串口控制扫码),一步步讲解串口通讯的流程与
具体操作,还分析关键代码段,使您知其然与所以然,在以后自己项目中使用此集成类畅通无阻。
视频教程附有源代码,可在项目中直接使用,视频拟分10个部分:
0.串口类创建并添加串口参数等全局数据类型
1.串口类成员变量添加与其初始化函数的实现
2.串口类事件监控函数的实现
3.串口类事件处理与日志生成函数的实现
4.串口类串口参数设置与串口开关函数的实现
5.串口类数据收发与串口复位函数的实现
6.串口类的使用之连接断开下位机
7.串口类的使用之数据收发与出错处理
8.串口类的使用之串口设置界面编写
9.额外功能之获取当前系统全部串口号
03 适合谁看:
对串口RS232或并口RS485感兴趣的朋友。
自己项目需要编写代码通过RS232或RS485控制的朋友。
有一定C++基础的朋友。刚入门可学习我录制的三套教程快速上手上位机编程
上位机编程语言基础篇-使用C++
上位机编程新手上路篇-编写程序
上位机VC MFC程序开发精典实例大全源码与视频讲解配套下载408例
04 你将获得:
了解RS232和RS485 串口并口通讯控制的过程
RS232和RS485串口并口通讯控制通用的集成类与如何使用
拥有开发RS232和RS485串口并口通讯控制程序的能力
05 您将投资:
观看没视频的时间约1小时。
代码编程实践的时间约2小时
视频与集成类的投资费用100元
微信上与作者在编程技术上的互动
源码附件
源码加视频加课件附件
如果您认可,可联系功能定制! 如果您着急,充值会员可直接联系发您资料!
上位机MFC串口通讯视频教程与源代码
0.串口类创建并添加串口参数等全局数据类型
关键讲解
- <font color="#000000">//0步
- #define WM_COMM_BREAK WM_USER+1// A break was detected on input.
- #define WM_COMM_CTS WM_USER+2// The CTS (clear-to-send) signal changed state.
- #define WM_COMM_DSR WM_USER+3// The DSR (data-set-ready) signal changed state.
- #define WM_COMM_ERR WM_USER+4// A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
- #define WM_COMM_RING WM_USER+5// A ring indicator was detected.
- #define WM_COMM_RLSD WM_USER+6// The RLSD (receive-line-signal-detect) signal changed state.
- #define WM_COMM_RXCHARWM_USER+7// A String was received and placed in the input buffer.
- #define WM_COMM_RXFLAG WM_USER+8// The event character was received and placed in the input buffer.
- #define WM_COMM_TXEMPTY WM_USER+9 // The last character in the output buffer was sent
- #define MAXSENDTIMES 50 //Repeat sending times when failed
- #define INTERVAL 10 //Internal waiting time when failed
- struct COM_PARA
- {
- UINT nPortIndex;
- UINT nBaud;
- CHAR cParity;
- UINT nDataBits
- UINT nStopBits
- DWORD dwComEvent};
- </font>
复制代码微软底层串口通讯 函数运行时会触发众多事件,宏WM_USER+1到WM_USER+9用于在触发对应事件时发送出消息。例程是通过虚拟函数来处理对应事件,在子类中要处理什么事件重写函数就好,所以这些宏仅作备用。 宏 MAXSENDTIMES 与INTERVAL用于处理串口通讯异常时重新建立连接。分别表示重连的最大次数与重连的间隔时间。我们都知道工业环境复杂,干扰众多,添加重连功能可以增加我们程序的鲁棒性。 结构体COM_PARA用于处理串口通讯的参数设置,其成员变量与串口参数串口号,波特率,奇偶校验位,数据位个数,停止位,掩码一一对应。
1.串口类成员变量添加与其初始化函数的实现 关键讲解 - <font color="#000000">//1步
- protected:
- CRITICAL_SECTION m_CS
- HANDLE m_hWriteEvent;
- HANDLE m_hComStop;
- OVERLAPPED m_OverLapped;
- OVERLAPPED m_WOverLapped;
- OVERLAPPED m_ROverLapped;
- CWinThread* m_pComThread;
- HANDLE m_hEventArray[2];
- HANDLE m_hPort;
- COM_PARA m_ComPara;
- CString m_sSendData;
- CString m_sLogPath;
- void ComInit();
- void Lock();
- void Unlock();
- </font>
复制代码这些成员变量都为集成类内部函数作用,起衔接数据作用,其具体的作用可以看源代码注释。 这些变量通过函数ComInit进行初始化, ComInit在类的构造函数中进行调用,也就是说,每构造一个类的对象时会自动调用此函数初始化变量。 工程项目中一般都是多线程设计,多线程程序中最大的忌讳就是多线程同时访问修改同一变量时,会使其结果未知。 所以集成类中加入Lock(); Unlock();两函数通过临界区变量避免这情况出现。此两函数需要成对出现。
2.串口类事件监控函数的实现
关键讲解
- <font color="#000000">UINT CComPort::ComThreadFunc(LPVOID pParam)
- {
- CComPort *pCom = (CComPort*) pParam;
- bool bFucResult = FALSE;
- DWORD Event = 0;
- DWORD dwError = 0;
- DWORD CommEvent = 0;
- COMSTAT comstat;
- while(true)
- {
- pCom->ComDetectStop();
- bFucResult = WaitCommEvent(pCom->m_hPort, &Event, &pCom->m_OverLapped)?true:false;//侦ì测a串?口ú事?件t;
- if (!bFucResult)
- {
- switch (dwError = GetLastError())
- {
- case ERROR_IO_PENDING: //normal return:no bytes to read
- break;
- case 87: //also valid reply:do nothing
- break;
- default: //serious error occured.
- {
- pCom->PrintErrors(_T( "Communication failed to connect!") );
- pCom->Comm_Failed();
- return 0;
- }
- }
- }
- </font>
复制代码- <font color="#000000">else
- {
- ClearCommError(pCom->m_hPort, &dwError, &comstat);
- if (comstat.cbInQue == 0)
- continue;
- }
- //><
- Event = WaitForMultipleObjects(2, pCom->m_hEventArray, FALSE, 10);
- switch(Event)
- {
- case 0:// read event
- {
- GetCommMask(pCom->m_hPort, &CommEvent);
- if (CommEvent & EV_CTS)
- pCom->Comm_CTS();
- if (CommEvent & EV_RXFLAG)
- pCom->Comm_RXFLAG();
- if (CommEvent & EV_BREAK)
- pCom->Comm_Break();
- if (CommEvent & EV_ERR)
- pCom->Comm_ERR();
- if (CommEvent & EV_RING)
- pCom->Comm_Ring();
- if (CommEvent & WM_COMM_DSR)
- pCom->Comm_DSR();
- if (CommEvent & WM_COMM_RLSD)
- pCom->Comm_RLSD();
- if (CommEvent & WM_COMM_TXEMPTY)
- pCom->Comm_TXEMPTY();
- if (CommEvent & EV_RXCHAR)
- pCom->ComReadData();
- }
- </font>
复制代码- <font color="#000000">break;
- case 1: // write event
- pCom->ComSendDataExe();
- break;
- }
- }
- //><
- return 0;
- }
- void CComPort::ComDetectStop()
- {
- DWORD dwResult = WaitForSingleObject(m_hComStop,0);
- if(dwResult==WAIT_OBJECT_0 || !m_hPort)
- {
- AfxEndThread(-1);
- }
- }
- </font>
复制代码ComThreadFunc是一个线程函数,由打开串口的函数ComOpen调用,实时运行监控串口,带有一个参数,例程中传递的是类的指针CComPort *pCom = (CComPort*) pParam;可在此线程函数中访问CComPort类数据。函数体内进入死循环监控数据,通过ComDetectStop()判断是否要退出死循环。 列循环内部调用函数WaitCommEvent(函数立即返回)侦测COM事件,正常返回ERROR_IO_PENDING往下执行,异常退出串口通讯。 紧接着调用函数WaitForMultipleObjects处理串口事件,串口读数据由ComReadData()完成,写数据由ComSendDataExe()完成。
3.串口类事件处理与日志生成函数的实现
关键讲解
- <font color="#000000">//3步
- protected://串口事件处理
- virtual void Comm_Break();
- virtual void Comm_CTS();
- virtual void Comm_DSR();
- virtual void Comm_ERR();
- virtual void Comm_Ring();
- virtual void Comm_RLSD();
- virtual void Comm_RXFLAG();
- virtual void Comm_TXEMPTY();
- virtual void Comm_RXCHAR(char c=0);
- virtual void Comm_Failed();
- public:
- CComPort();
- virtual ~CComPort();
- virtual void PrintErrors(CString sError);
- </font>
复制代码串口事件处理函数都在监控函数中调用,可以选择性的在Ccomport子类的继承覆盖使用,一般会使用函数Comm_RXCHAR(char c=0)串口中接收一字节时会触发此函数。 另外Comm_Failed()也会继承用于处理串口异常事件; 串口运行时都会发生各种情况,通过函数PrintErrors可以将这些情况记录下来。函数体内通过宏_DEBUG判断当前程序是调试版本则弹出事件对话框,是发布版本则向磁盘写入事件。
4.串口类串口参数设置与串口开关函数的实现关键讲解
- <font color="#000000">//4步
- protected:
- bool ComSetProtocol(COM_PARA *pComPara=NULL
- bool ComOpen();
- void ComClose
- </font>
复制代码串口在使用前,控制与受控端的串口通讯参数必需保持一致才能正常通讯。 串口通讯参数常要设置的有串口号,波特率,数据位,奇偶校验位,停止位等待。 Ccomport类通过函数ComSetProtocol设置串口参数,然后通过函数ComOpen打开串口进行通讯。 函数函数ComOpen在设置了串口一系列参数后,开启一线程,调用线程函数ComThreadFunc实时监测串口。
ComClose()用于关闭串口,退出线程函数,在析构函数中调用了一次以保证实例对象释放时自动关闭串口,释放串口资源。
ComOpen函数体内有几点要注意的是 1.调用函数 CreateFile打开串口时,串口号格式为\\.\COM,以保证也可正常打开9以上的串口号。 2.调用函数CreateFile 传递参数FILE_FLAG_OVERLAPPED,实现串口异步通讯。
5.串口类数据收发与串口复位函数的实现 关键讲解 - <font color="#000000">//5步
- void ComReadData();
- bool ComSendData(CString sData);
- void ComSendDataExe();
- bool ComReset();
- </font>
复制代码在串口成功打开后,最重要的功能也就是1.向串口发送数据。2.从串口读取数据。从串口读取数据前面已介绍,只要重载函数Comm_RXCHAR(char c=0)进行数据累加就好。其具体是在ComReadData()内被调用。 数据的发送是通过ComSendData(CString sData);实现。参数sData为要发送的数据。数据发送的具体实现是通过ComSendDataExe完成。此函数执行失败会重复发送几次,达到最大次数MAXSENDTIMES,串口关闭。 函数ComReset会将串口进行复位,停止数据收发,清空缓存区数据。一般在串口初始时或异常后调用。
6.串口类的使用之连接断开下位机 关键讲解 BOOL ConnectMeter(); //CONNECT METER BOOL DisconnectMeter(BOOLbNoticeFail=FALSE);//DISCONNECTMETER
一般串口都是连接智能仪表进行通讯。打开串口后会向仪表发送指令,仪表作出回应后串口才算成功连接。在具体通讯时应该根据仪表协议具体编写代码。这里通过与霍尼威尔310G扫描枪通讯协议为例进行串口类的使用。
ConnectMeter只打开事先设置的串口, DisconnectMeter进行串口关闭,函数 bNoticeFail 表示是否要调用Comm_Failed
7.串口类的使用之数据收发与出错处理
关键讲解
CString m_sCodeReceived bool m_bReceived;
BOOL GetBarcode(CString&sCode,short nTimeout=3000); BOOL GetBarcodeStop();//LETSCANNER STOP SCANNING CString GetBarcodeLast();//getlast barcode;
void Comm_RXCHAR(charc=0) void Comm_Failed();
在串口监测到一个字符时都会调用函数Comm_RXCHAR,在此函数中通过m_sCodeReceived进行字符的累加,边累加边提取数据。 在完整接收一帧数据后m_bReceived变量设为TRUE。
函数GetBarcode实现通过串口向扫描枪发送扫描指令sCode,枪超时nTimeout没回应则失败。 函数GetBarcodeStop实现通过串口向扫描枪发送停止扫描指令。 函数GetBarcodeLast获取上次扫描的结果。 向扫描枪发送的数据是通过其通讯手册查询0X16,0X54,0X0D为扫描指令0X16,0X55,0X0D为停止扫描指令.
8.串口类的使用之串口设置界面编写 关键讲解 #include "C3310G.h“ C3310G m_3310g; void LoadAndSetComPara();
在编写的串口类后,就得设计 界面实现人机交互。首先是包含类对应的头文件#include"C3310G.h“ ,其次是在界面文件中定义类的实例C3310Gm_3310g; 例程还添加了函数LoadAndSetComPara();实现串口参数的加载。在对话框函数OnInitDialog中进行了调用与打开串口。 LoadAndSetComPara(); if(!m_3310g.ConnectMeter()) MessageBox("failedto connect!"); 然后是在界面部分调用m_3310g的成员函数实现串口通讯 实现按钮功能<从扫描枪获取条码><串口设置>.串口设置额外新建的对话框CComSet实现
9.额外功能之获取当前系统全部串口号 关键讲解 - <font color="#000000">void CComPort::GetAllComPort(CStringArray &sPortArray)
- {
- HKEY hKey=NULL;
- if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"),0,KEY_READ,&hKey)!=ERROR_SUCCESS)
- {
- return ;
- }
- CString valuename,databuffer;
- DWORD valuenamebufferlength=200,valuetype,databuddersize=200;
- int i=0;
- while(RegEnumValue(hKey,i++,valuename.GetBuffer(200),&valuenamebufferlength,NULL,&valuetype,(BYTE*)databuffer.GetBuffer(200),&databuddersize) != ERROR_NO_MORE_ITEMS)
- {
- sPortArray.Add(CString(databuffer));
- databuddersize=200;
- valuenamebufferlength=200;
- }
- RegCloseKey(hKey);
- }
- </font>
复制代码在实际串口程序编写时,还会检测当前系统存在的全部串口号,而不是事先定义几个串口号供用户选择,这样当用户使用的串口号不在我们事先定义范围时,程序就无法满足要求。 获取串口号的方法有多种,当前例程通过注册表实现。其他方法可以在工控编程吧搜索关键字获取有效串口查看
|