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

因工作的需要,采用了基于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);
	}
}

时间: 2024-10-21 19:17:32

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

串口类QextSerialPort

QextSerialPort类是基于Qt程序串口类,在win和linux都适用,win下可以使用EventDriven,linux好像不行. 整个类的层次关系. 我下载的是qextserialport-1.2win-alpha这个版本,不过编译时有点问题,Posix下的open函数setTimeout(Settings.Timeout_Sec, Settings.Timeout_Millisec); 第一个参数应该去掉,现在Setting结构体里没有这个参数. 整个类可以编译成动态库. 如果当前

文件系统(02):基于SpringBoot框架,管理Xml和CSV文件类型

本文源码:GitHub·点这里 || GitEE·点这里 一.文档类型简介 1.XML文档 XML是可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言.标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如数据结构,格式等.它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.适合网络传输,提供统一的方法来描述和交换应用程序的结构化数据. 2.CSV文档 CSV文档,以逗号分隔文档内容值,其文件以纯文本形式存储结构数据.CSV文

一个由印度人编写的VC串口类

软件介绍 一个由印度人编写的VC串口类(也是一种VC串口控件),他还配合这个类写了VC 串口通信方面的一些基础知识,如怎么用VC打开串口,如何对串口进行配置,读串口.写串口等. 这个类有点特别,它没有使用事件驱动原理,它是以查询方式工作的. 简介: 对没有接触过串口通信的VC程序员来说显得非常困难,很久以前我在 codeguru.com 上搜索过串口通信相关信息得到了非常大的帮助,从那时起能编写一个简单易用的VC 串口类是我的梦想. 经过七个月在串口通信编程方面实践经验后,我编写了一个基于API

一个印度人写的VC串口类CSerialCom(有串口基础介绍)

一个由印度人编写的VC串口类(也是一种VC串口控件),他还配合这个类写了VC 串口通信方面的一些基础知识,如怎么用VC打开串口,如何对串口进行配置,读串口.写串口等. 这个类有点特别,它没有使用事件驱动原理,它是以查询方式工作的. 简介: 对没有接触过串口通信的VC程序员来说显得非常困难,很久以前我在 codeguru.com 上搜索过串口通信相关信息得到了非常大的帮助,从那时起能编写一个简单易用的VC 串口类是我的梦想. 经过七个月在串口通信编程方面实践经验后,我编写了一个基于API实现的简单

上位机简单串口类,VC串口类

我是在一家做硬件的的公司里面做软件开发工程师的,我做的软件大多是是编写软件通过串口去控制硬件,所以串口编程对于我来说是很重要的.串口编程之前一直使用的是自己写的简单串口(只有发送,没有接收)类,或者上网下的CSerialPort类(个人觉得不好用啊,互锁变量一堆,代码一开始还让人看不懂,对于上位机使用不合适).对于我做的上位机软件,其实使用串口只是需要顺序发送,接收数据无误就行了,不需要考虑太多东西.所以最近有点空,就自己再写了一个串口类(希望可以简单使用的,没有太高的要求的). (1)界面 界

Remon Spekreijse CSerialPort串口类的修正版2014-01-10

转自:http://m.blog.csdn.net/blog/itas109/18358297# 2014-1-16阅读691 评论0 如需转载请标明出处:http://blog.csdn.net/itas109 这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等.Remon提供的串口类网址为:http://codeguru.earthweb.com/network/serialport.shtml, 其他贡献者:http://blog.csdn.ne

C#开发微信门户及应用(32)--微信支付接入和API封装使用

在微信的应用上,微信支付是一个比较有用的部分,但也是比较复杂的技术要点,在微商大行其道的年代,自己的商店没有增加微信支付好像也说不过去,微信支付旨在为广大微信用户及商户提供更优质的支付服务,微信的支付和安全系统由腾讯财付通提供支持.本文主要介绍如何在微信公众号上实现微信支付的接入.微信支付API的封装,以及API的调用,实现我们一些常见的业务调用. 1.开通微信支付并配置 微信支付是需要微信公众号的认证基础,也就是只对认证的公众号开放,微信认证需要签署相关的资料,并且进行对账认证,一般会有电话联

CSerialPort串口类最新修正版(解决关闭死锁问题)

这是一份优秀的类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等.Remon提供的串口类网址为:http://codeguru.earthweb.com/network/serialport.shtml,由于已经运行十几年了,原文的问答部分列出来这么多年来的问题,经过网友们的总结,补充和修改原来代码后,整理出一份相对比较完美的代码. 此外还附带一份小项目的源代码,它超越了串口助手,给人一种耳目一新的感觉.亮点如下: 1. 它解决了串口关闭时出现死锁不响应问题,可以直

CSerialPort串口类最新修正版2016-05-07

如需转载请标明出处:http://blog.csdn.net/itas109 QQ技术交流群:129518033 这是一份优秀的串口类文件,好多的地方值得我们学习,具体在多线程,事件,自定义消息,类的封装方面等等. Remon提供的串口类网址为:http://codeguru.earthweb.com/network/serialport.shtml, 由于已经运行十几年了,原文的问答部分列出来这么多年来的问题,经过网友们的总结,补充和修改原来代码后,整理出一份相对比较完美的代码. 2016-0