RS232 串行通信:C++ 实现指南

image.png

文章目录

    • 一、RS232 简介
      • 1. 电气特性
      • 2. 传输速率
      • 3. 传输距离
    • 二、在 C++ 中实现 RS232 通信
      • 1. Windows 平台
        • (1)打开串行端口
        • (2)配置串行通信参数
        • (3)发送数据
        • (4)接收数据
        • (5)主函数示例
      • 2. Linux 平台
        • (1)打开串行端口
        • (2)配置串行通信参数
        • (3)发送数据
        • (4)接收数据
        • (5)主函数示例
    • 三、注意事项
    • 四、总结

在现代嵌入式系统和工业控制领域,RS232 串行通信仍然是一种不可或缺的技术。尽管 USB 和以太网等高速通信技术已经广泛应用,但在一些需要低速、简单通信的场景中,RS232 仍然是首选。本文将详细介绍如何在 C++ 中实现与 RS232 的通信,包括 Windows 和 Linux 平台的实现方法。

一、RS232 简介

RS232 是一种串行通信协议,用于实现设备之间的近距离数据传输。它通过一对发送和接收引脚(TxD 和 RxD)进行数据通信,并支持控制信号(如 RTS 和 CTS)以实现流控制。RS232 的传输速率通常在几百比特每秒(bps)到几十千比特每秒(kbps)之间,适用于低速数据传输场景。

1. 电气特性

RS232 使用负逻辑,逻辑“1”对应的电平范围是 -3V 到 -15V,逻辑“0”对应的电平范围是 +3V 到 +15V。这种设计使得信号在传输过程中具有一定的抗干扰能力。

2. 传输速率

RS232 的传输速率通常在几百 bps 到几十 kbps 之间。例如,常见的波特率有 9600 bps、19200 bps 和 115200 bps。波特率越高,数据传输速度越快,但传输距离和可靠性可能会受到影响。

3. 传输距离

RS232 的传输距离通常在 15 米以内。如果需要更长的传输距离,可以使用 RS485 等其他串行通信协议。

二、在 C++ 中实现 RS232 通信

在 C++ 中实现 RS232 通信,需要使用操作系统提供的串行通信 API。以下是基于 Windows 和 Linux 平台的实现方法。

1. Windows 平台

在 Windows 系统中,可以使用 Windows API 中的串行通信函数来实现与 RS232 的通信。以下是一个简单的示例代码,展示如何打开串行端口、设置通信参数、发送和接收数据。

