迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > C++ >

C++中的串口连接

作者:迹忆客 最近更新:2023/08/18 浏览次数:

本文介绍了使用 Windows API 打开、读取、写入和管理串行端口连接的基础知识。

本文的主要目的是让您基本了解编程中的串行通信如何工作,并让您朝着正确的方向开始。

本文假定您对 C/C++ 有基本的了解,可以编译和运行程序,并且您的开发环境已设置为使用 Windows API 调用。


C++中的串口连接

C++中读取数据或写入串口有六个步骤:

  1. 打开串口
  2. 设置一些基本属性
  3. 设置超时时间
  4. 读取或写入数据
  5. 清理端口
  6. 一些高级功能

打开串口

第一步是开放港口。 这是最简单的步骤之一,特别是如果您已经了解 Windows 文件 I/O。

首先,您必须确保已在文件中包含所需的头文件,即 windows.h。 然后,使用以下代码:

HANDLE h_Serial;
h_Serial = CreateFile("COM1",GENERIC_READ | GENERIC_WRITE,
                     0,
                     0,
                     OPEN_EXISTING,
                     FILE_ATTRIBUTE_NORMAL,
                     0);
if(h_Serial==INVALID_HANDLE_VALUE){
    if(GetLastError()==ERROR_FILE_NOT_FOUND){
        //serial port not found. Handle error here.
    }
    //any other error. Handle error here.
}

我们在第一行创建了一个HANDLE类型的变量,然后调用函数CreateFile对其进行初始化。 该函数的第一个参数是您需要打开的文件的名称; 在本例中,我们想要打开一个串行端口,这就是为什么我们使用该端口的名称,即 COM1。

第二个参数是指定我们是否需要读取或写入数据。 如果不需要完成任何任务,则可以保留此参数。

接下来的两个参数始终保持为零。 下一个参数是指定是否需要打开现有文件或创建新文件。

在本例中,它是端口,因此我们使用 OPEN_EXISTING。 下一个参数是向 Windows 指定我们不需要任何花哨的东西,但需要定期读取或写入。

最后一个参数也始终保持为零。

设置基本属性

获得端口的句柄后,我们需要设置一些基本属性,如带宽速率、字节大小、停止位等。这是使用 struct DCB 完成的。

DCB dcbSerialParam = {0};
dcbSerial.DCBlength=sizeof(dcbSerialParam);

if (!GetCommState(h_Serial, &dcbSerialParam)) {
    //handle error here
}

dcbSerialParam.BaudRate=CBR_19200;
dcbSerialParam.ByteSize=8;
dcbSerialParam.StopBits=ONESTOPBIT;
dcbSerialParam.Parity=NOPARITY;

if(!SetCommState(h_Serial, &dcbSerialParam)){
    //handle error here
}

在上面的代码片段中,我们在第一行代码中创建了一个 DCB 对象,并将其初始化为零以清除值。 在下面的行中,我们设置了该结构的长度,这是 Windows 的强制步骤。

之后,我们调用函数 GetCommState 并向其传递两个参数,即我们的端口 HANDLE 和 DCB 对象,以填写当前使用的参数。

一旦我们有了这个,我们必须设置关键参数,即波特率、字节大小、停止位和奇偶校验。

Windows 需要我们使用特殊常量提供 BaudRate。 例如,CBR 19200 代表 19200 波特,CBR 9600 代表 9600 波特,CBR 57600 代表 57600 波特等。

我们可以直接定义 ByteSize,但 StopBits 和 Parity 需要额外的变量。 ONESTOPBIT、ONE5STOPBITS 和 TWOSTOPBITS 是 StopBit 的可能性。

EVENPARITY、NOPARITY 和 ODDPARITY 是最广泛使用的奇偶校验替代方案。 其他的也存在,但不太出名。

有关详细信息,请参阅 MSDN 库项目(搜索 DCB)。

在将 DCB 结构配置为优先级后,我们需要将这些设置应用于串行端口。 SetCommState 函数用于完成此操作。

设置超时

如果没有数据进入串行端口(例如,串行端口设备已关闭或断开连接),则从串行端口读取数据可能会导致应用程序在等待数据出现时停止。 有两种方法可以处理这种情况。

首先,可以在应用程序中使用多线程,一个线程处理串行端口困难,另一个线程处理实际处理。 这可能会变得麻烦和复杂,而且不是必需的。

另一种选择更简单:告诉 Windows 停止等待数据出现! 这是通过以下方式实现的:

COMMTIMEOUTS timeout={0};
timeout.ReadIntervalTimeout=60;
timeout.ReadTotalTimeoutConstant=60;
timeout.ReadTotalTimeoutMultiplier=15;
timeout.WriteTotalTimeoutConstant=60;
timeout.WriteTotalTimeoutMultiplier=8;
if(!SetCommTimeouts(h_Serial, &timeout)){
    //handle error here
}

