This commit is contained in:
2026-03-28 04:50:38 +08:00
parent 28827fe008
commit f40c37c136
4 changed files with 351 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
#ifndef CSERIALPORT_SERIALPORT_H
#define CSERIALPORT_SERIALPORT_H
#include <cstdint>
#include <cstring>
namespace itas109 {
enum class Parity {
ParityNone = 0,
ParityOdd = 1,
ParityEven = 2,
};
enum class DataBits {
DataBits5 = 5,
DataBits6 = 6,
DataBits7 = 7,
DataBits8 = 8,
};
enum class StopBits {
StopOne = 1,
StopOneAndHalf = 3,
StopTwo = 2,
};
enum class FlowControl {
FlowNone = 0,
FlowHardware = 1,
FlowSoftware = 2,
};
class CSerialPort {
public:
CSerialPort();
~CSerialPort();
// 初始化串口参数(不打开)
void init(const char* portName,
int baudRate = 115200,
Parity parity = Parity::ParityNone,
DataBits dataBits = DataBits::DataBits8,
StopBits stopBits = StopBits::StopOne,
FlowControl flowControl = FlowControl::FlowNone,
int readTimeoutMs = 1000);
// 打开串口,成功返回 true
bool open();
// 关闭串口
void close();
// 写数据,返回实际写入字节数(失败返回 -1
int writeData(const uint8_t* data, size_t length);
// 读数据,返回实际读取字节数(失败返回 -1
int readData(uint8_t* buffer, size_t maxLength);
// 返回最近一次错误码
int getLastError() const { return m_lastError; }
private:
char m_portName[256];
int m_baudRate;
Parity m_parity;
DataBits m_dataBits;
StopBits m_stopBits;
FlowControl m_flowControl;
int m_readTimeoutMs;
int m_fd; // 文件描述符
int m_lastError;
bool m_isOpen;
// termios 波特率映射
static int toBaudRate(int baud);
};
} // namespace itas109
#endif // CSERIALPORT_SERIALPORT_H

View File

@@ -0,0 +1,27 @@
#ifndef CSERIALPORT_SERIALPORTINFO_H
#define CSERIALPORT_SERIALPORTINFO_H
#include <vector>
#include <cstring>
namespace itas109 {
struct SerialPortInfo {
char portName[256];
char description[256];
SerialPortInfo() {
std::memset(portName, 0, sizeof(portName));
std::memset(description, 0, sizeof(description));
}
};
class CSerialPortInfo {
public:
// 枚举系统上所有可用串口
static std::vector<SerialPortInfo> availablePortInfos();
};
} // namespace itas109
#endif // CSERIALPORT_SERIALPORTINFO_H

View File

