1
This commit is contained in:
82
armor/include/CSerialPort/SerialPort.h
Normal file
82
armor/include/CSerialPort/SerialPort.h
Normal 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
|
||||
27
armor/include/CSerialPort/SerialPortInfo.h
Normal file
27
armor/include/CSerialPort/SerialPortInfo.h
Normal 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
|
||||
207
armor/src/CSerialPort/SerialPort.cpp
Normal file
207
armor/src/CSerialPort/SerialPort.cpp
Normal 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
|
||||
35
armor/src/CSerialPort/SerialPortInfo.cpp
Normal file
35
armor/src/CSerialPort/SerialPortInfo.cpp
Normal 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
|
||||
Reference in New Issue
Block a user