MercurySDK
Software development kit for Mercury digital servos
port_handler_windows.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2021 <Robot Articulation/code@robotarticulation.com>
3 *
4 * Source files modified to support the Mercury range of digital servo motors from Robot Articulation
5 *******************************************************************************/
6 
7 /*******************************************************************************
8 * Copyright 2017 ROBOTIS CO., LTD.
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 *******************************************************************************/
22 
23 /* Original author: Zerom, Leon (RyuWoon Jung) */
24 
25 #if defined(_WIN32) || defined(_WIN64)
26 #define WINDLLEXPORT
27 
28 #include "port_handler_windows.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <time.h>
33 
34 #define LATENCY_TIMER 16 // msec (USB latency timer)
35  // You should adjust the latency timer value. In Windows, the default latency timer of the usb serial is '16 msec'.
36  // When you are going to use sync / bulk read, the latency timer should be loosen.
37  // the lower latency timer value, the faster communication speed.
38 
39  // Note:
40  // You can either checking or changing its value by:
41  // [Device Manager] -> [Port (COM & LPT)] -> the port you use but starts with COMx-> mouse right click -> properties
42  // -> [port settings] -> [details] -> change response time from 16 to the value you need
43 
44 using namespace mercury;
45 
46 PortHandlerWindows::PortHandlerWindows(const char *port_name)
47  : serial_handle_(INVALID_HANDLE_VALUE),
48  baudrate_(DEFAULT_BAUDRATE_),
49  packet_start_time_(0.0),
50  packet_timeout_(0.0),
51  tx_time_per_byte_(0.0)
52 {
53  is_using_ = false;
54 
55  char buffer[15];
56  sprintf_s(buffer, sizeof(buffer), "\\\\.\\%s", port_name);
57  setPortName(buffer);
58 }
59 
60 bool PortHandlerWindows::openPort()
61 {
62  return setBaudRate(baudrate_);
63 }
64 
66 {
67  if (serial_handle_ != INVALID_HANDLE_VALUE)
68  {
69  CloseHandle(serial_handle_);
70  serial_handle_ = INVALID_HANDLE_VALUE;
71  }
72 }
73 
75 {
76  PurgeComm(serial_handle_, PURGE_RXABORT | PURGE_RXCLEAR);
77 }
78 
79 void PortHandlerWindows::setPortName(const char *port_name)
80 {
81  strcpy_s(port_name_, sizeof(port_name_), port_name);
82 }
83 
85 {
86  return port_name_;
87 }
88 
89 bool PortHandlerWindows::setBaudRate(const int baudrate)
90 {
91  closePort();
92 
93  baudrate_ = baudrate;
94  return setupPort(baudrate);
95 }
96 
98 {
99  return baudrate_;
100 }
101 
103 {
104  DWORD retbyte = 2;
105  BOOL res = DeviceIoControl(serial_handle_, GENERIC_READ | GENERIC_WRITE, NULL, 0, 0, 0, &retbyte, (LPOVERLAPPED)NULL);
106 
107  printf("%d", (int)res);
108  return (int)retbyte;
109 }
110 
111 int PortHandlerWindows::readPort(uint8_t *packet, int length)
112 {
113  DWORD dwRead = 0;
114 
115  if (ReadFile(serial_handle_, packet, (DWORD)length, &dwRead, NULL) == FALSE)
116  return -1;
117 
118  return (int)dwRead;
119 }
120 
121 int PortHandlerWindows::writePort(uint8_t *packet, int length)
122 {
123  DWORD dwWrite = 0;
124 
125  if (WriteFile(serial_handle_, packet, (DWORD)length, &dwWrite, NULL) == FALSE)
126  return -1;
127 
128  return (int)dwWrite;
129 }
130 
131 void PortHandlerWindows::setPacketTimeout(uint16_t packet_length)
132 {
133  packet_start_time_ = getCurrentTime();
134  packet_timeout_ = (tx_time_per_byte_ * (double)packet_length) + (LATENCY_TIMER * 2.0) + 2.0;
135 }
136 
137 void PortHandlerWindows::setPacketTimeout(double msec)
138 {
139  packet_start_time_ = getCurrentTime();
140  packet_timeout_ = msec;
141 }
142 
144 {
145  if (getTimeSinceStart() > packet_timeout_)
146  {
147  packet_timeout_ = 0;
148  return true;
149  }
150  return false;
151 }
152 
153 double PortHandlerWindows::getCurrentTime()
154 {
155  QueryPerformanceCounter(&counter_);
156  QueryPerformanceFrequency(&freq_);
157  return (double)counter_.QuadPart / (double)freq_.QuadPart * 1000.0;
158 }
159 
160 double PortHandlerWindows::getTimeSinceStart()
161 {
162  double time;
163 
164  time = getCurrentTime() - packet_start_time_;
165  if (time < 0.0) packet_start_time_ = getCurrentTime();
166 
167  return time;
168 }
169 
170 bool PortHandlerWindows::setupPort(int baudrate)
171 {
172  DCB dcb;
173  COMMTIMEOUTS timeouts;
174  DWORD dwError;
175 
176  closePort();
177 
178  serial_handle_ = CreateFileA(port_name_, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
179  if (serial_handle_ == INVALID_HANDLE_VALUE)
180  {
181  printf("[PortHandlerWindows::SetupPort] Error opening serial port!\n");
182  return false;
183  }
184 
185  dcb.DCBlength = sizeof(DCB);
186  if (GetCommState(serial_handle_, &dcb) == FALSE)
187  goto MCY_HAL_OPEN_ERROR;
188 
189  // Set baudrate
190  dcb.BaudRate = (DWORD)baudrate;
191  dcb.ByteSize = 8; // Data bit = 8bit
192  dcb.Parity = NOPARITY; // No parity
193  dcb.StopBits = ONESTOPBIT; // Stop bit = 1
194  dcb.fParity = NOPARITY; // No Parity check
195  dcb.fBinary = 1; // Binary mode
196  dcb.fNull = 0; // Get Null byte
197  dcb.fAbortOnError = 0;
198  dcb.fErrorChar = 0;
199  // Not using XOn/XOff
200  dcb.fOutX = 0;
201  dcb.fInX = 0;
202  // Not using H/W flow control
203  dcb.fDtrControl = DTR_CONTROL_DISABLE;
204  dcb.fRtsControl = RTS_CONTROL_DISABLE;
205  dcb.fDsrSensitivity = 0;
206  dcb.fOutxDsrFlow = 0;
207  dcb.fOutxCtsFlow = 0;
208 
209  if (SetCommState(serial_handle_, &dcb) == FALSE)
210  goto MCY_HAL_OPEN_ERROR;
211 
212  if (SetCommMask(serial_handle_, 0) == FALSE) // Not using Comm event
213  goto MCY_HAL_OPEN_ERROR;
214  if (SetupComm(serial_handle_, 4096, 4096) == FALSE) // Buffer size (Rx,Tx)
215  goto MCY_HAL_OPEN_ERROR;
216  if (PurgeComm(serial_handle_, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR) == FALSE) // Clear buffer
217  goto MCY_HAL_OPEN_ERROR;
218  if (ClearCommError(serial_handle_, &dwError, NULL) == FALSE)
219  goto MCY_HAL_OPEN_ERROR;
220 
221  if (GetCommTimeouts(serial_handle_, &timeouts) == FALSE)
222  goto MCY_HAL_OPEN_ERROR;
223  // Timeout (Not using timeout)
224  // Immediatly return
225  timeouts.ReadIntervalTimeout = 0;
226  timeouts.ReadTotalTimeoutMultiplier = 0;
227  timeouts.ReadTotalTimeoutConstant = 1; // must not be zero.
228  timeouts.WriteTotalTimeoutMultiplier = 0;
229  timeouts.WriteTotalTimeoutConstant = 0;
230  if (SetCommTimeouts(serial_handle_, &timeouts) == FALSE)
231  goto MCY_HAL_OPEN_ERROR;
232 
233  tx_time_per_byte_ = (1000.0 / (double)baudrate_) * 10.0;
234  return true;
235 
236 MCY_HAL_OPEN_ERROR:
237  closePort();
238  return false;
239 }
240 
241 #endif
void clearPort()
The function that clears the port @description The function clears the port.
bool setBaudRate(const int baudrate)
The function that sets baudrate into the port handler @description The function sets baudrate into th...
int getBaudRate()
The function that returns current baudrate set into the port handler @description The function return...
int writePort(uint8_t *packet, int length)
The function that writes bytes on the port buffer @description The function writes bytes on the port ...
void closePort()
The function that closes the port @description The function closes the port.
int getBytesAvailable()
The function that checks how much bytes are able to be read from the port buffer @description The fun...
PortHandlerWindows(const char *port_name)
The function that initializes instance of PortHandler and gets port_name @description The function in...
void setPacketTimeout(uint16_t packet_length)
The function that sets and starts stopwatch for watching packet timeout @description The function set...
void setPortName(const char *port_name)
The function that sets port name into the port handler @description The function sets port name into ...
bool isPacketTimeout()
The function that checks whether packet timeout is occurred @description The function checks whether ...
char * getPortName()
The function that returns port name set into the port handler @description The function returns curre...
int readPort(uint8_t *packet, int length)
The function that reads bytes from the port buffer @description The function gets bytes from the port...