RMVL  2.4.0-dev
Robotic Manipulation and Vision Library
载入中...
搜索中...
未找到
串口通信模块

串口通信模块使用教程

作者
赵曦
日期
2025/09/25
版本
2.0

上一篇教程:进程间通信设施 —— IPC
下一篇教程:传输层设施 —— Socket


相关类 rm::SerialPortrm::async::SerialPort

1 简介

串口通信是一种通过串行数据传输进行通信的方式。它使用串行接口将数据以逐位的方式进行传输,常用于连接计算机与外部设备、嵌入式系统之间的数据传输。

串口通信一般涉及两个主要概念: 串口波特率

  • 串口:指的是通信中的物理接口,常见的有 RS-232RS-485UART 等标准。每个串口都有相应的引脚用于发送和接收数据以及控制信号。
  • 波特率:也称作数据传输速率,指的是每秒钟传输的位数。波特率决定了传输速度的快慢,通常使用常见的波特率如 9600、115200 等。

2 同步模式使用

2.1 初始化

rm::SerialPort 的构造函数原型如下

SerialPort(std::string_view device, BaudRate baud_rate, SerialReadMode read_mode = {});
参数 含义
device 设备名,在 Windows 上一般是 COMx,Linux 上一般为 ttyUSBxttyACMx
baud_rate 波特率
read_mode 读取模式,表示阻塞与非阻塞, rm::async::SerialPort 不提供该参数

2.2 数据 I/O

rm::SerialPort 提供了极其方便的串口读取、写入的接口。

读取函数原型
template <typename AggregateT>
bool read(uint8_t head, uint8_t tail, AggregateT &data); // (1) 指定头尾帧的读取结构化数据
template <typename AggregateT>
bool read(AggregateT &data); // (2) 读取结构化数据
bool read(std::string &data); // (3) 读取字符串数据
template <typename AggregateOrStringT>
SerialPort &operator>>(AggregateOrStringT &data); // (4) 串口读取操作符重载
写入函数原型
template <typename AggregateT>
bool write(const AggregateT &data); // (5) 写入结构化数据
bool write(std::string_view data); // (6) 写入字符串数据
template <typename AggregateOrStringT>
SerialPort &operator<<(const AggregateOrStringT &data); // (7) 串口写入操作符重载
参数
data读取、写入的数据
head帧头
tail帧尾

注解

(3) 和 (5) 带有 Python 接口

2.3 链路层协议

写数据(视觉端 → 电控端)

SerialPort 的通信协议一般就是指数据链路层的协议,采用封装成帧的方式,由于终端设备输出缓冲区可在写入数据前被清空,因此 rm::SerialPortwrite 方法不设置任何帧头帧尾。

读数据(电控端 → 视觉端)

而输入缓冲区则会持续收到来自下位机的数据,因此 rm::SerialPortread 方法提供了两个 uint8_t 类型帧头帧尾的传入参数,下位机发送的数据需要在有效数据包前后设置两个 uint8_t 的校验位,帧头和帧尾具体的数据可自行设置。

需要注意的是,在读取数据前需要留意

  • 协议(结构体)内容
  • 内存对齐规则,关于内存对齐可使用 #pragma pack 宏或使用 C++ 标准的 alignas 关键字
  • 头尾帧是否一致

2.4 用法

using namespace rm;
struct Data {
uint8_t head[4]{};
float x{};
float y{};
};
int main() {
// 创建串口对象,以非阻塞模式打开 /dev/ttyUSB0,设置波特率为 115200
auto serial = SerialPort("/dev/ttyUSB0", BaudRate::BR_115200, SerialReadMode::NONBLOCK);
// 1. 字符串读写
std::string str{};
bool success = serial.read(str);
success = serial.write("test string");
// 2. 聚合体读写
Data data{};
success = serial.read(data);
Data send_data = {1, 2, 3, 4, 1.1f, 2.2f};
success = serial.write(send_data);
// 3. 带帧头帧尾的聚合体读取
// 适合于同步非阻塞模式下大量数据的读取处理
success = serial.read(0x44, 0x55, data);
}
同步模式串行接口通信库
定义 serial.hpp:50
@ NONBLOCK
同步非阻塞模式,即读取数据时不会等待,如果没有数据到来则立即返回 -1
定义 serial.hpp:46
@ BR_115200
波特率 115200
定义 serial.hpp:40
定义 str.hpp:19
定义 datastruct.hpp:20
同步/异步的数据链路层串口通信框架

3 异步模式使用

rm::async::SerialPort 需要配合 rm::async::IOContext 使用,目前,异步串口仅提供了字符串读写的功能

SerialReadAwaiter read();
SerialWriteAwaiter write(std::string_view);

其中 SerialReadAwaiter 为串口读等待器,相比于 rm::async::AsyncReadAwaiter 增加了读缓冲区清理的功能。同理,SerialWriteAwaiter 增加了写缓冲区清理的功能。用法如下:

using namespace rm;
// 仅支持字符串读写
std::string str = co_await serial.read();
bool success = co_await serial.write("test string");
}
int main() {
async::IOContext io_context{};
async::SerialPort serial(io_context, "/dev/ttyUSB0", BaudRate::BR_115200);
co_spawn(io_context, session, std::move(serial));
io_context.run();
}
异步 I/O 执行上下文,负责管理 IO 事件循环和协程任务的调度
定义 async.hpp:177
void run()
启用事件循环
异步串行接口通信库,仅支持读写字符串
定义 serial.hpp:182
SerialWriteAwaiter write(std::string_view data)
异步写入串口数据
定义 serial.hpp:258
SerialReadAwaiter read()
异步读取串口数据
定义 serial.hpp:226
保有 rm::async::Promise 的异步协程任务
定义 async.hpp:146