首页 > C++ > Win32串口API

Win32串口API

2014年5月26日 发表评论 阅读评论

哎哟我去,根据最近老板催我干活的状态,大概我离去帝都某查水表研究所搬砖的日子不远了,那边的活要做一个系统,系统里面包含一个上位机软件,上位机软件和各种硬件各种通信,然后其中一个精密的东西通信用的是串口,于是,有了这篇东西;

作为一个Google的代码搬运工,我看了好多别人的串口代码,但是都发现没有封装得很简洁很给力的API;

之前用的某个代码扔给师弟用后,发现有些端口检测不到,而且接受数据那个模块是每收到一个字节就会调用一次回调函数,有没有接收完还要我自己写代码判断;

其他的代码完全没有把串口这个模块彻底封装起来,在MFC里面操作串口那些函数还和各种修改窗口的控件的代码混在一起,基本不具备可移植性;

再有一些代码就是很底层很细的,每次用起来都好麻烦的样子。。

考虑到到时帝都的工作环境是绝对不允许我接触互联网的,所以我现在只能把可以做的那些模块尽可能的完善掉,保证去到那不会出什么意外,至少出了意外我要可以在不google的情况下解决;再另外,又为了可以尽快滚离帝都,我需要一个几行就可以用得上的API代码;懒得找,没找着,于是乎,我花了点时间,读了某个几千行的强大的串口助手的源码,一边剥一边改,捣鼓出来一个简单易用不知道稳不稳定的API来了。。

东西我扔Github上了。。有爱自取。。githubfavicon

下面把说明也扔一份在这博客了(其实是为了水一篇博文),搞不好可以帮助到路过的骚年们。。


食用方法:

环境:MFCVS2008下测试通过;(实验室的机子没有跑得起VS2013的,所以没法测试,估计没问题。。)

需要添加文件:

  • SerialPort.h
  • SerialPort.cpp
  • SerialPortApi.h
  • SerialPortApi.cpp

设置头文件:

#include "SerialPortApi.h"

添加封装的API变量:

CSerialPortApi COMPort;

打开串口并设置定时器(其实不一定要定时器,可以再开一个线程监视):

if(OPEN_PORT_SUCCESS == 
 COMPort.OpenPort("COM3",CBR_57600,8,NOPARITY,ONESTOPBIT)){
	SetTimer(RECV_TIMER_ID,50,NULL);
}
else{
	AfxMessageBox(COMPort.ErrorMsg());
}

定时器/监视线程内部读取数据方法:

if(COMPort.ReceiveFlag){//检测是否有接收到数据
	CString str += COMPort.ReadRecv();
	//Todo
}

发送数据:

COMPort.Send(str);

关闭串口:

COMPort.ClosePort();

妈蛋就这么几个,为什么别人要搞这么复杂?!


API说明:

1.打开串口:

INT CSerialPortApi::OpenPort(
       CString sPort,
       DWORD dwBaudRate,
       BYTE byDataBits,
       BYTE byParity,
       BYTE byStopBits);

输入参数:

  • sPort:端口号,一定要属于PortList(见下文)中的元素,否则会返回错误;
  • dwBaudRate:波特率,使用BAUDRATE中的宏元素,否则会返回错误;
  • static const DWORD BAUDRATE[] = {
    CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,
    CBR_4800,CBR_9600,CBR_14400,CBR_19200,
    CBR_38400,CBR_56000 ,CBR_57600,
    CBR_115200 ,CBR_128000 ,CBR_256000};
    
  • byDataBits:数据位,可选值只有6,7,8,否则返回错误;
  • byParity:校验位,使用PARITY中的宏元素(分别表示无校验,奇校验,偶校验,标记,空格),否则会返回错误;
  • static const DWORD PARITY[] = {
    NOPARITY,ODDPARITY,EVENPARITY,MARKPARITY,SPACEPARITY};
    
  • byStopBits:停止位,使用STOPBITS中的宏元素(分别表示1,1.5,2),否则会返回错误;
  • static const DWORD STOPBITS[]={
    ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS};
    

