串口通讯对于需要进行嵌入式开发的小伙伴们来说是必不可少的,甚至说是无法回避的。而VC却又不像C#或者QT那样人性化,没有自带串口类(貌似VC6时代还有一个串口控件可以下载然后通过安装到VC6中),所以我们不得不到处寻找开源的串口通讯代码。
这里我找到的是CSerialPort,虽然官网作者没有继续更新了(还停留在2000年8月),但国内的牛人则在继续维护。
官网地址是这里:https://www.codeguru.com/cpp/i-n/network/serialcommunications/article.php/c2483/A-communication-class-for-serial-port.htm
我找到了牛人的维护版本下载了,感觉实现起来还算比较简单,这里将摸索过程罗列出来。
首先,我们必须找到SerialPort.cpp和SerialPort.h两个文件,并把他们添加到我们自己的工程中。
我这里建立一个基于对话框的MFC工程,添加完毕后注意引用到代码中,由于这个类修改者已经将SerialPort类置于了名称空间itas109中,所以不要忘记引入名称空间,开始代码如下:
第一步,引入必要的头文件
// SerialPortTestDlg.h : header file
//
#pragma once
#include "SerialPort.h"
using namespace std;
using namespace itas109;
第二步,声明串口变量及准备波特率数据
// CSerialPortTestDlg dialog
//此处准备我们需要用到的波特率及串口变量
int BaudRate[] = { 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200 };
CSerialPort m_SerialPort;
第三步,初始化串口(枚举所有串口)
这里要声明两个ComboBox变量,分别来存放串口号和波特率
在对话框的OnInitDialog中增加界面及串口初始化代码(增加代码之前在对话框中增加三个按钮和两个文本框两个下拉框)如下图:
// TODO: Add extra initialization here
//这里添加我们初始化串口的必要代码
CString temp;
//添加波特率到下拉列表
for (int i = 0; i < sizeof(BaudRate) / sizeof(int); i++)
{
temp.Format(_T("%d"), BaudRate[i]);
m_BaudRate.AddString((LPCTSTR)temp);
}
temp.Format(_T("%d"), 9600);
m_BaudRate.SetCurSel(m_BaudRate.FindString(0, temp));
//获取串口号
CSerialPortInfo a;
list<string> m_portsList = CSerialPortInfo::availablePorts();
list<string>::iterator itor;
TCHAR m_regKeyValue[255];
for (itor = m_portsList.begin(); itor != m_portsList.end(); ++itor)
{
#ifdef UNICODE
int iLength;
const char * _char = (*itor).c_str();
iLength = MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, NULL, 0);
MultiByteToWideChar(CP_ACP, 0, _char, strlen(_char) + 1, m_regKeyValue, iLength);
#else
strcpy_s(m_regKeyValue, 255, (*itor).c_str());
#endif
m_PortNr.AddString(m_regKeyValue);
}
m_PortNr.SetCurSel(0);
到这里注意,如果我们编译会出现这样的错误:
一开始 ,我也是很郁闷,总怎么修改都有这个
Error C1010 unexpected end of file while looking for precompiled header. Did you forget to add ‘#include “stdafx.h”’ to your source? SerialPortTest
后来,经过摸索发现,应该将SerialPort.cpp预编译设置修改为,不使用预编译
我们再次编译运行,一切正常,如下图:
第四步,实现读数据功能(建立接受函数的消息映射)
通过类想到,找到Message选项,建立一个消息映射(记住,根据SerialPort.cpp中的定义,这里的消息WM_COMM_RXSTR只能用,否则无效)
之后我们在OnReceiveStr中添加如下代码即可
struct serialPortInfo
{
UINT portNr;//串口号
DWORD bytesRead;//读取的字节数
}*pCommInfo;
pCommInfo = (serialPortInfo*)lParam;
CString str1((char*)wParam);
int len = _tcslen(str1.GetBuffer(0));
if (len == pCommInfo->bytesRead)
{
m_ReceiveCtrl.SetSel(-1, -1);
m_ReceiveCtrl.ReplaceSel(str1);
}
else
{
AfxMessageBox(_T("数据长度错误"));
}
return 0;
到这里为止,我们的串口基本可以实现读取数据的功能了。
写功能比较简单,在发送数据的按钮事件中添加下面的代码即可:
GetDlgItem(IDC_EDIT2)->SetFocus();
CString temp;
UpdateData(true);
m_OpenCloseCtrl.GetWindowText(temp);
if (temp == "打开串口")///没有打开串口
{
AfxMessageBox(_T("请首先打开串口"));
return;
}
m_Send.GetWindowText(temp);
size_t len = _tcsclen(temp) + 1;;
char* m_str = NULL;
size_t* converted = 0;
m_str = new char[len];
#ifdef UNICODE
wcstombs_s(converted, m_str, len, temp.GetBuffer(0), _TRUNCATE);
#else
m_str = temp.GetBuffer(0);
#endif
m_SerialPort.WriteToPort(m_str, len);
VS2017下编写的串口通讯源代码见下面链接:
VC2017串口通讯实例源代码