码迷,mamicode.com
首页 > Windows程序 > 详细

VC基于API封装串口类(只有一个头文件)

时间:2015-06-03 11:51:51      阅读:293      评论:0      收藏:0      [点我收藏+]

标签:串口类   mfc   同步   modbus协议   

        因工作的需要,采用了基于VC开发项目,因需要用到串口,这里面没用到windows的MSCOMM空间和CSerialPot的类,而是专门利用windows api函数的同步机制来封装此类,类的接口模式有点模仿QT的Win_QextSerialPort。本库可以直接用在MFC上,当然也可以移植到QT上面。

#pragma once
#include<Windows.h>
#include <math.h>
#define MAX_RECV_BUFFER		1024
#define MAX_SEND_BUFFER		1024
typedef struct 
{
	DWORD baudRate;
	BYTE parity;
	BYTE stopBits;
	BYTE bytesize;
}PortSettings;
typedef void (*SerialPortCallBack)(char *, DWORD);

class SerialPortClass {
private:
	HANDLE m_hCom;
	PortSettings portSet;
	CString portName;
	SerialPortCallBack recvCb;
	HANDLE rThread;
	char recvBuffer[MAX_RECV_BUFFER];
	bool recvRun;
private:
	void SetDefaultPortSettings(PortSettings &port)
	{
		port.baudRate = 115200;
		port.parity = 0;
		port.stopBits = 0;
		port.bytesize = 8;
	}
public:
	SerialPortClass()
	{
		portName = _T("");
		SetDefaultPortSettings(portSet);
		m_hCom = INVALID_HANDLE_VALUE;
	}

	SerialPortClass(const CString &name)
	{
		portName = name;
		SetDefaultPortSettings(portSet);
		m_hCom = INVALID_HANDLE_VALUE;
	}

	SerialPortClass(PortSettings &port)
	{
		portName = _T("");
		portSet = port;
		m_hCom = INVALID_HANDLE_VALUE;
	}

	SerialPortClass(const CString &name, PortSettings &port)
	{
		portName = name;
		portSet = port;
		m_hCom = INVALID_HANDLE_VALUE;
	}
	~SerialPortClass()
	{
		if (m_hCom != INVALID_HANDLE_VALUE)
		{
			CloseHandle(m_hCom);
		}
		recvRun = false;
	}
	
	void SetPortName(const CString &name)
	{
		portName = name;
	}

	bool IsOpen()
	{
		if (m_hCom == INVALID_HANDLE_VALUE)
		{
			return false;			
		}
		return true;
	}

