工控编程吧
标题:
上位机MFC串口通讯视频教程与源代码
[打印本页]
作者:
qq263946146
时间:
2018-12-22 17:45
标题:
上位机MFC串口通讯视频教程与源代码
上位机MFC串口通讯视频教程与源代码
(, 下载次数: 9)
上传
点击文件名下载附件
(, 下载次数: 4)
上传
点击文件名下载附件
(, 下载次数: 4)
上传
点击文件名下载附件
(, 下载次数: 9)
上传
点击文件名下载附件
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元
微信上与作者在编程技术上的互动
源码附件
(, 下载次数: 2)
上传
点击文件名下载附件
(, 下载次数: 10)
上传
点击文件名下载附件
源码加视频加课件附件
(, 下载次数: 16)
上传
点击文件名下载附件
[note]1[/note]
[weixinlianxi]1[/weixinlianxi]
(, 下载次数: 5)
上传
点击文件名下载附件
0.串口类创建并添加串口参数等全局数据类型
[iqiyi]//player.video.iqiyi.com/9caddb719e69d1d935985a218e67e3bd/0/0/w_19s3qexbd5.swf-albumId=29835999909-tvId=29835999909-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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.串口类成员变量添加与其初始化函数的实现
[iqiyi]//player.video.iqiyi.com/7ab2ad5656f24787614265ad74c6fcda/0/0/w_19s3qe2sud.swf-albumId=29836690409-tvId=29836690409-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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.串口类事件监控函数的实现
[iqiyi]//player.video.iqiyi.com/1dbbb10b4ff626ad86a62046d7362368/0/0/w_19s3qe3ip5.swf-albumId=29836591109-tvId=29836591109-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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.串口类事件处理与日志生成函数的实现
[iqiyi]//player.video.iqiyi.com/525ba991ab6c97037ebc5eea355d39dd/0/0/w_19s3qe4udh.swf-albumId=29836804009-tvId=29836804009-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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.串口类串口参数设置与串口开关函数的实现
关键讲解
[iqiyi]//player.video.iqiyi.com/fa241feac79616714f99549df3db10f0/0/0/w_19s3qeabap.swf-albumId=29836876509-tvId=29836876509-isPurchase=0-cnId=undefined[/iqiyi]
<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.串口类数据收发与串口复位函数的实现
[iqiyi]//player.video.iqiyi.com/54b1f8ad886d822c64b4561e5275eb6b/0/0/w_19s3qe2rah.swf-albumId=29836704709-tvId=29836704709-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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.串口类的使用之连接断开下位机
[iqiyi]//player.video.iqiyi.com/b34df73e6d74557304386150e0b0a60b/0/0/w_19s3qe9nul.swf-albumId=29836894609-tvId=29836894609-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
BOOL ConnectMeter(); //CONNECT METER
BOOL DisconnectMeter(BOOLbNoticeFail=FALSE);//DISCONNECTMETER
一般串口都是连接智能仪表进行通讯。打开串口后会向仪表发送指令,仪表作出回应后串口才算成功连接。在具体通讯时应该根据仪表协议具体编写代码。这里通过与霍尼威尔310G扫描枪通讯协议为例进行串口类的使用。
ConnectMeter只打开事先设置的串口, DisconnectMeter进行串口关闭,函数 bNoticeFail 表示是否要调用Comm_Failed
7.串口类的使用之数据收发与出错处理
[iqiyi]//player.video.iqiyi.com/91582a1b96e16db2fc99db4ff50a4cdc/0/0/w_19s3qeapz1.swf-albumId=29837041809-tvId=29837041809-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
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.串口类的使用之串口设置界面编写
[iqiyi]//player.video.iqiyi.com/e931053b574b87447ddf95113e49b6f5/0/0/w_19s3qe5s4p.swf-albumId=29836813509-tvId=29836813509-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
#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.额外功能之获取当前系统全部串口号
[iqiyi]//player.video.iqiyi.com/e9031390503a7244598652b9eac835fc/0/0/w_19s3qe88yh.swf-albumId=29836960309-tvId=29836960309-isPurchase=0-cnId=undefined[/iqiyi]
关键讲解
<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>
复制代码
在实际串口程序编写时,还会检测当前系统存在的全部串口号,而不是事先定义几个串口号供用户选择,这样当用户使用的串口号不在我们事先定义范围时,程序就无法满足要求。
获取串口号的方法有多种,当前例程通过注册表实现。其他方法可以在工控编程吧搜索关键字
获取有效串口查
看
欢迎光临 工控编程吧 (https://www.gkbc8.com/)
Powered by Discuz! X3.4