COMMTIMEOUTS 结构相当简单,只有上面列出的字段。 快速回顾一下:

  • ReadIntervalTimeout - 指定超时之前接收字符之间必须经过的时间(以毫秒为单位)。
  • ReadTotalTimeoutConstant - 提供返回之前等待的时间(以毫秒为单位)。
  • ReadTotalTimeoutMultiplier - 指定响应读取操作中请求的每个字节之前等待的时间长度(以毫秒为单位)。
  • WriteTotalTimeoutConstant 和 WriteTotalTimeoutMultiplier - 两者都完成与 ReadTotalTimeoutConstant 和 WriteTotalTimeoutMultiplier 相同的任务,但用于写入而不是读取。

将 ReadIntervalTimeout 设置为 MAXDWORD 并将 ReadTotalTimeoutConstant 和 ReadTotalTimeoutMultiplier 设置为 0 会导致任何读取操作立即返回缓冲区中的任何字符(即已经接收到的字符),即使不存在任何字符。

配置 COMMTIMEOUTS 结构后,我们需要使用 SetCommTimeouts 方法将更改应用到串行端口。

读/写数据

您可以在打开串行端口后使用必要的参数和超时开始读取数据。 这些都很容易理解。

考虑从串口读取n个字节的场景。 然后,我们只需按照以下步骤操作:

char sBuff[n + 1] = {0};
DWORD dwRead = 0;
if(!ReadFile(h_Serial, sBuff, n, &dwRead, NULL)){
//handle error here
}

文件(串行端口)的句柄、存储数据的缓冲区、要读取的字节数、对要设置为读取的字节数的整数的引用以及 NULL 都传递给 ReadFile。 ReadFile 操作读取的字节数将存储在 dwRead 中。

写入数据也是同样的过程。 唯一的区别是WriteFile函数可以写入数据。

关闭端口

使用完串口后,合上手柄。 如果不这样做,可能会发生奇怪的事情,包括在您重新启动之前没有其他人能够使用串行端口。

无论如何,这很容易完成,因此请记住以下几点:

CloseHandle(h_Serial);

处理错误 正如您可能已经看到的,我们在每个系统调用后插入了一条注释,说明您应该处理错误。

这始终是一种优秀的编程实践,但对于 I/O 函数尤其重要,因为 I/O 函数更容易失败。 无论如何,以下所有函数在失败时都返回 0,在成功时返回 0 以外的任何值。

要找出问题所在,请使用 GetLastError() 函数,该函数以 DWORD 形式返回错误代码。 您可以使用 FormatMessage 方法将其转换为有意义的字符串:

char lastErr[1020];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL,
            GetLastError(),
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            lastErr,
            1020,
            NULL);

上一篇:C++ 中的反射

下一篇:没有了

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

C++ 中的反射

发布时间:2023/08/18 浏览次数:171 分类:C++

本文将讨论 C++ 中的反射、目的和实现。 我们将进一步研究使用反射的优点和缺点。C++ 中的反射 反射是一种编程机制,允许您编写适用于任何对象类型的通用代码。

C++ 中 DWORD 和 Unsigned Int 的区别

发布时间:2023/08/18 浏览次数:187 分类:C++

本文将介绍 DWORD 在 C++ 中的一般用法,它与 unsigned int 有着根本的不同,尽管它们目前具有相同的值。C++ 中 DWORD 和 unsigned int 的区别 根据定义,unsigned int 至少有 16 位长。 unsigned int 通常是特定

修复 C++ 中的分段错误

发布时间:2023/08/18 浏览次数:192 分类:C++

本文将讨论 C++ 中的分段错误并提供解决此问题的解决方案。C++ 中的分段错误当您的程序或系统尝试访问超出其范围的内存时,C++ 中就会出现分段错误。 这是导致程序在运行时崩溃的常见情况

在 C++ 中通过掷骰子生成随机值

发布时间:2023/04/09 浏览次数:172 分类:C++

本文解释了如何使用时间因子方法和模拟 C++ 中的掷骰子的任意数方法生成随机数。了解它是如何工作的以及它包含哪些缺点。提供了一个 C++ 程序来演示伪数生成器。

在 C++ 中使用模板的链表

发布时间:2023/04/09 浏览次数:162 分类:C++

本文解释了使用模板在 C++ 中创建链表所涉及的各个步骤。工作程序演示了一个链表,该链表使用模板来避免在创建新变量时声明数据类型的需要。

在 C++ 中添加定时延迟

发布时间:2023/04/09 浏览次数:235 分类:C++

本教程将为你提供有关在 C++ 程序中添加定时延迟的简要指南。这可以使用 C++ 库为我们提供的一些函数以多种方式完成。

在 C++ 中创建查找表

发布时间:2023/04/09 浏览次数:172 分类:C++

本文重点介绍如何创建查找表及其在不同场景中的用途。提供了三个代码示例以使理解更容易,并附有代码片段以详细了解代码。

如何在 C++ 中把字符串转换为小写

发布时间:2023/04/09 浏览次数:93 分类:C++

介绍了如何将 C++ std::string 转换为小写的方法。当我们在考虑 C++ 中的字符串转换方法时,首先要问自己的是我的输入字符串有什么样的编码

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便