@@ -0,0 +1,207 @@
#include "CSerialPort/SerialPort.h"
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <cerrno>
#include <cstring>
namespace itas109 {
CSerialPort::CSerialPort()
: m_baudRate(115200)
, m_parity(Parity::ParityNone)
, m_dataBits(DataBits::DataBits8)
, m_stopBits(StopBits::StopOne)
, m_flowControl(FlowControl::FlowNone)
, m_readTimeoutMs(1000)
, m_fd(-1)
, m_lastError(0)
, m_isOpen(false)
{
std::memset(m_portName, 0, sizeof(m_portName));
}
CSerialPort::~CSerialPort() {
close();
}
void CSerialPort::init(const char* portName,
int baudRate,
Parity parity,
DataBits dataBits,
StopBits stopBits,
FlowControl flowControl,
int readTimeoutMs)
{
std::strncpy(m_portName, portName, sizeof(m_portName) - 1);
m_portName[sizeof(m_portName) - 1] = '\0';
m_baudRate = baudRate;
m_parity = parity;
m_dataBits = dataBits;
m_stopBits = stopBits;
m_flowControl = flowControl;
m_readTimeoutMs = readTimeoutMs;
}
bool CSerialPort::open() {
if (m_isOpen) close();
m_fd = ::open(m_portName, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (m_fd < 0) {
m_lastError = errno;
return false;
}
struct termios tty;
std::memset(&tty, 0, sizeof(tty));
if (tcgetattr(m_fd, &tty) != 0) {
m_lastError = errno;
::close(m_fd);
m_fd = -1;
return false;
}
// 波特率
speed_t speed = static_cast<speed_t>(toBaudRate(m_baudRate));
cfsetispeed(&tty, speed);
cfsetospeed(&tty, speed);
// 数据位
tty.c_cflag &= ~CSIZE;
switch (m_dataBits) {
case DataBits::DataBits5: tty.c_cflag |= CS5; break;
case DataBits::DataBits6: tty.c_cflag |= CS6; break;
case DataBits::DataBits7: tty.c_cflag |= CS7; break;
case DataBits::DataBits8:
default: tty.c_cflag |= CS8; break;
}
// 停止位
if (m_stopBits == StopBits::StopTwo)
tty.c_cflag |= CSTOPB;
else
tty.c_cflag &= ~CSTOPB;
// 校验位
switch (m_parity) {
case Parity::ParityOdd:
tty.c_cflag |= PARENB;
tty.c_cflag |= PARODD;
tty.c_iflag |= (INPCK | ISTRIP);
break;
case Parity::ParityEven:
tty.c_cflag |= PARENB;
tty.c_cflag &= ~PARODD;
tty.c_iflag |= (INPCK | ISTRIP);
break;
case Parity::ParityNone:
default:
tty.c_cflag &= ~PARENB;
tty.c_iflag &= ~(INPCK | ISTRIP);
break;
}
// 流控
if (m_flowControl == FlowControl::FlowHardware)
tty.c_cflag |= CRTSCTS;
else
tty.c_cflag &= ~CRTSCTS;
if (m_flowControl == FlowControl::FlowSoftware)
tty.c_iflag |= (IXON | IXOFF | IXANY);
else
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// 本地模式 & 输入模式raw
tty.c_cflag |= (CREAD | CLOCAL);
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_oflag &= ~OPOST;
// 非阻塞读,超时由 select 控制
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
if (tcsetattr(m_fd, TCSANOW, &tty) != 0) {
m_lastError = errno;
::close(m_fd);
m_fd = -1;
return false;
}
tcflush(m_fd, TCIOFLUSH);
m_isOpen = true;
m_lastError = 0;
return true;
}
void CSerialPort::close() {
if (m_fd >= 0) {
::close(m_fd);
m_fd = -1;
}
m_isOpen = false;
}
int CSerialPort::writeData(const uint8_t* data, size_t length) {
if (!m_isOpen || m_fd < 0) {
m_lastError = EBADF;
return -1;
}
ssize_t written = ::write(m_fd, data, length);
if (written < 0) {
m_lastError = errno;
return -1;
}
tcdrain(m_fd); // 等待发送完成
return static_cast<int>(written);
}
int CSerialPort::readData(uint8_t* buffer, size_t maxLength) {
if (!m_isOpen || m_fd < 0) {
m_lastError = EBADF;
return -1;
}
// 用 select 实现超时
struct timeval tv;
tv.tv_sec = m_readTimeoutMs / 1000;
tv.tv_usec = (m_readTimeoutMs % 1000) * 1000;
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_fd, &fds);
int ret = select(m_fd + 1, &fds, nullptr, nullptr, &tv);
if (ret <= 0) {
// 超时或错误
m_lastError = (ret == 0) ? ETIME : errno;
return 0;
}
ssize_t bytesRead = ::read(m_fd, buffer, maxLength);
if (bytesRead < 0) {
m_lastError = errno;
return -1;
}
m_lastError = 0;
return static_cast<int>(bytesRead);
}
int CSerialPort::toBaudRate(int baud) {
switch (baud) {
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 921600: return B921600;
default: return B115200;
}
}
} // namespace itas109

View File

@@ -0,0 +1,35 @@
#include "CSerialPort/SerialPortInfo.h"
#include <dirent.h>
#include <cstring>
#include <string>
namespace itas109 {
std::vector<SerialPortInfo> CSerialPortInfo::availablePortInfos() {
std::vector<SerialPortInfo> result;
// 扫描 /dev 目录,找 ttyUSB / ttyCH341USB / ttyACM / ttyS 等设备
DIR* dir = opendir("/dev");
if (!dir) return result;
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
const char* name = entry->d_name;
if (std::strstr(name, "ttyUSB") != nullptr ||
std::strstr(name, "ttyCH341") != nullptr ||
std::strstr(name, "ttyACM") != nullptr)
{
SerialPortInfo info;
std::string fullPath = std::string("/dev/") + name;
std::strncpy(info.portName, fullPath.c_str(), sizeof(info.portName) - 1);
std::strncpy(info.description, name, sizeof(info.description) - 1);
result.push_back(info);
}
}
closedir(dir);
return result;
}
} // namespace itas109