CSerialPort 是一个使用现代 C++17 编写的跨平台串口通信库,支持 Windows 和 Linux 操作系统。本库提供了简洁易用的 API,支持同步和异步操作,以及回调机制。
- 现代 C++17 - 使用最新的 C++ 特性,如
std::optional、std::variant、智能指针等 - 跨平台支持 - 同时支持 Windows 和 Linux
- 线程安全 - 内置互斥锁保护,原子操作统计信息,支持多线程环境
- 异步操作 - 支持
std::future异步读写 - 回调机制 - 支持数据接收、事件和错误回调
- RAII 资源管理 - 自动管理串口资源,防止泄漏
- 统计信息 - 线程安全的收发字节数和错误统计
- 灵活配置 - 支持各种波特率、数据位、停止位、校验位和流控制
- 控制线操作 - 支持 DTR/RTS 设置和 CTS/DSR/CD/RI 读取
- Break 信号 - 支持发送 Break 信号
| 版本 | 日期 | 说明 |
|---|---|---|
| 3.0.0 | 2026-01-01 | 使用现代C++17重写,支持跨平台 |
| 2.0.0 | 2014-01-10 | 添加串口枚举功能 |
| 1.0.0 | 1997-09-15 | 初始版本 |
- C++17 兼容的编译器
- GCC 7+
- Clang 5+
- MSVC 2017+
- CMake 3.14+
- Windows: Windows SDK
- Linux: 无额外依赖
mkdir build && cd build
cmake .. -DCSERIALPORT_BUILD_EXAMPLES=ON -DCSERIALPORT_BUILD_TESTS=ON
cmake --build .
# 运行测试
cmake --build . --target run_tests| 选项 | 默认值 | 说明 |
|---|---|---|
CSERIALPORT_BUILD_SHARED |
ON | 构建动态库 |
CSERIALPORT_BUILD_STATIC |
ON | 构建静态库 |
CSERIALPORT_BUILD_EXAMPLES |
ON | 构建示例程序 |
CSERIALPORT_BUILD_TESTS |
OFF | 构建单元测试 |
#include "SerialPort.h"
#include <iostream>
int main() {
// 创建串口对象
csp::SerialPort port;
// 打开串口(Windows: "COM1", Linux: "/dev/ttyUSB0")
auto result = port.open("COM1", csp::SerialConfig::config_115200_8N1());
if (!result) {
std::cerr << "打开串口失败: " << result.errorMessage() << std::endl;
return 1;
}
// 发送数据
std::string message = "Hello, Serial Port!";
auto writeResult = port.write(message);
if (writeResult) {
std::cout << "发送了 " << writeResult.value() << " 字节" << std::endl;
}
// 接收数据
auto readResult = port.read(100);
if (readResult) {
auto& data = readResult.value();
std::cout << "接收了 " << data.size() << " 字节" << std::endl;
}
// 串口会在析构时自动关闭
return 0;
}所有类型和函数都在 csp 命名空间中。
enum class BaudRate : uint32_t {
BR_110 = 110,
BR_300 = 300,
BR_600 = 600,
BR_1200 = 1200,
BR_2400 = 2400,
BR_4800 = 4800,
BR_9600 = 9600,
BR_14400 = 14400,
BR_19200 = 19200,
BR_38400 = 38400,
BR_57600 = 57600,
BR_115200 = 115200,
BR_230400 = 230400,
BR_460800 = 460800,
BR_921600 = 921600,
Custom = 0 // 自定义波特率
};enum class DataBits : uint8_t {
Five = 5,
Six = 6,
Seven = 7,
Eight = 8
};enum class StopBits : uint8_t {
One = 1,
OneHalf = 2, // 仅Windows支持
Two = 3
};enum class Parity : uint8_t {
None = 0,
Odd = 1,
Even = 2,
Mark = 3, // 仅Windows支持
Space = 4 // 仅Windows支持
};enum class FlowControl : uint8_t {
None = 0,
Hardware = 1, // RTS/CTS
Software = 2 // XON/XOFF
};struct SerialConfig {
BaudRate baudRate = BaudRate::BR_9600;
DataBits dataBits = DataBits::Eight;
StopBits stopBits = StopBits::One;
Parity parity = Parity::None;
FlowControl flowControl = FlowControl::None;
uint32_t customBaudRate = 0;
Duration readTimeout = Duration(1000);
Duration writeTimeout = Duration(1000);
size_t readBufferSize = 4096;
size_t writeBufferSize = 4096;
// 便捷工厂方法
static SerialConfig defaultConfig();
static SerialConfig config_9600_8N1();
static SerialConfig config_115200_8N1();
};SerialPort(); // 默认构造注意: 使用
open()方法打开串口,构造函数不会自动打开串口。
// 枚举系统中的所有串口
static std::vector<PortInfo> enumerate();
// 检查串口是否存在
static bool exists(const std::string& portName);
// 获取库版本
static std::string version();VoidResult open(const std::string& portName, const SerialConfig& config);
VoidResult close();
bool isOpen() const;SerialConfig config() const;
VoidResult setConfig(const SerialConfig& config);
VoidResult setBaudRate(BaudRate baudRate);
VoidResult setDataBits(DataBits dataBits);
VoidResult setStopBits(StopBits stopBits);
VoidResult setParity(Parity parity);
VoidResult setFlowControl(FlowControl flowControl);
VoidResult setReadTimeout(Duration timeout);
VoidResult setWriteTimeout(Duration timeout);// 读取最多 maxBytes 字节
Result<ByteBuffer> read(size_t maxBytes, std::optional<Duration> timeout = std::nullopt);
// 精确读取 exactBytes 字节
Result<ByteBuffer> readExact(size_t exactBytes, std::optional<Duration> timeout = std::nullopt);
// 读取直到遇到分隔符
Result<ByteBuffer> readUntil(Byte delimiter, size_t maxBytes = 4096,
std::optional<Duration> timeout = std::nullopt);
// 读取一行
Result<std::string> readLine(size_t maxBytes = 4096,
std::optional<Duration> timeout = std::nullopt);Result<size_t> write(const Byte* data, size_t size, std::optional<Duration> timeout = std::nullopt);
Result<size_t> write(const ByteBuffer& data, std::optional<Duration> timeout = std::nullopt);
Result<size_t> write(const std::string& str, std::optional<Duration> timeout = std::nullopt);
Result<size_t> writeLine(const std::string& line, std::optional<Duration> timeout = std::nullopt);std::future<Result<ByteBuffer>> readAsync(size_t maxBytes);
std::future<Result<size_t>> writeAsync(ByteBuffer data);
std::future<Result<size_t>> writeAsync(std::string str);void setDataCallback(DataCallback callback);
void setEventCallback(EventCallback callback);
void setErrorCallback(ErrorCallback callback);
VoidResult startAsyncReceive();
void stopAsyncReceive();
bool isAsyncReceiving() const;Result<size_t> available() const; // 获取可读字节数
VoidResult flushInput(); // 清空接收缓冲区
VoidResult flushOutput(); // 清空发送缓冲区
VoidResult flush(); // 清空所有缓冲区// 设置控制线
VoidResult setDTR(bool state);
VoidResult setRTS(bool state);
// 读取控制线状态
Result<bool> getCTS() const;
Result<bool> getDSR() const;
Result<bool> getCD() const;
Result<bool> getRI() const;
// 发送 Break 信号
VoidResult sendBreak(Duration duration = Duration(250));std::string portName() const;
PortStatistics statistics() const;
void resetStatistics();struct PortStatistics {
std::atomic<uint64_t> bytesReceived{0};
std::atomic<uint64_t> bytesSent{0};
std::atomic<uint64_t> readErrors{0};
std::atomic<uint64_t> writeErrors{0};
// 便捷获取方法
uint64_t getBytesReceived() const noexcept;
uint64_t getBytesSent() const noexcept;
uint64_t getReadErrors() const noexcept;
uint64_t getWriteErrors() const noexcept;
// 时间相关方法
Duration getUptime() const noexcept; // 获取运行时长
Duration getLastActivityOffset() const noexcept; // 获取最后活动时间偏移
Duration getTimeSinceLastActivity() const noexcept; // 获取自最后活动以来的时间
void reset() noexcept;
};#include "SerialPort.h"
#include <iostream>
int main() {
auto ports = csp::SerialPort::enumerate();
std::cout << "发现 " << ports.size() << " 个串口:" << std::endl;
for (const auto& port : ports) {
std::cout << " " << port.portName;
if (!port.description.empty()) {
std::cout << " - " << port.description;
}
std::cout << (port.isAvailable ? " (可用)" : " (占用)") << std::endl;
}
return 0;
}#include "SerialPort.h"
#include <iostream>
#include <thread>
#include <chrono>
int main() {
csp::SerialPort port;
if (!port.open("COM1", csp::SerialConfig::config_115200_8N1())) {
std::cerr << "打开串口失败" << std::endl;
return 1;
}
// 设置数据接收回调
port.setDataCallback([](const csp::Byte* data, size_t size) {
std::cout << "收到 " << size << " 字节: ";
for (size_t i = 0; i < size; ++i) {
std::cout << std::hex << static_cast<int>(data[i]) << " ";
}
std::cout << std::endl;
});
// 设置错误回调
port.setErrorCallback([](csp::ErrorCode error, const std::string& message) {
std::cerr << "错误: " << message << std::endl;
});
// 启动异步接收
port.startAsyncReceive();
// 主线程等待
std::cout << "按 Enter 键退出..." << std::endl;
std::cin.get();
// 停止异步接收
port.stopAsyncReceive();
return 0;
}#include "SerialPort.h"
#include <iostream>
#include <future>
int main() {
csp::SerialPort port;
if (!port.open("COM1", csp::SerialConfig::config_115200_8N1())) {
return 1;
}
// 异步发送
auto writeFuture = port.writeAsync("Hello, World!");
// 异步接收
auto readFuture = port.readAsync(100);
// 等待发送完成
auto writeResult = writeFuture.get();
if (writeResult) {
std::cout << "发送了 " << writeResult.value() << " 字节" << std::endl;
}
// 等待接收完成
auto readResult = readFuture.get();
if (readResult) {
std::cout << "接收了 " << readResult.value().size() << " 字节" << std::endl;
}
return 0;
}#include "SerialPort.h"
#include <iostream>
int main() {
csp::SerialPort port;
if (!port.open("COM1", csp::SerialConfig::config_115200_8N1())) {
return 1;
}
// 设置 DTR 和 RTS
port.setDTR(true);
port.setRTS(true);
// 读取控制线状态
auto cts = port.getCTS();
auto dsr = port.getDSR();
auto cd = port.getCD();
auto ri = port.getRI();
if (cts) std::cout << "CTS: " << (cts.value() ? "高" : "低") << std::endl;
if (dsr) std::cout << "DSR: " << (dsr.value() ? "高" : "低") << std::endl;
if (cd) std::cout << "CD: " << (cd.value() ? "高" : "低") << std::endl;
if (ri) std::cout << "RI: " << (ri.value() ? "高" : "低") << std::endl;
// 发送 Break 信号
port.sendBreak(csp::Duration(500)); // 500ms
return 0;
}#include "SerialPort.h"
int main() {
csp::SerialPort port;
// 创建自定义配置
csp::SerialConfig config;
config.baudRate = csp::BaudRate::BR_38400;
config.dataBits = csp::DataBits::Seven;
config.stopBits = csp::StopBits::Two;
config.parity = csp::Parity::Even;
config.flowControl = csp::FlowControl::Hardware;
config.readTimeout = csp::Duration(2000); // 2秒超时
config.writeTimeout = csp::Duration(2000);
if (!port.open("COM1", config)) {
return 1;
}
// 动态修改配置
port.setBaudRate(csp::BaudRate::BR_115200);
return 0;
}本库使用 Result<T> 类型进行错误处理,类似于 Rust 的 Result 或 C++23 的 std::expected。
auto result = port.read(100);
if (result) {
// 成功
auto& data = result.value();
// 或使用 *result
} else {
// 失败
std::cerr << "错误码: " << static_cast<int>(result.error()) << std::endl;
std::cerr << "错误信息: " << result.errorMessage() << std::endl;
}
// 使用 valueOr 提供默认值
auto data = result.valueOr(csp::ByteBuffer{});| 错误码 | 说明 |
|---|---|
| Success | 成功 |
| InvalidParameter | 无效参数 |
| NotOpen | 串口未打开 |
| AlreadyOpen | 串口已打开 |
| Timeout | 操作超时 |
| OpenFailed | 打开失败 |
| PortNotFound | 串口不存在 |
| PermissionDenied | 权限不足 |
| PortBusy | 串口被占用 |
| ConfigFailed | 配置失败 |
| ReadFailed | 读取失败 |
| WriteFailed | 写入失败 |
- 串口名称格式:
COM1,COM2, ...COM256 - 支持 1.5 停止位
- 支持 Mark 和 Space 校验
- 使用 SetupAPI 高效枚举串口(获取设备描述和硬件ID)
- 使用 Overlapped I/O 实现异步操作
- 串口名称格式:
/dev/ttyS0,/dev/ttyUSB0,/dev/ttyACM0等 - 不支持 1.5 停止位(会返回
InvalidParameter错误) - 不支持 Mark 和 Space 校验(会返回
InvalidParameter错误) - 使用 select() 实现超时控制
- 自定义波特率需要系统支持
- 所有公共方法都是线程安全的
- 可以在多个线程中同时调用读写方法
PortStatistics使用原子操作,可安全地在多线程中访问- 回调函数在内部线程中执行,请注意同步
编译后的示例程序位于 build/bin/examples/ 目录:
# 基本使用示例
./example_basic [端口名] [波特率]
./example_basic COM1 115200
# 串口枚举示例
./example_enumerate [-v]
# 异步接收示例
./example_async [端口名] [波特率]# 构建测试
cmake .. -DCSERIALPORT_BUILD_TESTS=ON
cmake --build .
# 运行测试
cmake --build . --target run_tests
# 或
ctest --output-on-failure本项目采用 MIT 许可证。详见 LICENSE 文件。
欢迎提交 Issue 和 Pull Request!
- 作者:CSerialPort Team
- 博客:https://blog.csdn.net/liquanhai