文章 C++读取串口超时功能 介绍了使用 Boost::Asio 中提供的 serial_port 串口类进行串口 IO 操作. Boost::Asio 对串口等 IO 操作进行了很好地封装, 便于跨平台使用.
如果仅限于 Windows 平台编程, 则可以更方便地使用 Windows API 进行串口读写操作. 在 Windows 平台中, 读写串口和读写一个普通的文件没有很大差异, 具体看如下使用 Windows API 封装的串口类:
#include <Windows.h>
#include <spdlog/spdlog.h>
class SerialPort {
public:
SerialPort(const std::string& portname, const DWORD baudRate,
const BYTE stopBits, const BYTE byteSize, const BYTE parity,
const DWORD timeout_ms)
: m_portname(portname),
m_baudRate(baudRate),
m_byteSize(byteSize),
m_stopBits(stopBits),
m_parity(parity),
m_timeout_ms(timeout_ms) {
/* 打开串口 */
m_hSerial = CreateFile(portname.c_str(), GENERIC_READ | GENERIC_WRITE, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (m_hSerial == INVALID_HANDLE_VALUE) {
throw std::runtime_error("ERROR: CreateFile");
}
/* 设置串口参数 */
DCB dcbSerialParam = {0};
dcbSerialParam.DCBlength = sizeof(dcbSerialParam);
if (!GetCommState(m_hSerial, &dcbSerialParam)) {
CloseHandle(m_hSerial);
throw std::runtime_error("ERROR: GetCommState");
}
dcbSerialParam.BaudRate = m_baudRate;
dcbSerialParam.ByteSize = m_byteSize;
dcbSerialParam.StopBits = m_stopBits;
dcbSerialParam.Parity = m_parity;
if (!SetCommState(m_hSerial, &dcbSerialParam)) {
CloseHandle(m_hSerial);
throw std::runtime_error("ERROR: SetCommState");
}
/* 设置读写超时参数 */
COMMTIMEOUTS timeout = {0};
timeout.ReadIntervalTimeout = 0;
timeout.ReadTotalTimeoutConstant = m_timeout_ms;
timeout.ReadTotalTimeoutMultiplier = 0;
timeout.WriteTotalTimeoutConstant = m_timeout_ms;
timeout.WriteTotalTimeoutMultiplier = 0;
if (!SetCommTimeouts(m_hSerial, &timeout)) {
CloseHandle(m_hSerial);
throw std::runtime_error("ERROR: SetCommTimeouts");
}
}
~SerialPort() { CloseHandle(m_hSerial); }
/**
* @brief 从串口中读取一个字节
*
* @param ch 保存读取的字节数据
*
* @return true 读取成功
* @return false 读取失败
*/
bool read(char* ch) {
if (!ch) {
throw std::runtime_error("ERROR: nullptr");
}
DWORD dwRead = 0;
if (!ReadFile(m_hSerial, ch, 1, &dwRead, NULL)) {
return false;
}
return dwRead == 1;
}
/**
* @brief 向串口中写入1个字节
*
* @param ch 要写入的字节数据
*
* @return true 写入成功
* @return false 写入失败
*/
bool write(const char* ch) {
if (!ch) {
throw std::runtime_error("ERROR: nullptr");
}
DWORD dwWritten = 0;
if (!WriteFile(m_hSerial, ch, 1, &dwWritten, NULL)) {
return false;
}
return dwWritten == 1;
}
private:
HANDLE m_hSerial;
std::string m_portname;
DWORD m_baudRate;
BYTE m_stopBits;
BYTE m_parity;
BYTE m_byteSize;
DWORD m_timeout_ms;
};
int main() {
SerialPort sp("COM5", CBR_19200, ONESTOPBIT, 8, NOPARITY, 5000);
char x = 'x';
if (sp.write(&x)) {
spdlog::info("Write: {}", x);
} else {
spdlog::error("Write: TIMEOUT");
}
char ch = '\0';
if (sp.read(&ch)) {
spdlog::info("Read: {}", ch);
} else {
spdlog::error("Read: TIMEOUT");
}
return 0;
}
/*运行会输出类似如下结果:
[2024-12-25 20:56:51.866] [info] Write: x
[2024-12-25 20:56:56.878] [error] Read: TIMEOUT
或者
[2024-12-25 20:57:09.737] [info] Write: x
[2024-12-25 20:57:14.297] [info] Read: y
*/ CreateFile 函数用于打开一个串口并返回一个文件句柄 (handle), ReadFile 从文件句柄中读取数据, WriteFile 向文件句柄中写入数据. 串口使用 SetCommState 设置串口通讯参数, 使用 SetCommTimeouts 设置串口读写超时参数. 当然, 上面的示例只展示了如何同步对串口进行操作, 如果要进行异步操作, 相应的代码逻辑会复杂一些, 如果真的有需要建议直接使用 Boost::Asio 中的串口类提供的异步读写功能.
(完)