C++MFC 串口通信 上位机

本节介绍

        在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信。串口通信方便易行,应用广泛。
        一般情况下,工控机和各智能仪表通过RS485总线进行通信。RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络.上的各智能控制单元子节点。每次通信都是由PC机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。
        在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序单,但欠灵活。其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。本文我们只介绍API串口通信部分。
        接下来我们来实现一个串口调试助手!

ADO访问数据库
        ➢1、 安装串口虚拟软件
        ➢2、 打开串口
        ➢3、设置串口属性(波特率、奇偶校验等)
        ➢4、 读写串口
        ➢5、 校验(求和校验、CRC校验 )
        ➢6、 通信协议

建立工程

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

C++MFC 串口通信 上位机_第1张图片

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

C++MFC 串口通信 上位机_第2张图片

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

C++MFC 串口通信 上位机_第3张图片

C++MFC 串口通信 上位机_第4张图片

 点击完成后:

C++MFC 串口通信 上位机_第5张图片

 接下来开始构建项目:

编辑如图所示的窗口:

C++MFC 串口通信 上位机_第6张图片

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

public:
	void InitComboBox();

 在WinDemoDlg.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++MFC 串口通信 上位机_第7张图片

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

        点击“项目”->“添加类”

C++MFC 串口通信 上位机_第8张图片

         添加一般的C++类即可

C++MFC 串口通信 上位机_第9张图片

         将类名设置为:CSerialPort

C++MFC 串口通信 上位机_第10张图片

 其中CSerialPort.h代码如下:

#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代码如下:

#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。

C++MFC 串口通信 上位机_第11张图片

 CThread.h代码:

#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代码:

#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用来处理串口数据的收发线程。

C++MFC 串口通信 上位机_第12张图片

CThreadComm.h代码:

CThreadComm.cpp代码:

你可能感兴趣的:(stm32,单片机,嵌入式硬件)