C++MFC 串口通信 上位机

本节介绍

在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信。串口通信方便易行,应用广泛。

一般情况下,工控机和各智能仪表通过RS485总线进行通信。RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络.上的各智能控制单元子节点。每次通信都是由PC机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。

在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序单,但欠灵活。其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。本文我们只介绍API串口通信部分。

接下来我们来实现一个串口调试助手!

ADO访问数据库

➢1、 安装串口虚拟软件

➢2、 打开串口

➢3、设置串口属性(波特率、奇偶校验等)

➢4、 读写串口

➢5、 校验(求和校验、CRC校验 )

➢6、 通信协议

建立工程

项目是在visual studio2005编译器内创建,点击"文件"->"新建"->"项目"

按照图标提示选择,点击下一步。

单击下一步,下一步,完成。

点击完成后:

接下来开始构建项目:

编辑如图所示的窗口:

在WinDemoDlg.h中声明初始化函数

cpp 复制代码
public:
	void InitComboBox();

在WinDemoDlg.cpp中实现改函数:

cpp 复制代码
void CWinDemoDlg::InitComboBox(){
	CComboBox* pComboComm=(CComboBox*)GetDlgItem(IDC_COMBO_COMM);
	ASSERT(pComboComm);
	for(int i=1;i<=8;i++){
		CString strComm;
		strComm.Format(_T("COM%d"),i);
		pComboComm->AddString(strComm);
	}
	pComboComm->SetCurSel(0);

	//波特率
	CComboBox* pComboBaudrate=(CComboBox*)GetDlgItem(IDC_COMBO_BAUDRATE);
	ASSERT(pComboBaudrate);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("300")),300);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("600")),600);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("1200")),1200);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("2400")),2400);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("4800")),4800);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("9600")),9600);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("19200")),19200);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("43000")),43000);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("56000")),56000);
	pComboBaudrate->SetItemData(pComboBaudrate->AddString(_T("1152000")),1152000);
	pComboBaudrate->SetCurSel(5);
	
	//校验位
	CComboBox* pComboCheckBit=(CComboBox*)GetDlgItem(IDC_COMBO_CHECKBIT);
	ASSERT(pComboCheckBit);
	pComboCheckBit->SetItemData(pComboCheckBit->AddString(_T("无None")),NOPARITY);
	pComboCheckBit->SetItemData(pComboCheckBit->AddString(_T("奇ODD")),ODDPARITY);
	//pComboCheckBit->SetItemData(pComboCheckBit->AddString(_T("偶EUEN")),EUENPARITY);
	pComboCheckBit->SetCurSel(0);

	//数据位
	CComboBox* pComboDateBit=(CComboBox*)GetDlgItem(IDC_COMBO_DATABIT);
	ASSERT(pComboDateBit);
	pComboDateBit->SetItemData(pComboDateBit->AddString(_T("6")),6);
	pComboDateBit->SetItemData(pComboDateBit->AddString(_T("7")),7);
	pComboDateBit->SetItemData(pComboDateBit->AddString(_T("8")),8);
	pComboDateBit->SetCurSel(0);

	//停止位
	CComboBox* pComboStopBit=(CComboBox*)GetDlgItem(IDC_COMBO_STOPBIT);
	ASSERT(pComboStopBit);
	pComboStopBit->SetItemData(pComboStopBit->AddString(_T("1")),ONESTOPBIT);
	pComboStopBit->SetItemData(pComboStopBit->AddString(_T("2")),TWOSTOPBITS);
	pComboStopBit->SetCurSel(0);


}

并将初始化函数放入OnInitDialog()函数中:

添加一个串口类,这个串口类用来实现具体的串口通信:

点击"项目"->"添加类"

添加一般的C++类即可

将类名设置为:CSerialPort

其中CSerialPort.h代码如下:

cpp 复制代码
#pragma once