(1)打开串行端口
#include <windows.h>
#include <iostream>// 打开串行端口
HANDLE OpenSerialPort(const char* portName)
{HANDLE hSerial;hSerial = CreateFile(portName, // 端口名称,如 "COM1"GENERIC_READ | GENERIC_WRITE, // 读写权限0, // 不共享0, // 无安全属性OPEN_EXISTING, // 打开已存在的设备0, // 非异步0 // 无模板);if (hSerial == INVALID_HANDLE_VALUE){std::cerr << "Error opening serial port" << std::endl;return INVALID_HANDLE_VALUE;}return hSerial;
}
(2)配置串行通信参数
// 配置串行通信参数
bool ConfigureSerialPort(HANDLE hSerial)
{DCB dcbSerialParams = { 0 };dcbSerialParams.DCBlength = sizeof(dcbSerialParams);if (!GetCommState(hSerial, &dcbSerialParams)){std::cerr << "Error getting state" << std::endl;return false;}dcbSerialParams.BaudRate = CBR_9600; // 波特率 9600dcbSerialParams.ByteSize = 8; // 数据位 8dcbSerialParams.StopBits = ONESTOPBIT; // 停止位 1dcbSerialParams.Parity = NOPARITY; // 无校验位if (!SetCommState(hSerial, &dcbSerialParams)){std::cerr << "Error setting state" << std::endl;return false;}return true;
}
(3)发送数据
// 发送数据
bool WriteSerialPort(HANDLE hSerial, const char* data, DWORD size)
{DWORD bytesWritten;if (!WriteFile(hSerial, data, size, &bytesWritten, 0)){std::cerr << "Error writing to port" << std::endl;return false;}return true;
}
(4)接收数据
// 接收数据
bool ReadSerialPort(HANDLE hSerial, char* buffer, DWORD size)
{DWORD bytesRead;if (!ReadFile(hSerial, buffer, size, &bytesRead, 0)){std::cerr << "Error reading from port" << std::endl;return false;}return true;
}
(5)主函数示例
int main()
{const char* portName = "COM1";HANDLE hSerial = OpenSerialPort(portName);if (hSerial == INVALID_HANDLE_VALUE){return 1;}if (!ConfigureSerialPort(hSerial)){CloseHandle(hSerial);return 1;}const char* dataToSend = "Hello, Serial Port!";if (!WriteSerialPort(hSerial, dataToSend, strlen(dataToSend))){CloseHandle(hSerial);return 1;}char readBuffer[128];if (ReadSerialPort(hSerial, readBuffer, sizeof(readBuffer))){std::cout << "Received: " << readBuffer << std::endl;}CloseHandle(hSerial);return 0;
}

2. Linux 平台

在 Linux 系统中,可以使用 POSIX 串行通信 API 来实现与 RS232 的通信。以下是一个简单的示例代码。

(1)打开串行端口
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <cstring>// 打开串行端口
int OpenSerialPort(const char* portName)
{int fd = open(portName, O_RDWR | O_NOCTTY | O_SYNC);if (fd < 0){std::cerr << "Error opening serial port" << std::endl;return -1;}return fd;
}
(2)配置串行通信参数
// 配置串行通信参数
bool ConfigureSerialPort(int fd)
{struct termios tty;if (tcgetattr(fd, &tty) != 0){std::cerr << "Error getting attributes" << std::endl;return false;}cfsetospeed(&tty, B9600); // 波特率 9600cfsetispeed(&tty, B9600);tty.c_cflag &= ~PARENB; // 无校验位tty.c_cflag &= ~CSTOPB; // 1 停止位tty.c_cflag &= ~CSIZE; // 清除数据位掩码tty.c_cflag |= CS8; // 8 数据位tty.c_cflag &= ~CRTSCTS; // 关闭硬件流控制tty.c_cflag |= CREAD | CLOCAL; // 启用接收器,忽略调制解调器控制线tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控制tty.c_iflag &= ~(ICRNL | INLCR); // 禁用回车和换行的特殊处理tty.c_lflag &= ~ICANON;tty.c_lflag &= ~ECHO; // 关闭回显tty.c_lflag &= ~ISIG; // 关闭信号字符tty.c_oflag &= ~OPOST; // 防止特殊解释输出数据tty.c_oflag &= ~ONLCR; // 防止将换行符转换为回车换行符if (tcsetattr(fd, TCSANOW, &tty) != 0){std::cerr << "Error setting attributes" << std::endl;return false;}return true;
}
(3)发送数据
// 发送数据
bool WriteSerialPort(int fd, const char* data, size_t size)
{ssize_t bytesWritten = write(fd, data, size);if (bytesWritten < 0){std::cerr << "Error writing to port" << std::endl;return false;}return true;
}
(4)接收数据
// 接收数据
bool ReadSerialPort(int fd, char* buffer, size_t size)
{ssize_t bytesRead = read(fd, buffer, size);if (bytesRead < 0){std::cerr << "Error reading from port" << std::endl;return false;}return true;
}
(5)主函数示例
int main()
{const char* portName = "/dev/ttyS0";int fd = OpenSerialPort(portName);if (fd < 0){return 1;}if (!ConfigureSerialPort(fd)){close(fd);return 1;}const char* dataToSend = "Hello, Serial Port!";if (!WriteSerialPort(fd, dataToSend, strlen(dataToSend))){close(fd);return 1;}char readBuffer[128];if (ReadSerialPort(fd, readBuffer, sizeof(readBuffer))){std::cout << "Received: " << readBuffer << std::endl;}close(fd);return 0;
}

三、注意事项

  1. 波特率匹配:发送端和接收端的波特率必须一致,否则会导致数据传输错误。
  2. 串行端口名称:在 Windows 上,串行端口名称通常为 “COM1”、“COM2” 等;在 Linux 上,通常为 “/dev/ttyS0”、“/dev/ttyUSB0” 等。
  3. 错误处理:在实际应用中,需要对串行通信中的错误进行详细处理,例如超时错误、硬件故障等。
  4. 多线程支持:在需要同时进行发送和接收操作时,可以使用多线程来实现。

四、总结

RS232 串行通信在工业控制和嵌入式系统中仍然具有重要地位。通过使用 Windows API 和 POSIX API,我们可以在 C++ 中实现与 RS232 的通信。本文提供了详细的代码示例,帮助开发者快速上手。在实际应用中,还需要根据具体需求进行适当的扩展和优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902292.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux指令合集

一、VI的使用 命令行模式&#xff1a;默认此模式&#xff0c;从输入模式回到命令行模式&#xff1a;esc &#xff0c; esc按完&#xff0c;insert消失 输入模式&#xff1a;按 i 进入 &#xff0c; 看到insert就能编辑代码 退出vi 保存代码 命令行模式下 按&#xf…

IDEA使用jclasslib Bytecode Viewer查看jvm字节码

学习jvm的时候&#xff0c;想查看字节码和局部变量表&#xff0c;可以使用idea安装jclasslib Bytecode View插件查看。 &#xff08;1&#xff09;安装工具&#xff1a; 安装完成后需要重启idea. &#xff08;2&#xff09;准备一段代码&#xff0c;编译运行 package com.te…

从多个Excel批量筛查数据后合并到一起

这篇文章将讲解如何批量的从多个Excel文件中筛选出需要的数据&#xff0c;最后合并到一张新的Excel。 全程0代码图形化界面操作。 准备数据 这里准备了3个测试文件&#xff0c;每个文件的格式是一样的 现在我们需要筛选出每个文件里面&#xff0c;基金简称包含“南方远见”&a…

Debian GNU/Linux的新手入门介绍

Debian GNU/Linux&#xff1a;起源、基本介绍与发行版对比 一、起源与发展历程 Debian GNU/Linux 是现存最古老的 Linux 发行版之一&#xff0c;由 Ian Murdock 于 1993 年 8 月 16 日创立。其名称结合了他的女友&#xff08;后成为妻子&#xff09;Debra 和他自己的名字 Ian…

Sentinel源码—7.参数限流和注解的实现一

大纲 1.参数限流的原理和源码 2.SentinelResource注解的使用和实现 1.参数限流的原理和源码 (1)参数限流规则ParamFlowRule的配置Demo (2)ParamFlowSlot根据参数限流规则验证请求 (1)参数限流规则ParamFlowRule的配置Demo 一.参数限流的应用场景 二.参数限流规则的属性 …

多数据源配置(MyBatis-Plus vs AbstractRoutingDataSource)

MyBatis-Plus vs AbstractRoutingDataSource MyBatis-Plus多数据源配 1.添加依赖 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version> <…

聊透多线程编程-线程互斥与同步-13. C# Mutex类实现线程互斥

目录 一、什么是临界区&#xff1f; 二、Mutex类简介 三、Mutex的基本用法 解释&#xff1a; 四、Mutex的工作原理 五、使用示例1-保护共享资源 解释&#xff1a; 六、使用示例2-跨进程同步 示例场景 1. 进程A - 主进程 2. 进程B - 第二个进程 输出结果 ProcessA …

stm32week12

stm32学习 九.stm32与HAL库 2.HAL库框架 总架构&#xff1a; 文件介绍&#xff1a; ppp是某一外设&#xff0c;ex是拓展功能 HAL库API函数和变量命名规则&#xff1a; HAL库对寄存器位操作的相关宏定义&#xff1a; HAL库的回调函数&#xff1a; 3.STM32启动过程 MDK编译过…

opencv HSV的具体描述

色调H&#xff1a; 使用角度度量&#xff0c;取值范围为0\~360&#xff0c;从红色开始按逆时针方向计算&#xff0c;红色为0&#xff0c;绿色为120&#xff0c;蓝色为240。它们的补色是&#xff1a;黄色为60&#xff0c;青色为180&#xff0c;紫色为300。通过改变H的值&#x…

Java Lambda表达式指南

一、Lambda表达式基础 1. 什么是Lambda表达式&#xff1f; 匿名函数&#xff1a;没有名称的函数函数式编程&#xff1a;可作为参数传递的代码块简洁语法&#xff1a;替代匿名内部类的更紧凑写法 2. 基本语法 (parameters) -> expression 或 (parameters) -> { statem…

面向对象设计中的类的分类:实体类、控制类和边界类

目录 前言1. 实体类&#xff08;Entity Class&#xff09;1.1 定义和作用1.2 实体类的特点1.3 实体类的示例 2. 控制类&#xff08;Control Class&#xff09;2.1 定义和作用2.2 控制类的特点2.3 控制类的示例 3. 边界类&#xff08;Boundary Class&#xff09;3.1 定义和作用3…

C# 封装教程

原文&#xff1a;C# 封装_w3cschool &#xff08;注&#xff1a;本文为教程文章&#xff0c;请勿标记为付费文章&#xff01;特此声明&#xff09; 封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中&#xff0c;封装是…

量化交易 - RSRS(阻力支撑相对强度)- 正确用法 - 年均收益18%

经过研究&#xff0c;发现RSRS的正确用法其实是需要用到两个数据&#xff0c;分别是 n: 一阶拟合样本数&#xff0c;m:求均值方差样本数&#xff0c;其中n比较小 如18&#xff0c;m比较大 如1100 经过调优后&#xff0c;收益率显著上升&#xff01; 如下图&#xff1a; &…

Oracle expdp的 EXCLUDE 参数详解

Oracle expdp的 EXCLUDE 参数详解 EXCLUDE 是 Oracle Data Pump Export (expdp) 工具中的一个关键参数&#xff0c;用于指定在导出过程中要排除的对象或对象类型。 一、基本语法 expdp username/password DUMPFILEexport.dmp DIRECTORYdpump_dir EXCLUDEobject_type[:name_c…

如何使用3DMAX插件PFSpliner将3D对象转化为艺术样条线?

什么是粒子流源(Particle Flow)是3DMAX的一个功能极其强大的粒子系统。它采用事件驱动模型,使用一个名为“粒子视图”的特殊对话框。在“粒子视图”中,您可以将描述粒子属性(如形状、速度、方向和一段时间内的旋转)的单个运算符组合成称为事件的组。每个操作符都提供一组…

【python】 循环语句(while)

1、循环语句 语法&#xff1a; while 条件:......... #只有条件为真时&#xff0c;才会执行while中的内容。 1.1循环语句基本使用 示例1&#xff1a; print("开始") while 1>2:print("人生得意须尽欢") print("结束") #输出结果&#…

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比 目录 OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于OOA-CN…

20250421在荣品的PRO-RK3566开发板的Android13下频繁重启RKNPU fde40000.npu: Adding to iommu gr

20250421在荣品的PRO-RK3566开发板的Android13下频繁重启RKNPU fde40000.npu: Adding to iommu gr 2025/4/21 14:50 缘起&#xff1a;电池没电了&#xff0c;导致荣品的PRO-RK3566的核心板频繁重启。 内核时间4s就重启。100%复现。 PRO-RK3566 Android13启动到这里 复位&#…

动态监控进程

1.介绍: top和ps命令很相似,它们都是用来显示正在执行的进程,top和ps最大的不同之处,在于top在执行中可以更新正在执行的进程. 2.基本语法&#xff1a; top [选项] 选项说明 ⭐️僵死进程&#xff1a;内存没有释放,但是进程已经停止工作了,需要及时清理 交互操作说明 应用案…

657SJBH西藏藏药特产销售管理系统

毕业论文&#xff08;设计&#xff09;文献综述 西藏藏药特产销售管理系统的设计与实现 近年来&#xff0c;随着网络技术特别是Internet技术的普及和发展&#xff0c;电子商务的开发和应用成为一个热门领域&#xff0c;在线藏药特产销售系统就是这其中的一员。 藏药产业在西藏…