From daf8bb058a671eb8a889e3aa46f2017c7af71777 Mon Sep 17 00:00:00 2001 From: zhaoxi <535394140@qq.com> Date: Thu, 21 Nov 2024 13:21:54 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20=E4=B8=B2=E5=8F=A3=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E5=A2=9E=E5=8A=A0=E9=98=BB=E5=A1=9E=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=B8=8D=E5=8C=85=E5=90=AB=E5=A4=B4=E5=B0=BE=E5=B8=A7=E7=9A=84?= =?UTF-8?q?=20IO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/core/include/rmvl/core/io.hpp | 75 +++++++++++++--- modules/core/src/io_impl.hpp | 15 ++-- modules/core/src/serial.cpp | 120 ++++++++++++++++++-------- modules/light/include/rmvl/light.hpp | 3 +- 4 files changed, 153 insertions(+), 60 deletions(-) diff --git a/modules/core/include/rmvl/core/io.hpp b/modules/core/include/rmvl/core/io.hpp index dcab8a01..4f7d36a3 100644 --- a/modules/core/include/rmvl/core/io.hpp +++ b/modules/core/include/rmvl/core/io.hpp @@ -12,6 +12,7 @@ #pragma once #include +#include #include #include #include @@ -140,11 +141,28 @@ void readCorners(std::istream &in, std::vector> ///////////////////////////////////// 串口通信 ///////////////////////////////////// -enum class BaudRate +enum class BaudRate : uint8_t { + BR_1200, //!< 波特率 1200 + BR_4800, //!< 波特率 4800 + BR_9600, //!< 波特率 9600 + BR_19200, //!< 波特率 19200 BR_57600, //!< 波特率 57600 BR_115200, //!< 波特率 115200 - BR_230400, //!< 波特率 230400 +}; + +//! 串口数据读取模式 +enum class SPReadMode : uint8_t +{ + BLOCK, //!< 阻塞模式,即读取数据时会一直等待直到有数据到来 + NONBLOCK //!< 非阻塞模式,即读取数据时不会等待,如果没有数据到来则立即返回 `-1` +}; + +//! 串口通信模式 +struct RMVL_EXPORTS SerialPortMode +{ + BaudRate baud_rate{BaudRate::BR_115200}; //!< 波特率 + SPReadMode read_mode{SPReadMode::BLOCK}; //!< 读取模式 }; //! 串行接口通信库 @@ -155,9 +173,9 @@ class RMVL_EXPORTS SerialPort * @brief 构造新 SerialPort 对象 * * @param[in] device 设备名 - * @param[in] baud_rate 波特率,一般为 `BaudRate::BR_115200` + * @param[in] mode 串口通信模式 */ - SerialPort(std::string_view device, BaudRate baud_rate); + SerialPort(std::string_view device, SerialPortMode mode = {}); SerialPort(const SerialPort &) = delete; SerialPort(SerialPort &&) = default; @@ -165,26 +183,26 @@ class RMVL_EXPORTS SerialPort SerialPort &operator=(SerialPort &&) = default; /** - * @brief 从串口读取数据到结构体 + * @brief 从串口读取数据到聚合体中 * @note 每次读取后会清空缓冲区 * - * @tparam Tp 读取到结构体的类型 + * @tparam Tp 读取到聚合体的类型 * @param[in] head_flag 头帧 * @param[in] tail_flag 尾帧 - * @param[out] data 读取的结构体数据 + * @param[out] data 读取的聚合体数据 * @return 是否读取成功 */ - template + template >> inline bool read(unsigned char head_flag, unsigned char tail_flag, Tp &data) { - bool retval{false}; + bool retval{}; constexpr int LENGTH = 512, SIZE = sizeof(Tp); unsigned char buffer[LENGTH] = {0}; auto len_result = fdread(buffer, LENGTH); for (long int i = 0; (i + SIZE + 1) < len_result; i++) if (buffer[i] == head_flag && buffer[i + SIZE + 1] == tail_flag) { - auto p = memcpy(&data, &buffer[i + 1], SIZE); + auto p = std::memcpy(&data, &buffer[i + 1], SIZE); if (p == &data) retval = true; } @@ -192,16 +210,45 @@ class RMVL_EXPORTS SerialPort } /** - * @brief 结构体数据写入串口 + * @brief 不带头尾标志的数据读取,从串口读取数据到聚合体中 + * + * @tparam Tp 读取到聚合体的类型 + * @param[out] data 读取的聚合体数据 + * @return 是否读取成功 + */ + template >> + inline bool read(Tp &data) + { + bool retval{}; + constexpr int LENGTH = 512, SIZE = sizeof(Tp); + unsigned char buffer[LENGTH] = {0}; + auto len_result = fdread(buffer, LENGTH); + if (len_result >= SIZE) + { + auto p = std::memcpy(&data, buffer, SIZE); + if (p == &data) + retval = true; + } + return retval; + } + + template >> + inline SerialPort &operator>>(Tp &data) { return (this->read(data), *this); } + + /** + * @brief 数据写入串口 * @note 每次写入前会清空缓冲区 * - * @tparam Tp 写入结构体的类型 - * @param[in] data 要写入的结构体 + * @tparam Tp 写入聚合体的类型 + * @param[in] data 要写入的聚合体 * @return 是否写入成功 */ - template + template >> inline bool write(const Tp &data) { return (sizeof(data) == fdwrite(&data, sizeof(data))); } + template >> + inline SerialPort &operator<<(const Tp &data) { return (this->write(data), *this); } + //! 串口是否打开 bool isOpened() const; diff --git a/modules/core/src/io_impl.hpp b/modules/core/src/io_impl.hpp index 32d029e0..6601519b 100644 --- a/modules/core/src/io_impl.hpp +++ b/modules/core/src/io_impl.hpp @@ -13,8 +13,6 @@ #ifdef _WIN32 #include -#else -#include #endif #include "rmvl/core/io.hpp" @@ -22,12 +20,10 @@ namespace rm { -int getBaudRate(BaudRate baud_rate); - class SerialPort::Impl { public: - explicit Impl(std::string_view device, BaudRate baud_rate) : _device(device), _baud_rate(getBaudRate(baud_rate)) { open(); } + explicit Impl(std::string_view device, SerialPortMode mode = {}) : _device(device), _mode(mode) { open(); } ~Impl() { close(); } @@ -45,12 +41,11 @@ class SerialPort::Impl #ifdef _WIN32 HANDLE _handle{INVALID_HANDLE_VALUE}; //!< 文件句柄 #else - int _fd{-1}; //!< 文件描述符 - termios _option; //!< 终端控制 + int _fd{-1}; //!< 文件描述符 #endif - bool _is_open{}; //!< 串口打开标志位 - std::string _device; //!< 设备名 - int _baud_rate{}; //!< 波特率 + bool _is_open{}; //!< 串口打开标志位 + std::string _device; //!< 设备名 + SerialPortMode _mode; //!< 串口通信模式 }; class PipeServer::Impl diff --git a/modules/core/src/serial.cpp b/modules/core/src/serial.cpp index 83c18a70..b4b7b578 100755 --- a/modules/core/src/serial.cpp +++ b/modules/core/src/serial.cpp @@ -16,6 +16,7 @@ #ifndef _WIN32 #include #include +#include #include #endif @@ -24,36 +25,44 @@ namespace rm RMVL_IMPL_DEF(SerialPort) -int getBaudRate(BaudRate baud_rate) +static unsigned int getBaudRate(BaudRate baud_rate) { #ifdef _WIN32 switch (baud_rate) { + case BaudRate::BR_1200: + return 1200; + case BaudRate::BR_4800: + return 4800; + case BaudRate::BR_9600: + return 9600; case BaudRate::BR_57600: return 57600; case BaudRate::BR_115200: return 115200; - case BaudRate::BR_230400: - return 230400; default: return 115200; } #else switch (baud_rate) { + case BaudRate::BR_1200: + return B1200; + case BaudRate::BR_4800: + return B4800; + case BaudRate::BR_9600: + return B9600; case BaudRate::BR_57600: return B57600; case BaudRate::BR_115200: return B115200; - case BaudRate::BR_230400: - return B230400; default: return B115200; } #endif } -SerialPort::SerialPort(std::string_view device, BaudRate baud_rate) : _impl(new Impl(device, baud_rate)) {} +SerialPort::SerialPort(std::string_view device, SerialPortMode mode) : _impl(new Impl(device, mode)) {} bool SerialPort::isOpened() const { return _impl->isOpened(); } long int SerialPort::fdwrite(const void *data, size_t length) { return _impl->fdwrite(data, length); } long int SerialPort::fdread(void *data, size_t len) { return _impl->fdread(data, len); } @@ -61,23 +70,57 @@ long int SerialPort::fdread(void *data, size_t len) { return _impl->fdread(data, #ifdef _WIN32 void SerialPort::Impl::open() { - _is_open = false; INFO_("Opening the serial port: %s", _device.c_str()); - _handle = CreateFileA(_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); - + _handle = CreateFileA( + _device.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); if (_handle == INVALID_HANDLE_VALUE) { ERROR_("Failed to open the serial port."); + _is_open = false; return; } - DCB dcb; + COMMTIMEOUTS timeouts{}; + if (_mode.read_mode == SPReadMode::BLOCK) + { + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + } + else + { + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + } + timeouts.WriteTotalTimeoutConstant = 1; + timeouts.WriteTotalTimeoutMultiplier = 1; + if (!SetCommTimeouts(_handle, &timeouts)) + { + WARNING_("Failed to set the serial port timeout."); + _is_open = false; + return; + } + + DCB dcb{}; GetCommState(_handle, &dcb); - dcb.BaudRate = _baud_rate; - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - SetCommState(_handle, &dcb); + DWORD bps = getBaudRate(_mode.baud_rate); + dcb.BaudRate = bps; // 波特率 + dcb.ByteSize = 8; // 数据位 + dcb.Parity = NOPARITY; // 无校验 + dcb.StopBits = ONESTOPBIT; // 1 位停止位 + if (!SetCommState(_handle, &dcb)) + { + WARNING_("Failed to set the serial port state."); + _is_open = false; + return; + } _is_open = true; } @@ -89,14 +132,14 @@ void SerialPort::Impl::close() _is_open = false; } -long int SerialPort::Impl::fdwrite(void *data, std::size_t len) +long int SerialPort::Impl::fdwrite(const void *data, std::size_t len) { DWORD len_result{}; if (_is_open) { if (!WriteFile(_handle, data, static_cast(len), &len_result, nullptr)) { - WARNING_("Unable to write to serial port, error code: %d", ::GetLastError()); + WARNING_("Unable to write to serial port, error code: %ld", ::GetLastError()); open(); } else @@ -114,7 +157,7 @@ long int SerialPort::Impl::fdread(void *data, std::size_t len) { if (!ReadFile(_handle, data, static_cast(len), &len_result, nullptr)) { - WARNING_("The serial port cannot be read, error code: %d, restart...", ::GetLastError()); + WARNING_("The serial port cannot be read, error code: %ld, restart...", ::GetLastError()); open(); } } @@ -128,33 +171,40 @@ long int SerialPort::Impl::fdread(void *data, std::size_t len) #else void SerialPort::Impl::open() { - _is_open = false; INFO_("Opening the serial port: %s", _device.c_str()); - _fd = ::open(_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); // 非堵塞情况 - + _fd = ::open(_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); if (_fd == -1) { ERROR_("Failed to open the serial port."); + _is_open = false; return; } - tcgetattr(_fd, &_option); + if (_mode.read_mode == SPReadMode::BLOCK) + { + // 清除 O_NDELAY 标志,设置为阻塞模式 + int flags = fcntl(_fd, F_GETFL, 0); + flags &= ~O_NONBLOCK; + fcntl(_fd, F_SETFL, flags); + } + + termios option; + tcgetattr(_fd, &option); // 修改所获得的参数 - _option.c_iflag = 0; // 原始输入模式 - _option.c_oflag = 0; // 原始输出模式 - _option.c_lflag = 0; // 关闭终端模式 - _option.c_cflag |= (CLOCAL | CREAD); // 设置控制模式状态,本地连接,接收使能 - _option.c_cflag &= ~CSIZE; // 字符长度,设置数据位之前一定要屏掉这个位 - _option.c_cflag &= ~CRTSCTS; // 无硬件流控 - _option.c_cflag |= CS8; // 8位数据长度 - _option.c_cflag &= ~CSTOPB; // 1位停止位 - _option.c_cc[VTIME] = 0; - _option.c_cc[VMIN] = 0; - cfsetospeed(&_option, _baud_rate); // 设置输入波特率 - cfsetispeed(&_option, _baud_rate); // 设置输出波特率 + option.c_iflag = 0; // 原始输入模式 + option.c_oflag = 0; // 原始输出模式 + option.c_lflag = 0; // 关闭终端模式 + option.c_cflag |= (CLOCAL | CREAD); // 设置控制模式状态,本地连接,接收使能 + option.c_cflag &= ~CSIZE; // 字符长度,设置数据位之前一定要屏掉这个位 + option.c_cflag &= ~CRTSCTS; // 无硬件流控 + option.c_cflag |= CS8; // 8 位数据长度 + option.c_cflag &= ~CSTOPB; // 1 位停止位 + option.c_cc[VTIME] = 0; + option.c_cc[VMIN] = _mode.read_mode == SPReadMode::BLOCK ? 1 : 0; + cfsetspeed(&option, getBaudRate(_mode.baud_rate)); // 设置输入波特率 // 设置新属性,TCSANOW:所有改变立即生效 - tcsetattr(_fd, TCSANOW, &_option); + tcsetattr(_fd, TCSANOW, &option); _is_open = true; } diff --git a/modules/light/include/rmvl/light.hpp b/modules/light/include/rmvl/light.hpp index 56db78b2..b3a2e244 100644 --- a/modules/light/include/rmvl/light.hpp +++ b/modules/light/include/rmvl/light.hpp @@ -12,7 +12,8 @@ /** * @defgroup light 光源控制器 * @{ - * @defgroup opt_light_control OPT 奥普特光源控制器 + * @defgroup opt_light_control OPT 奥普特 GigE 光源控制库 + * @defgroup hik_light_control 海康机器人 RS-232 光源控制库 * @} */