class CSerialPort
{
public:
	CSerialPort(void);
public:
	~CSerialPort(void);
public:
	BOOL OpenComm(CString strComm);
	BOOL SetCommState(DWORD dwBaudrate,BYTE byParity,BYTE byByteSize,BYTE byStopBits);
	BOOL SetupComm(DWORD dwInQueue,DWORD dwOutQueue);
	BOOL PurgeComm(DWORD dwFlags);
	BOOL SetCommMask(DWORD dwEvtMask);
	BOOL WriteFile(IN LPCVOID lpBuffer,IN DWORD nNumberOfBytesToWrite,OUT LPDWORD lpNumberOfBytesWritten,IN LPOVERLAPPED lpOverlapped);
	BOOL ReadFile(OUT LPVOID lpBuffer,IN DWORD nNumberOfBytesToRead,OUT LPDWORD lpNumberOfBytesRead,IN LPOVERLAPPED lpOverlapped);
	BOOL ClearCommError(OUT LPDWORD lpErrors,OUT LPCOMSTAT lpStat);
	BOOL GetOverlappedResult(IN LPOVERLAPPED lpoverlapped,OUT LPDWORD lpNumberOfByterTransferred,IN BOOL bWait);
	void CloseComm();	//关闭窗口

public:
	HANDLE m_hComm;

};

CSerialPort.cpp代码如下:

cpp 复制代码
#include "StdAfx.h"
#include "SerialPort.h"

CSerialPort::CSerialPort(void)
{
	m_hComm=NULL;
}

CSerialPort::~CSerialPort(void)
{
}
BOOL CSerialPort::OpenComm(CString strComm){
	if(NULL==m_hComm){
		m_hComm=CreateFile((TCHAR*)(LPCTSTR)strComm,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,0);
		if(INVALID_HANDLE_VALUE==m_hComm){
			int nError=GetLastError();
			m_hComm=NULL;
			return FALSE;
		}
		return TRUE;
	}
	return FALSE;

}
BOOL CSerialPort::SetCommState(DWORD dwBaudrate,BYTE byParity,BYTE byByteSize,BYTE byStopBits){
	if(NULL==m_hComm) return FALSE; 
	DCB dcb;
	BOOL bRet= ::GetCommState(m_hComm,&dcb);
	if(!bRet){
		if(m_hComm){

			//CloseHandle(m_hComm);
			m_hComm=NULL;
		}
		return FALSE;
	}
	dcb.BaudRate=dwBaudrate;
	dcb.ByteSize=byByteSize;
	dcb.Parity=byParity;
	dcb.StopBits=byStopBits;
	bRet=::SetCommState(m_hComm,&dcb);
	if(!bRet){
		if(m_hComm){

			CloseHandle(m_hComm);
			m_hComm=NULL;
		}
		return FALSE;
	}
	return TRUE;
}
BOOL CSerialPort::SetupComm(DWORD dwInQueue,DWORD dwOutQueue){
	if(NULL==m_hComm) return FALSE; 
	return ::SetupComm(m_hComm,dwInQueue,dwOutQueue);
}
BOOL CSerialPort::PurgeComm(DWORD dwFlags){
	if(NULL==m_hComm) return FALSE; 

	return ::PurgeComm(m_hComm,dwFlags);
}
BOOL CSerialPort::SetCommMask(DWORD dwEvtMask){
	if(NULL==m_hComm) return FALSE; 

	return ::SetCommMask(m_hComm,dwEvtMask);
}
BOOL CSerialPort::WriteFile(IN LPCVOID lpBuffer,IN DWORD nNumberOfBytesToWrite,OUT LPDWORD lpNumberOfBytesWritten,IN LPOVERLAPPED lpOverlappe)
{
	if(NULL==m_hComm) return FALSE; 

	return ::WriteFile(m_hComm,lpBuffer,nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlappe);
}
BOOL CSerialPort::ReadFile(OUT LPVOID lpBuffer,IN DWORD nNumberOfBytesToRead,OUT LPDWORD lpNumberOfBytesRead,IN LPOVERLAPPED lpOverlapped)
{
	if(NULL==m_hComm) return FALSE; 

	return ::ReadFile(m_hComm,lpBuffer,nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);
}
BOOL CSerialPort::ClearCommError(OUT LPDWORD lpErrors,OUT LPCOMSTAT lpStat)
{
	if(NULL==m_hComm) return FALSE; 

	return ::ClearCommError(m_hComm,lpErrors,lpStat);
}
BOOL CSerialPort::GetOverlappedResult(IN LPOVERLAPPED lpoverlapped,OUT LPDWORD lpNumberOfByterTransferred,IN BOOL bWait)
{
	if(NULL==m_hComm) return FALSE; 

	return ::GetOverlappedResult(m_hComm,lpoverlapped,lpNumberOfByterTransferred,bWait);
}
//关闭窗口
void CSerialPort::CloseComm(){
	if(m_hComm){
		CloseHandle(m_hComm);
		m_hComm=NULL;
	}
}	