	bool SetPortSetttings(PortSettings &port)
	{
		DCB dcb;
		if (m_hCom == INVALID_HANDLE_VALUE)	//如果没有打开,直接返回,设置也没用
		{
			return false;
		}
		double tmp = 1 + port.bytesize;		
		tmp += port.bytesize;	
		if (port.parity > 0)				//0:无校验 1:奇 2:偶 3:标记校验
		{
			tmp += 1;
		}
		if (port.stopBits == 0)				//0:1个停止位 1:1.5个停止位 2:2两个停止位
		{
			tmp += 1;
		}
		else if (port.stopBits == 1)
		{
			tmp += 1.5;
		}
		else
		{
			tmp += 2;
		}
		GetCommState(m_hCom, &dcb);
		dcb.BaudRate = port.baudRate;
		dcb.Parity = port.parity;
		dcb.StopBits = port.stopBits;
		dcb.ByteSize = port.bytesize;
		if (!SetCommState(m_hCom, &dcb))
		{
			return false;
		}
		SetupComm(m_hCom, MAX_RECV_BUFFER, MAX_SEND_BUFFER);			//设置缓冲区大小
		PurgeComm(m_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
		COMMTIMEOUTS timeouts;				
		//这10000本来的数字是3500,这个是数字是根据modbus协议超时机制来设置的也就是两个字符之间时间间隔
		//为3.5个字符,但测试效果不佳,原始是这个时间间隔太短,取整的话也是1ms,相当于如果两个字符时间
		//如果接收的一个字符,过了1ms还没有收到字符,这回调函数直接响应,确实时间间隔太小,不过没关系,
		//我们自己私定以下,字符间隔时间设置为10,这相当于是这里的10000了,反复效果很理想,ReadIntervalTimeout
		//这个变量是其实和ReadFile有关系的,如果该变量设置为0,就是一直等待接收到指定数量的字符才返回,
		//本例子只针对于串口同步机制,想了解更清楚,还是要看具体的api函数。这个地方也是难点和重点
		//如果下位机发送部分写的不好的话,这时间间隔还可以设置大一点,我串口发送采用的是中断机制,还算可以
		tmp = (tmp * (double)10000) / (double)(port.baudRate);//超时,有点像modbus协议
		timeouts.ReadIntervalTimeout = (DWORD)ceil(tmp);
		timeouts.ReadTotalTimeoutMultiplier = 0;
		timeouts.ReadTotalTimeoutConstant = 0;
		timeouts.WriteTotalTimeoutMultiplier = 0;
		timeouts.WriteTotalTimeoutConstant = 0;
		if(!SetCommTimeouts(m_hCom, &timeouts))
		{
			return false;
		}
		return true;
	}
	
	bool Open(SerialPortCallBack cb, CString name = _T(""))
	{
		if (m_hCom != INVALID_HANDLE_VALUE)				//如果已经打开,则直接返回
		{
			return false;
		}
		if (name != _T(""))
		{
			portName = name;
		}
		if (_ttoi(portName.Mid(1, portName.GetLength() - 3)) > 9)
		{
			portName = _T("\\\\.\\") +  portName;
		}
		m_hCom = CreateFile(portName,                   //端口号  
							GENERIC_READ|GENERIC_WRITE, //权限可读写  
							0,  
							NULL,  
							OPEN_EXISTING,              //打开存在的端口  
							NULL,                       //以同步方式打开  
							NULL);
		if (!SetPortSetttings(portSet))					//设置波特率等失败
		{
			CloseHandle(m_hCom);						//但是此时串口是打开的,因此关闭
			m_hCom = INVALID_HANDLE_VALUE;
			return false;
		}
		recvCb = cb;									//保存回调函数指针,当有数据来临时,方便调用
		rThread = CreateThread(NULL, 0, CommRecvThread, this, 0, NULL);	//新建监听线程
		CloseHandle(rThread);
		recvRun = true;
		return true;
	}

	void Close()
	{
		if (m_hCom != INVALID_HANDLE_VALUE)
		{
			CloseHandle(m_hCom);
			recvRun = false;		//停止接收线程
		}
	}

	DWORD Write(char *str, DWORD len)
	{
		DWORD dwBytesWrite;
		if (m_hCom == INVALID_HANDLE_VALUE)
		{
			return false;
		}
		WriteFile(m_hCom, str, len, &dwBytesWrite, NULL);
		PurgeComm(m_hCom, PURGE_TXCLEAR);
		return dwBytesWrite;
	}

	static DWORD WINAPI CommRecvThread(LPVOID lpParameter)
	{
		SerialPortClass *p = (SerialPortClass *)lpParameter;
		p->RecvThread();
		return 0;
	}
	void RecvThread()
	{
		DWORD dwBytesRead, dwErrorFlags; 
		COMSTAT comStat;
		while (1)
		{
			ClearCommError(m_hCom, &dwErrorFlags, &comStat);
			if (comStat.cbInQue != 0)		//缓冲区的个数
			{
				if (ReadFile(m_hCom, recvBuffer, MAX_RECV_BUFFER, &dwBytesRead, NULL))
				{
					recvCb(recvBuffer, dwBytesRead);		//响应回调函数
					PurgeComm(m_hCom, PURGE_RXCLEAR);		//清缓冲区
				}
			}
		}
	}
};

 

        使用步骤如下:

        1、在工作的头文件中引用头文件申明, #include "SerialPortClass.h"

        2、定义类对象指针SerialPortClass *pSerialPortCls;

        3、写一个接收函数,当串口初始化并且打开后,该函数会只想响应的。

        4、贴出使用代码,哈哈,本例程后续本人还将维护,有问题的可以直接留言给我。

void RecvHwb(char *str, DWORD l)
{
	for (int i = 0; i < l; i++)
	{
		_cprintf("%d = %02x\r\n", i, str[i]);
	}
}

void CControlCardDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码
	char t[] = {0x00, 0x01, 0x00, 0x70, 0x50};
	PortSettings settings = {115200, 0, 0, 8};
	pSerialPortCls = new SerialPortClass();
	pSerialPortCls->SetPortName(L"COM4");
	pSerialPortCls->SetPortSetttings(settings);
	if (pSerialPortCls->Open(RecvHwb))
	{
		DWORD writed = pSerialPortCls->Write(t, sizeof(t));
		_cprintf("write byte = %d\r\n", writed);
	}
}

技术分享

 



VC基于API封装串口类(只有一个头文件)

标签:串口类   mfc   同步   modbus协议   

原文地址:http://blog.csdn.net/hwb_1988/article/details/46342423

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!