返回值:

  • 串口名有误: PORT_NUM_INVALID
  • 波特率有误:BAUDRATE_INVALID
  • 数据位有误:DATABITS_INVALID
  • 校验位有误:PARITY_INVALID
  • 停止位有误:STOPBIT_INVALID
  • 成功:OPEN_PORT_SUCCESS
  • 打开失败:OPEN_PORT_FAIL

OPEN_PORT_FAIL的详细错误信息可以用CString CSerialPortApi::ErrorMsg()获取,另外如果打开串口失败又找不到原因的话,可以将SerialPortApi.h中的:

#define PORT_DEBUG_MODE 0

改成:

#define PORT_DEBUG_MODE 1

这样出错了会以弹窗显示错误原因;

2.关闭串口:

INT CSerialPortApi::ClosePort();

返回值:

  • 成功:CLOSE_PORT_SUCCESS
  • 失败:CLOSR_PORT_FAIL

3.发送数据:

void CSerialPortApi::Send(CString str);

输入参数:

  • str:待发送数据

4.读取数据:

CString CSerialPortApi::ReadRecv();

返回值:

  • 读取到的数据

说明:读取数据之前请先判断ReceiveFlag是否为TRUE,否则读到的是空字符串;

5.读取错误信息:

CString CSerialPortApi::ErrorMsg();

返回值:

  • 错误代码和错误消息

可用变量和方法:

1.可用的端口:
CString PortList[MAX_PORT_NUM];

2.获取可用的端口数目:
size_t GetPortNum();

3.缓冲区是否有接受数据未读取:
BOOL ReceiveFlag;

Private函数说明:

说明:由于是私有函数,这一部分函数原则上是无法直接拿来用的,使用者原则上无需知晓内部怎么实现,如为了读代码或者修复BUG可以稍作了解;

1.获取所有可用的端口:

void CSerialPortApi::getExistPort()

基于读取注册表项HARDWARE\DEVICEMAP\SERIALCOMM,理论上和设备管理器看到的一样。

调用后将会初始化CSerialPortApi内的两个变量:PortNum,PortList,分别表示读取到的串口的数目和列表;

API类变量外部读取PortNum要使用size_t GetPortNum()函数;

getExistPort放在了CSerialPortApi的构造函数中,所以定义变量后即可读取PortList;

2.发送数据线程:

static UINT CSerialPortApi::SendThreadProc(LPVOID pParam);

线程开启后,只要b_portIsOpenTRUE,即一直在一个while循环中等待,由m_hSendEvent变量激活发送模块,激活方法是:SetEvent(m_hSendEvent);,激活后即会发送Str4Send中的信息;

3.接受数据线程:

static UINT CSerialPortApi::RevThreadProc(LPVOID pParam);

该线程如果接收到数据,会把数据放到deque m_dequeRevData的尾部,并且设置ReceiveFlagTRUE;

如果检测到ReceiveFlagTRUE,可以调用ReadRecv()读取数据,将读取到的缓存数据转化为CString类型变量返回,并且清空队列,设置ReceiveFlagFALSE

由于采用了信号锁机制,所以不会发生RevThreadProcm_dequeRevData写信息的同时ReadRecv()m_dequeRevData读信息这种事情;

其他:

如果想像串口助手等辅助软件一样添加若干个CComboBox,然后供用户选择的话,可以设置如下五个CComboBox变量关联相关控件:

CComboBox m_port;
CComboBox m_baudrate;
CComboBox m_parity;
CComboBox m_databits;
CComboBox m_stopbits;

然后使用下面代码初始化即可:

	m_port.ResetContent();

	//Set Port
	for(size_t i = 0;i < COMPort.GetPortNum();i++)
	{
		m_port.AddString(COMPort.PortList[i]);
	}
	m_port.SetCurSel(0);

	//Set Baudrate
	CString str = "";
	m_baudrate.ResetContent();
	int i = 0;
	for(i = 0;i<sizeof(BAUDRATE)/sizeof(DWORD);i++)
	{
		str.Format(_T("%d"),BAUDRATE[i]);
		m_baudrate.AddString(str);
	}
	m_baudrate.SelectString(-1,"57600");

	m_parity.ResetContent();
	m_parity.AddString(_T("无校验"));
	m_parity.AddString(_T("奇校验"));
	m_parity.AddString(_T("偶校验"));
	m_parity.AddString(_T("标记"));
	m_parity.AddString(_T("空格"));
	m_parity.SetCurSel(0);

	m_databits.ResetContent();
	m_databits.AddString(_T("6"));
	m_databits.AddString(_T("7"));
	m_databits.AddString(_T("8"));
	m_databits.SelectString(-1,"8");

	m_stopbits.ResetContent();
	m_stopbits.AddString(_T("1"));
	m_stopbits.AddString(_T("1.5"));
	m_stopbits.AddString(_T("2"));
	m_stopbits.SetCurSel(0);

以上!!【唉~刷完存在感~浑身舒畅!!】


【完】

本文内容遵从CC版权协议,转载请注明出自http://www.kylen314.com

分类: C++ 标签: , ,
  1. 2014年5月26日09:16 | #1

    我们的同志遍布五湖四海,甚至打入了帝都某极密科学机关内部(雾查了下上位机和串口是啥,发现你从事的工作好杂啊。

    • 2014年5月26日15:07 | #2

      我早就这样觉得了。。

      • 2014年5月26日15:35 | #3

        其实工作不杂,只是我那些乱七八糟的博文会让人以为那是我的正业。。其实都是业余兴趣而已。。

    • 2014年5月26日15:34 | #5

      其实不是打入,而是被扔进去了。。只是隔行如隔山,电子类的专业大一的就会接触到这些概念了。。

      • 2014年5月26日16:07 | #6

        我就是电子类的,我到大二都没在课上学到,只是这学期玩单片机的时候接触了些。

        • 2014年5月26日16:17 | #7

          额。。好吧,我们以前那个班是按三年毕业标准来设立课程的可能会早一些。。

          • 2014年5月26日18:54 | #8

            我们学校确实学的很慢,中科大大二上就把模电和数电学完了,我们大二下才开模电,大三上才开数电,落后一年啊。哎不知学校是怎么想的,貌似没有比我们进度更慢的学校。

            • 2014年5月26日19:09 | #9

              大三才开数电?!是有点慢。。我们大一下是数电和电路,大二上是模电,大三上还是大二下开的高频和射频。

      • 2014年5月26日21:05 | #10

        大一净学了一堆乱七八糟的通识课,不分专业有空余时间都不知道该学神马,一直觉得无比坑爹……

        • 2014年5月26日21:27 | #11

          我们那个班级还好,因为入学时学制是三年,所以赶紧让我们提前进实验室做项目,提前学课程以及。。提前考四六级。。但是最后我们集体要求改成四年,所以大四就完全没事干在学校混日子。。

  2. 2014年5月26日21:06 | #13

    话说开始好奇菊苣是啥专业的说……

    • 2014年5月26日21:27 | #14

      本科入学录取时是微电子,后来转通信工程,现在是微波光子学,搞的是水声。。【别吐槽

      • 2014年5月27日10:09 | #15

        通信啊,难怪…话说微电子具体是学啥的一直不明白我学校通信工程全是一群学霸,果断分专业的时候挤不进去

        • 2014年5月27日13:26 | #16

          微电子主要偏向于半导体器件的制作,工艺,封装,原理这一块的吧。。信工确实竞争很紧张,我们本科那里信工一个是转专业班,对于有第二次生命的他们普通的觉悟根本不够战斗;然后我们班虽然三年毕业,但是是淘汰制的。。。

        • 2014年5月27日21:00 | #18

          通信工程全是一群学霸。。我学校也是啊,谁能解释下

  3. 2014年5月27日00:47 | #20

    云里雾里

  4. 2014年5月27日19:18 | #21

    Vespa每次刷存在感都异常给力啊

  5. 2014年5月28日16:24 | #27

    下午好,

  6. 2014年5月29日16:36 | #28

    完全是代码水啊

  7. 2014年5月30日11:14 | #30

    直接fork了,然后突然发现几百年没用windows写过程序了,唯一的用来下各种番的win8只装了个qt,连vs2008都没得

    • 2014年5月30日14:50 | #31

      “砖”务需求,无法彻底脱离windows。。总不能给某所开发一个linux程序吧。。。而且有些仿真软件只有win有。。虽然我现在办公室两台电脑,一台win的用来搬砖,另一台搜刮来的电脑装了个ubuntu完全自娱自乐调教用。。

      • 2014年5月30日17:46 | #32

        我给某神秘机关做个东西,表示”我们的东西只能在linux下用”。然后一本正经的给他们机架上的服务器装了个fedora因为实验室mac使用者最多,linux其次,windows最后,所以基本没啥”必须要windows”的情况。突然感觉好幸运。然后linuxer貌似都ubuntu,只有一个货是arch,我大fedora er 好孤独….

        • 2014年5月30日19:14 | #33

          专业不一样导致待遇不一样。。比如我们这边macer=0,我说我做了个linux下的东西给别人的话,老板非劈了我不可。。

          • 2014年5月30日19:37 | #34

            主要大概是面向的用户在程序中的地位。我们主要是搞数据的,所以后台想咋折腾咋折腾,最后随便开个页面展示下,只要让用户浏览器看到的巴掌大地方没问题就成。但前台有时候也挺扯的。比如前面提到的那个神秘机关的头头说他们用的是IE6,看不了基于CSS3的d3js生成的效果。为了让所有人都看到,不得不将若干页面做了两份,一份漂亮一份兼容。居然要为10年前的浏览器擦屁股,唉。。。不过,反正不是我做,对我来说反正各种model各种index各种service搞定就成

            • 2014年5月30日19:48 | #35

              我们这种是纯粹做光学的实验室,实验室码代码的也就我一个人,linux这种东西根本普及不了。。果然还是计算机相关的专业好玩啊~

  8. 2014年5月31日20:48 | #37

    你学啥专业的。

  9. yuki
    2014年6月7日15:04 | #38

    某所到底是啥。。。该不会是⑨所吧。。。终于还是走上了毁灭世界的道路

    • 2014年6月7日22:54 | #39

      当然不是⑨。。HTS,项目就是某次在你家跟你讲的那个。。是呀,国家又让我造高达去了。。

      • yuki
        2014年6月7日23:12 | #40

        还好。。。如果是⑨所搞不好真的就消失于人世了

  10. 2014年6月27日17:10 | #44

    我之前做这个,先是用了labview,然后又用的pyserial

    • 2014年6月27日17:37 | #45

      我这个项目必须C++。。木有办法。。

      • 2014年6月27日17:39 | #46

        貌似我从来没有真正的使用过c++这门语言。最多写个cout和include iostream,然后里面的代码就是C了

        • 2014年6月27日17:43 | #47

          毕竟不需要做软件,脚本化的话都很少会用C++

  11. gnaggnoyil
    2014年7月2日11:02 | #48

    fork一发

  12. 2014年7月6日15:22 | #49

    下次进来之前先滴好眼药水。。

验证码:8 + 9 = ?

友情提示:留言可以使用大部分html标签和属性;

添加代码示例:[code lang="cpp"]your code...[/code]

添加公式请用Latex代码,前后分别添加两个$$