这时就封装好了一个串口类,一旦串口开始工作的时候,需要一个线程,用这个线程来进行收发数据;线程运行时通过ReadFile()函数从串口中读出数据,如果有数据需要将数据放到接受框中显示出来。

下面创建一个线程,一个串口对应一个线程对象,创建C++类,类名为:CThread。

CThread.h代码:

cpp 复制代码
#pragma once

class CThread
{
public:
	CThread(void);
public:
	~CThread(void);
public:
	void Start();
	void Stop();
public:
	virtual void SetThreadData(DWORD dwParam);
	virtual DWORD GetThreadData();

public:
	virtual void run();
public:
	static DWORD ThreadProc(LPVOID pParam);
public:
	HANDLE m_hThread;
	bool m_bExit;
	DWORD m_dwParam;

};

CThread.cpp代码:

cpp 复制代码
#include "StdAfx.h"
#include "Thread.h"

CThread::CThread(void)
{

	m_bExit=FALSE;
	m_dwParam=0;
	m_hThread=NULL;
}

CThread::~CThread(void)
{
	if(!m_bExit){
		Stop();
	}
}
void CThread::Start()
{
	DWORD dwThreadID;	//获取的线程ID
	HANDLE hThread=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc,this,0,&dwThreadID);
	ASSERT(hThread);
	m_hThread=hThread;
}
DWORD CThread::ThreadProc(LPVOID pParam)
{
	CThread* pThis=(CThread*)pParam;
	ASSERT(pThis);

	while(!pThis->m_bExit)
	{
		pThis->run();
	}
	return TRUE;
}
void CThread::Stop()
{
	if(m_hThread)
	{
		m_bExit=true;
		::WaitForSingleObject(m_hThread,INFINITE);
		::CloseHandle(m_hThread);
		m_hThread=NULL;
	}
}
void CThread::run()
{
	Sleep(100);
}
void CThread::SetThreadData(DWORD dwParam)
{
	if(m_dwParam!=dwParam)
	{
		m_dwParam=dwParam;
	}
}
DWORD CThread::GetThreadData()
{
	return m_dwParam;
}

接下来需要从这个线程派生出一个基类,创建C++类,类名为:CThreadComm; CThreadComm用来处理串口数据的收发线程。

CThreadComm.h代码:

CThreadComm.cpp代码:

相关推荐
MARIN_shen35 分钟前
Marin说PCB之POC电路layout设计仿真案例---06
网络·单片机·嵌入式硬件·硬件工程·pcb工艺
Asa3191 小时前
STM32-按键扫描配置
stm32·单片机·嵌入式硬件
南城花随雪。1 小时前
单片机:实现驱动超声波(附带源码)
单片机·嵌入式硬件
嵌入式科普1 小时前
十三、从0开始卷出一个新项目之瑞萨RZN2L串口DMA接收不定长
c语言·stm32·瑞萨·e2studio·rzn2l
yutian060610 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程13 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉17 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名67717 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普17 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣17 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp