Loading lib/mcp2221a.h +39 −4 Original line number Diff line number Diff line Loading @@ -5,7 +5,14 @@ #include <cstdint> #include <string> #ifdef _WIN32 #include <Windows.h> #endif #define MCP2221_NAME "MCP2221 USB-I2C/UART Combo" #define I2C_SPEED_100KHZ 100000 #define I2C_SPEED_400KHZ 400000 #define I2C_TIMEOUT_1MS 1 namespace MCP2221A { Loading Loading @@ -44,6 +51,8 @@ namespace MCP2221A { std::string mcp2221a_path_; uint8_t current_slave_addr_; std::vector<int> slaves_addr_; unsigned int i2c_speed_; unsigned char i2c_timeout_ms_; /** * @brief Scan the I2C bus looking for available devices Loading Loading @@ -79,7 +88,7 @@ namespace MCP2221A { * @throw I2CMasterException if something fails. */ #ifdef _WIN32 I2CMaster(std::string port = "COM3"); I2CMaster(unsigned int speed = I2C_SPEED_100KHZ, unsigned char timeout_ms = I2C_TIMEOUT_1MS); #elif UNIX I2CMaster(std::string port = "ttyACM0"); #endif Loading Loading @@ -129,6 +138,15 @@ namespace MCP2221A { * @return std::vector<int> Slave addresses. List can be empty if no slave are found */ std::vector<int> get_slaves_addresses(bool force_scan = false); /** * @brief Set the I2C bus speed * * @param speed Speed in Hz. Supported speeds are 100000 (standard mode) and 400000 (fast mode) * * @throw I2CMasterException if an error occurs */ void set_speed(uint32_t speed); }; /** Loading @@ -154,10 +172,16 @@ namespace MCP2221A { }; private: #ifdef UNIX int fd_; std::string port_; BAUD baudrate_; uint8_t read_timeout_; #elif _WIN32 HANDLE handle_; std::wstring port_; DWORD read_timeout_; #endif BAUD baudrate_; public: /** Loading @@ -169,7 +193,7 @@ namespace MCP2221A { * @throw SerialException if something fails. */ #ifdef _WIN32 Serial(std::string port = "COM3", BAUD baudrate = B_115200); Serial(std::wstring port = std::wstring(L"COM3"), BAUD baudrate = B_115200); #elif UNIX Serial(std::string port = "ttyACM0", BAUD baudrate = B_115200); #endif Loading Loading @@ -209,6 +233,7 @@ namespace MCP2221A { */ void set_baudrate(BAUD baudrate); #ifdef UNIX /** * @brief Set the timeout * Loading @@ -217,5 +242,15 @@ namespace MCP2221A { * @throw SerialException if an error occurs */ void set_timeout(uint8_t timeout); #elif _WIN32 /** * @brief Set the timeout * * @param timeout Timeout value in milliseconds. * * @throw SerialException if an error occurs */ void set_timeout(DWORD timeout_ms); #endif }; } No newline at end of file lib/mcp2221a_i2c_win.cpp +30 −9 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ #define I2C_MIN_ADDR 0x08 #define I2C_10BIT_ADDRESS 0 #define I2C_7BIT_ADDRESS 1 #define I2C_SPEED_100KHZ 100000 #define I2C_MAX_RETRIES 5 using namespace std; Loading Loading @@ -43,7 +43,7 @@ namespace MCP2221A { } } I2CMaster::I2CMaster(std::string port) : handle_(nullptr), mcp2221a_path_(port), current_slave_addr_(0) I2CMaster::I2CMaster(unsigned int speed, unsigned char timeout_ms) : handle_(nullptr), current_slave_addr_(0), i2c_speed_(speed), i2c_timeout_ms_(timeout_ms) { unsigned int device_count = 0; int ret = 0; Loading @@ -63,8 +63,11 @@ namespace MCP2221A { throw I2CMasterException("Failed to open MCP2221A device: " + get_error_string(ret)); } if ((ret = Mcp2221_SetSpeed(handle_, I2C_SPEED_100KHZ)) < 0) { throw I2CMasterException("Failed to set I2C speed: " + get_error_string(ret)); set_speed(speed); if ((ret = Mcp2221_SetAdvancedCommParams(handle_, i2c_timeout_ms_, I2C_MAX_RETRIES)) < 0) { handle_ = nullptr; throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } } Loading @@ -83,15 +86,24 @@ namespace MCP2221A { int ret = 0; unsigned char dummy = 0; slaves_addr_.clear(); if ((ret = Mcp2221_SetAdvancedCommParams(handle_, I2C_TIMEOUT_1MS, 2)) < 0) { throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } for (unsigned char addr = I2C_MIN_ADDR; addr < I2C_MAX_ADDR; addr++) { // if ((ret = Mcp2221_I2cWrite(handle_, sizeof(dummy), addr, I2C_7BIT_ADDRESS, &dummy)) < 0) { // throw I2CMasterException(get_error_string(ret)); // } if ((ret = Mcp2221_I2cRead(handle_, sizeof(dummy), addr, I2C_7BIT_ADDRESS, &dummy)) < 0) { try { receive(addr, &dummy, 1); } catch (const I2CMasterException &e) { continue; } slaves_addr_.push_back(addr); } if ((ret = Mcp2221_SetAdvancedCommParams(handle_, i2c_timeout_ms_, I2C_MAX_RETRIES)) < 0) { throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } } void I2CMaster::set_slave_address(uint8_t addr) Loading Loading @@ -144,4 +156,13 @@ namespace MCP2221A { return slaves_addr_; } void I2CMaster::set_speed(uint32_t speed) { int ret = 0; if ((ret = Mcp2221_SetSpeed(handle_, speed)) < 0) { throw I2CMasterException("Failed to set I2C speed: " + get_error_string(ret)); } } } // namespace MCP2221A No newline at end of file lib/mcp2221a_serial_win.cpp +241 −9 Original line number Diff line number Diff line #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #include <string> #include <iostream> #include <map> extern "C" { #include <Windows.h> #include "mcp2221a.h" #include <setupapi.h> #include <devguid.h> #include <regstr.h> #include <tchar.h> } #include "mcp2221a.h" #include "mcp2221_dll_um.h" #pragma comment(lib, "setupapi.lib") #define MCP2221A_VID "04D8" #define MCP2221A_PID "00DD" #define READ_TIMEOUT 1000 // in milliseconds namespace MCP2221A { static std::map<Serial::BAUD, int> baud_map = { {Serial::B_300, CBR_300}, {Serial::B_1200, CBR_1200}, {Serial::B_2400, CBR_2400}, {Serial::B_4800, CBR_4800}, {Serial::B_9600, CBR_9600}, {Serial::B_19200, CBR_19200}, {Serial::B_38400, CBR_38400}, {Serial::B_57600, CBR_57600}, {Serial::B_115200, CBR_115200} }; static void PrintCommState(DCB dcb) { // Print some of the DCB structure values _tprintf( TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"), dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits ); } static bool check_com_port_vid_pid(const std::wstring& targetPort, const std::string& targetVid = MCP2221A_VID, const std::string& targetPid = MCP2221A_PID) { bool ret = false; HDEVINFO deviceInfoSet = SetupDiGetClassDevs( (const GUID*)&GUID_DEVCLASS_PORTS, NULL, NULL, DIGCF_PRESENT ); if (deviceInfoSet == INVALID_HANDLE_VALUE) { std::cerr << "Failed to get device info set." << std::endl; return false; } SP_DEVINFO_DATA deviceInfoData; deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) { TCHAR deviceInstanceId[512]; if (SetupDiGetDeviceInstanceId(deviceInfoSet, &deviceInfoData, deviceInstanceId, sizeof(deviceInstanceId) / sizeof(TCHAR), NULL)) { // Example: "USB\\VID_2341&PID_0043\\55639313337351D011E1" std::basic_string<TCHAR> instance(deviceInstanceId); // Retrieve COM port name HKEY hDeviceRegistryKey = SetupDiOpenDevRegKey( deviceInfoSet, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ ); Serial::Serial(std::string port, BAUD baudrate) if (hDeviceRegistryKey == INVALID_HANDLE_VALUE) continue; TCHAR portName[256]; DWORD size = sizeof(portName); DWORD type = 0; if (RegQueryValueEx(hDeviceRegistryKey, _T("PortName"), NULL, &type, (LPBYTE)portName, &size) == ERROR_SUCCESS) { std::basic_string<TCHAR> tsPortName(portName); std::wstring comPort(tsPortName.begin(), tsPortName.end()); if (comPort == targetPort) { // std::wcout << L"Found port: " << tsPortName.c_str() << std::endl; // std::wcout << L"Instance ID: " << instance << std::endl; // Extract VID and PID size_t vidPos = instance.find(_T("VID_")); size_t pidPos = instance.find(_T("PID_")); if (vidPos != std::basic_string<TCHAR>::npos && pidPos != std::basic_string<TCHAR>::npos) { std::basic_string<TCHAR> vid = instance.substr(vidPos + 4, 4); std::basic_string<TCHAR> pid = instance.substr(pidPos + 4, 4); std::string strVid(vid.begin(), vid.end()); std::string strPid(pid.begin(), pid.end()); if (strVid == targetVid && strPid == targetPid) { ret = true; RegCloseKey(hDeviceRegistryKey); break; } } } } RegCloseKey(hDeviceRegistryKey); } } SetupDiDestroyDeviceInfoList(deviceInfoSet); return ret; } Serial::Serial(std::wstring port, BAUD baudrate): handle_(NULL), port_(port), baudrate_(baudrate), read_timeout_(READ_TIMEOUT) { DCB dcb; bool ret; TCHAR *port_name = new TCHAR[port.length() + 8]; // +8 for "\\\\.\\" if (!port_name) { throw SerialException("Failed to allocate memory for port name"); } if (_stprintf(port_name, TEXT("\\\\.\\%s"), port.c_str()) < 0) { delete[] port_name; throw SerialException("Failed to format port name"); } if (!check_com_port_vid_pid(port_)) { delete[] port_name; std::string narrow_port(port_.begin(), port_.end()); throw SerialException(MCP2221_NAME " not found or not connected on port " + narrow_port); } handle_ = CreateFile(port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle_ == INVALID_HANDLE_VALUE) { delete[] port_name; std::string narrow_port(port_.begin(), port_.end()); throw SerialException("Failed to open serial port " + narrow_port); } SecureZeroMemory(&dcb, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); ret = GetCommState(handle_, &dcb); if (!ret) { CloseHandle(handle_); delete[] port_name; throw SerialException("Failed to get serial port state"); } dcb.BaudRate = baud_map[baudrate_]; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; ret = SetCommState(handle_, &dcb); if (!ret) { CloseHandle(handle_); delete[] port_name; throw SerialException("Failed to set serial port state"); } set_timeout(read_timeout_); } // Implementations of pure virtual destructor Serial::~Serial() { delete[] port_.c_str(); if (handle_ != INVALID_HANDLE_VALUE) { CloseHandle(handle_); } } void Serial::send(uint8_t *data, size_t length) { // Windows implementation to send data DWORD bytes_written = 0; bool ret; ret = WriteFile(handle_, data, length, &bytes_written, NULL); if (!ret) { throw SerialException("Failed to send data"); } } void Serial::receive(uint8_t *data, size_t length) { // Windows implementation to receive data DWORD bytes_read = 0; bool ret; ret = ReadFile(handle_, data, length, &bytes_read, NULL); if (!ret) { throw SerialException("Failed to receive data"); } if (bytes_read < length) { throw TimeoutException("Timeout expired and " + std::to_string(bytes_read) + "/" + std::to_string(length) + " bytes were received"); } } void Serial::set_baudrate(BAUD baudrate) { // Windows implementation to set baudrate DCB dcb; bool ret; if (baudrate_ == baudrate) return; SecureZeroMemory(&dcb, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); ret = GetCommState(handle_, &dcb); if (!ret) { throw SerialException("Failed to get serial port state"); } dcb.BaudRate = baud_map[baudrate]; ret = SetCommState(handle_, &dcb); if (!ret) { throw SerialException("Failed to set serial port state"); } void Serial::set_timeout(uint8_t timeout) baudrate_ = baudrate; } void Serial::set_timeout(DWORD timeout_ms) { // Windows implementation to set timeout if (read_timeout_ == timeout_ms) return; COMMTIMEOUTS timeouts; SecureZeroMemory(&timeouts, sizeof(COMMTIMEOUTS)); if (!GetCommTimeouts(handle_, &timeouts)) { throw SerialException("Failed to get serial port timeouts"); } timeouts.ReadTotalTimeoutConstant = timeout_ms; if (!SetCommTimeouts(handle_, &timeouts)) { throw SerialException("Failed to set serial port timeouts"); } read_timeout_ = timeout_ms; } } tests/CMakeLists.txt +3 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,9 @@ project(test LANGUAGES C CXX) # add_executable(${PROJECT_NAME} test.cpp) # target_link_libraries(${PROJECT_NAME} PRIVATE mcp2221a) # add_executable(main main.cpp) # target_link_libraries(main PRIVATE mcp2221a) add_executable(serial_test serial_test.cpp) target_link_libraries(serial_test PRIVATE mcp2221a) add_executable(i2c_scan scan.cpp) add_executable(i2c_scan i2c_scan.cpp) target_link_libraries(i2c_scan PRIVATE mcp2221a) copy_runtime_dll(i2c_scan) No newline at end of file tests/scan.cpp→tests/i2c_scan.cpp +0 −0 File moved. View file Loading
lib/mcp2221a.h +39 −4 Original line number Diff line number Diff line Loading @@ -5,7 +5,14 @@ #include <cstdint> #include <string> #ifdef _WIN32 #include <Windows.h> #endif #define MCP2221_NAME "MCP2221 USB-I2C/UART Combo" #define I2C_SPEED_100KHZ 100000 #define I2C_SPEED_400KHZ 400000 #define I2C_TIMEOUT_1MS 1 namespace MCP2221A { Loading Loading @@ -44,6 +51,8 @@ namespace MCP2221A { std::string mcp2221a_path_; uint8_t current_slave_addr_; std::vector<int> slaves_addr_; unsigned int i2c_speed_; unsigned char i2c_timeout_ms_; /** * @brief Scan the I2C bus looking for available devices Loading Loading @@ -79,7 +88,7 @@ namespace MCP2221A { * @throw I2CMasterException if something fails. */ #ifdef _WIN32 I2CMaster(std::string port = "COM3"); I2CMaster(unsigned int speed = I2C_SPEED_100KHZ, unsigned char timeout_ms = I2C_TIMEOUT_1MS); #elif UNIX I2CMaster(std::string port = "ttyACM0"); #endif Loading Loading @@ -129,6 +138,15 @@ namespace MCP2221A { * @return std::vector<int> Slave addresses. List can be empty if no slave are found */ std::vector<int> get_slaves_addresses(bool force_scan = false); /** * @brief Set the I2C bus speed * * @param speed Speed in Hz. Supported speeds are 100000 (standard mode) and 400000 (fast mode) * * @throw I2CMasterException if an error occurs */ void set_speed(uint32_t speed); }; /** Loading @@ -154,10 +172,16 @@ namespace MCP2221A { }; private: #ifdef UNIX int fd_; std::string port_; BAUD baudrate_; uint8_t read_timeout_; #elif _WIN32 HANDLE handle_; std::wstring port_; DWORD read_timeout_; #endif BAUD baudrate_; public: /** Loading @@ -169,7 +193,7 @@ namespace MCP2221A { * @throw SerialException if something fails. */ #ifdef _WIN32 Serial(std::string port = "COM3", BAUD baudrate = B_115200); Serial(std::wstring port = std::wstring(L"COM3"), BAUD baudrate = B_115200); #elif UNIX Serial(std::string port = "ttyACM0", BAUD baudrate = B_115200); #endif Loading Loading @@ -209,6 +233,7 @@ namespace MCP2221A { */ void set_baudrate(BAUD baudrate); #ifdef UNIX /** * @brief Set the timeout * Loading @@ -217,5 +242,15 @@ namespace MCP2221A { * @throw SerialException if an error occurs */ void set_timeout(uint8_t timeout); #elif _WIN32 /** * @brief Set the timeout * * @param timeout Timeout value in milliseconds. * * @throw SerialException if an error occurs */ void set_timeout(DWORD timeout_ms); #endif }; } No newline at end of file
lib/mcp2221a_i2c_win.cpp +30 −9 Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ #define I2C_MIN_ADDR 0x08 #define I2C_10BIT_ADDRESS 0 #define I2C_7BIT_ADDRESS 1 #define I2C_SPEED_100KHZ 100000 #define I2C_MAX_RETRIES 5 using namespace std; Loading Loading @@ -43,7 +43,7 @@ namespace MCP2221A { } } I2CMaster::I2CMaster(std::string port) : handle_(nullptr), mcp2221a_path_(port), current_slave_addr_(0) I2CMaster::I2CMaster(unsigned int speed, unsigned char timeout_ms) : handle_(nullptr), current_slave_addr_(0), i2c_speed_(speed), i2c_timeout_ms_(timeout_ms) { unsigned int device_count = 0; int ret = 0; Loading @@ -63,8 +63,11 @@ namespace MCP2221A { throw I2CMasterException("Failed to open MCP2221A device: " + get_error_string(ret)); } if ((ret = Mcp2221_SetSpeed(handle_, I2C_SPEED_100KHZ)) < 0) { throw I2CMasterException("Failed to set I2C speed: " + get_error_string(ret)); set_speed(speed); if ((ret = Mcp2221_SetAdvancedCommParams(handle_, i2c_timeout_ms_, I2C_MAX_RETRIES)) < 0) { handle_ = nullptr; throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } } Loading @@ -83,15 +86,24 @@ namespace MCP2221A { int ret = 0; unsigned char dummy = 0; slaves_addr_.clear(); if ((ret = Mcp2221_SetAdvancedCommParams(handle_, I2C_TIMEOUT_1MS, 2)) < 0) { throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } for (unsigned char addr = I2C_MIN_ADDR; addr < I2C_MAX_ADDR; addr++) { // if ((ret = Mcp2221_I2cWrite(handle_, sizeof(dummy), addr, I2C_7BIT_ADDRESS, &dummy)) < 0) { // throw I2CMasterException(get_error_string(ret)); // } if ((ret = Mcp2221_I2cRead(handle_, sizeof(dummy), addr, I2C_7BIT_ADDRESS, &dummy)) < 0) { try { receive(addr, &dummy, 1); } catch (const I2CMasterException &e) { continue; } slaves_addr_.push_back(addr); } if ((ret = Mcp2221_SetAdvancedCommParams(handle_, i2c_timeout_ms_, I2C_MAX_RETRIES)) < 0) { throw I2CMasterException("Failed to set I2C advanced communication parameters: " + get_error_string(ret)); } } void I2CMaster::set_slave_address(uint8_t addr) Loading Loading @@ -144,4 +156,13 @@ namespace MCP2221A { return slaves_addr_; } void I2CMaster::set_speed(uint32_t speed) { int ret = 0; if ((ret = Mcp2221_SetSpeed(handle_, speed)) < 0) { throw I2CMasterException("Failed to set I2C speed: " + get_error_string(ret)); } } } // namespace MCP2221A No newline at end of file
lib/mcp2221a_serial_win.cpp +241 −9 Original line number Diff line number Diff line #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #include <string> #include <iostream> #include <map> extern "C" { #include <Windows.h> #include "mcp2221a.h" #include <setupapi.h> #include <devguid.h> #include <regstr.h> #include <tchar.h> } #include "mcp2221a.h" #include "mcp2221_dll_um.h" #pragma comment(lib, "setupapi.lib") #define MCP2221A_VID "04D8" #define MCP2221A_PID "00DD" #define READ_TIMEOUT 1000 // in milliseconds namespace MCP2221A { static std::map<Serial::BAUD, int> baud_map = { {Serial::B_300, CBR_300}, {Serial::B_1200, CBR_1200}, {Serial::B_2400, CBR_2400}, {Serial::B_4800, CBR_4800}, {Serial::B_9600, CBR_9600}, {Serial::B_19200, CBR_19200}, {Serial::B_38400, CBR_38400}, {Serial::B_57600, CBR_57600}, {Serial::B_115200, CBR_115200} }; static void PrintCommState(DCB dcb) { // Print some of the DCB structure values _tprintf( TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"), dcb.BaudRate, dcb.ByteSize, dcb.Parity, dcb.StopBits ); } static bool check_com_port_vid_pid(const std::wstring& targetPort, const std::string& targetVid = MCP2221A_VID, const std::string& targetPid = MCP2221A_PID) { bool ret = false; HDEVINFO deviceInfoSet = SetupDiGetClassDevs( (const GUID*)&GUID_DEVCLASS_PORTS, NULL, NULL, DIGCF_PRESENT ); if (deviceInfoSet == INVALID_HANDLE_VALUE) { std::cerr << "Failed to get device info set." << std::endl; return false; } SP_DEVINFO_DATA deviceInfoData; deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) { TCHAR deviceInstanceId[512]; if (SetupDiGetDeviceInstanceId(deviceInfoSet, &deviceInfoData, deviceInstanceId, sizeof(deviceInstanceId) / sizeof(TCHAR), NULL)) { // Example: "USB\\VID_2341&PID_0043\\55639313337351D011E1" std::basic_string<TCHAR> instance(deviceInstanceId); // Retrieve COM port name HKEY hDeviceRegistryKey = SetupDiOpenDevRegKey( deviceInfoSet, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ ); Serial::Serial(std::string port, BAUD baudrate) if (hDeviceRegistryKey == INVALID_HANDLE_VALUE) continue; TCHAR portName[256]; DWORD size = sizeof(portName); DWORD type = 0; if (RegQueryValueEx(hDeviceRegistryKey, _T("PortName"), NULL, &type, (LPBYTE)portName, &size) == ERROR_SUCCESS) { std::basic_string<TCHAR> tsPortName(portName); std::wstring comPort(tsPortName.begin(), tsPortName.end()); if (comPort == targetPort) { // std::wcout << L"Found port: " << tsPortName.c_str() << std::endl; // std::wcout << L"Instance ID: " << instance << std::endl; // Extract VID and PID size_t vidPos = instance.find(_T("VID_")); size_t pidPos = instance.find(_T("PID_")); if (vidPos != std::basic_string<TCHAR>::npos && pidPos != std::basic_string<TCHAR>::npos) { std::basic_string<TCHAR> vid = instance.substr(vidPos + 4, 4); std::basic_string<TCHAR> pid = instance.substr(pidPos + 4, 4); std::string strVid(vid.begin(), vid.end()); std::string strPid(pid.begin(), pid.end()); if (strVid == targetVid && strPid == targetPid) { ret = true; RegCloseKey(hDeviceRegistryKey); break; } } } } RegCloseKey(hDeviceRegistryKey); } } SetupDiDestroyDeviceInfoList(deviceInfoSet); return ret; } Serial::Serial(std::wstring port, BAUD baudrate): handle_(NULL), port_(port), baudrate_(baudrate), read_timeout_(READ_TIMEOUT) { DCB dcb; bool ret; TCHAR *port_name = new TCHAR[port.length() + 8]; // +8 for "\\\\.\\" if (!port_name) { throw SerialException("Failed to allocate memory for port name"); } if (_stprintf(port_name, TEXT("\\\\.\\%s"), port.c_str()) < 0) { delete[] port_name; throw SerialException("Failed to format port name"); } if (!check_com_port_vid_pid(port_)) { delete[] port_name; std::string narrow_port(port_.begin(), port_.end()); throw SerialException(MCP2221_NAME " not found or not connected on port " + narrow_port); } handle_ = CreateFile(port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle_ == INVALID_HANDLE_VALUE) { delete[] port_name; std::string narrow_port(port_.begin(), port_.end()); throw SerialException("Failed to open serial port " + narrow_port); } SecureZeroMemory(&dcb, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); ret = GetCommState(handle_, &dcb); if (!ret) { CloseHandle(handle_); delete[] port_name; throw SerialException("Failed to get serial port state"); } dcb.BaudRate = baud_map[baudrate_]; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; ret = SetCommState(handle_, &dcb); if (!ret) { CloseHandle(handle_); delete[] port_name; throw SerialException("Failed to set serial port state"); } set_timeout(read_timeout_); } // Implementations of pure virtual destructor Serial::~Serial() { delete[] port_.c_str(); if (handle_ != INVALID_HANDLE_VALUE) { CloseHandle(handle_); } } void Serial::send(uint8_t *data, size_t length) { // Windows implementation to send data DWORD bytes_written = 0; bool ret; ret = WriteFile(handle_, data, length, &bytes_written, NULL); if (!ret) { throw SerialException("Failed to send data"); } } void Serial::receive(uint8_t *data, size_t length) { // Windows implementation to receive data DWORD bytes_read = 0; bool ret; ret = ReadFile(handle_, data, length, &bytes_read, NULL); if (!ret) { throw SerialException("Failed to receive data"); } if (bytes_read < length) { throw TimeoutException("Timeout expired and " + std::to_string(bytes_read) + "/" + std::to_string(length) + " bytes were received"); } } void Serial::set_baudrate(BAUD baudrate) { // Windows implementation to set baudrate DCB dcb; bool ret; if (baudrate_ == baudrate) return; SecureZeroMemory(&dcb, sizeof(DCB)); dcb.DCBlength = sizeof(DCB); ret = GetCommState(handle_, &dcb); if (!ret) { throw SerialException("Failed to get serial port state"); } dcb.BaudRate = baud_map[baudrate]; ret = SetCommState(handle_, &dcb); if (!ret) { throw SerialException("Failed to set serial port state"); } void Serial::set_timeout(uint8_t timeout) baudrate_ = baudrate; } void Serial::set_timeout(DWORD timeout_ms) { // Windows implementation to set timeout if (read_timeout_ == timeout_ms) return; COMMTIMEOUTS timeouts; SecureZeroMemory(&timeouts, sizeof(COMMTIMEOUTS)); if (!GetCommTimeouts(handle_, &timeouts)) { throw SerialException("Failed to get serial port timeouts"); } timeouts.ReadTotalTimeoutConstant = timeout_ms; if (!SetCommTimeouts(handle_, &timeouts)) { throw SerialException("Failed to set serial port timeouts"); } read_timeout_ = timeout_ms; } }
tests/CMakeLists.txt +3 −3 Original line number Diff line number Diff line Loading @@ -5,9 +5,9 @@ project(test LANGUAGES C CXX) # add_executable(${PROJECT_NAME} test.cpp) # target_link_libraries(${PROJECT_NAME} PRIVATE mcp2221a) # add_executable(main main.cpp) # target_link_libraries(main PRIVATE mcp2221a) add_executable(serial_test serial_test.cpp) target_link_libraries(serial_test PRIVATE mcp2221a) add_executable(i2c_scan scan.cpp) add_executable(i2c_scan i2c_scan.cpp) target_link_libraries(i2c_scan PRIVATE mcp2221a) copy_runtime_dll(i2c_scan) No newline at end of file