Skip to content
Snippets Groups Projects
PortHandlerWindows.c 7.88 KiB
Newer Older
/*
* PortHandlerWindows.c
*
*  Created on: 2016. 5. 4.
*      Author: leon
*/

#if defined(_WIN32) || defined(_WIN64)
#define WINDLLEXPORT
#endif

#include <stdio.h>
#include <string.h>
#include <time.h>
#include "dynamixel_sdk_windows/PortHandlerWindows.h"

#define LATENCY_TIMER  16 // msec (USB latency timer) 

typedef struct
{
    HANDLE  serial_handle_;
    LARGE_INTEGER freq_, counter_;

    int     baudrate_;
    char    port_name_[30];

    double  packet_start_time_;
    double  packet_timeout_;
    double  tx_time_per_byte_;
}PortDataWindows;

PortDataWindows *portDataWindows;

int PortHandlerWindows(const char *port_name)
{
    int _port_num;
    char _port_name[15];

    sprintf_s(_port_name, sizeof(_port_name), "\\\\.\\", port_name);

    if (portDataWindows == NULL)
    {
        _port_num = 0;
        used_port_num_ = 1;
        portDataWindows = (PortDataWindows*)calloc(1, sizeof(PortDataWindows));
        is_using_ = (bool*)calloc(1, sizeof(bool));
    }
    else
    {
        for (_port_num = 0; _port_num < used_port_num_; _port_num++)
            if (!strcmp(portDataWindows[_port_num].port_name_, port_name))
                break;

        if (_port_num == used_port_num_)
        {
            for (_port_num = 0; _port_num < used_port_num_; _port_num++)
                if (portDataWindows[_port_num].serial_handle_ != INVALID_HANDLE_VALUE)
                    break;

            if (_port_num == used_port_num_)
            {
                used_port_num_++;
                portDataWindows = (PortDataWindows*)realloc(portDataWindows, used_port_num_ * sizeof(PortDataWindows));
                is_using_ = (bool*)realloc(is_using_, used_port_num_ * sizeof(bool));
            }
        }
        else
            printf("[PortHandler setup] The port number %d has same device name... reinitialize port number %d!!\n", _port_num, _port_num);
    }

    portDataWindows[_port_num].serial_handle_ = INVALID_HANDLE_VALUE;
    portDataWindows[_port_num].baudrate_ = DEFAULT_BAUDRATE;
    portDataWindows[_port_num].packet_start_time_ = 0.0;
    portDataWindows[_port_num].packet_timeout_ = 0.0;
    portDataWindows[_port_num].tx_time_per_byte_ = 0.0;

    is_using_[_port_num] = false;

    SetPortNameWindows(_port_num, port_name);

    return _port_num;
}

bool OpenPortWindows(int port_num)
{
    return SetBaudRateWindows(port_num, portDataWindows[port_num].baudrate_);
}

void ClosePortWindows(int port_num)
{
    if (portDataWindows[port_num].serial_handle_ != INVALID_HANDLE_VALUE)
    {
        CloseHandle(portDataWindows[port_num].serial_handle_);
        portDataWindows[port_num].serial_handle_ = INVALID_HANDLE_VALUE;
    }
}

void ClearPortWindows(int port_num)
{
    PurgeComm(portDataWindows[port_num].serial_handle_, PURGE_RXABORT | PURGE_RXCLEAR);
}

void SetPortNameWindows(int port_num, const char *port_name)
{
    strcpy_s(portDataWindows[port_num].port_name_, sizeof(portDataWindows[port_num].port_name_), port_name);
}

char *GetPortNameWindows(int port_num)
{
    return portDataWindows[port_num].port_name_;
}

bool SetBaudRateWindows(int port_num, const int baudrate)
{
    ClosePortWindows(port_num);

    portDataWindows[port_num].baudrate_ = baudrate;
    return SetupPortWindows(port_num, baudrate);
}

int GetBaudRateWindows(int port_num)
{
    return portDataWindows[port_num].baudrate_;
}

int ReadPortWindows(int port_num, UINT8_T *packet, int length)
{
    DWORD _dwRead = 0;

    if (ReadFile(portDataWindows[port_num].serial_handle_, packet, (DWORD)length, &_dwRead, NULL) == FALSE)
        return -1;

    return (int)_dwRead;
}

int WritePortWindows(int port_num, UINT8_T *packet, int length)
{
    DWORD _dwWrite = 0;

    if (WriteFile(portDataWindows[port_num].serial_handle_, packet, (DWORD)length, &_dwWrite, NULL) == FALSE)
        return -1;

    return (int)_dwWrite;
}

void SetPacketTimeoutWindows(int port_num, UINT16_T packet_length)
{
    portDataWindows[port_num].packet_start_time_ = GetCurrentTimeWindows(port_num);
    portDataWindows[port_num].packet_timeout_ = (portDataWindows[port_num].tx_time_per_byte_ * (double)packet_length) + (LATENCY_TIMER * 2.0) + 2.0;
}

void SetPacketTimeoutMSecWindows(int port_num, double msec)
{
    portDataWindows[port_num].packet_start_time_ = GetCurrentTimeWindows(port_num);
    portDataWindows[port_num].packet_timeout_ = msec;
}

bool IsPacketTimeoutWindows(int port_num)
{
    if (GetTimeSinceStartWindows(port_num) > portDataWindows[port_num].packet_timeout_)
    {
        portDataWindows[port_num].packet_timeout_ = 0;
        return true;
    }
    return false;
}
#undef GetCurrentTime
double GetCurrentTimeWindows(int port_num)
{
    QueryPerformanceCounter(&portDataWindows[port_num].counter_);
    QueryPerformanceFrequency(&portDataWindows[port_num].freq_);
    return (double)portDataWindows[port_num].counter_.QuadPart / (double)portDataWindows[port_num].freq_.QuadPart * 1000.0;
}

double GetTimeSinceStartWindows(int port_num)
{
    double _time;

    _time = GetCurrentTimeWindows(port_num) - portDataWindows[port_num].packet_start_time_;
    if (_time < 0.0)
        portDataWindows[port_num].packet_start_time_ = GetCurrentTimeWindows(port_num);

    return _time;
}

bool SetupPortWindows(int port_num, const int baudrate)
{
    DCB dcb;
    COMMTIMEOUTS timeouts;
    DWORD dwError;
    
    ClosePortWindows(port_num);
    
    portDataWindows[port_num].serial_handle_ = CreateFileA(portDataWindows[port_num].port_name_, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (portDataWindows[port_num].serial_handle_ == INVALID_HANDLE_VALUE)
    {
        printf("[PortHandlerWindows::SetupPort] Error opening serial port!\n");
        return false;
    }
    
    dcb.DCBlength = sizeof(DCB);
    if (GetCommState(portDataWindows[port_num].serial_handle_, &dcb) == FALSE)
        goto DXL_HAL_OPEN_ERROR;
    
    // Set baudrate
    dcb.BaudRate = (DWORD)baudrate;
    dcb.ByteSize = 8;                    // Data bit = 8bit
    dcb.Parity = NOPARITY;             // No parity
    dcb.StopBits = ONESTOPBIT;           // Stop bit = 1
    dcb.fParity = NOPARITY;             // No Parity check
    dcb.fBinary = 1;                    // Binary mode
    dcb.fNull = 0;                    // Get Null byte
    dcb.fAbortOnError = 0;
    dcb.fErrorChar = 0;
    // Not using XOn/XOff
    dcb.fOutX = 0;
    dcb.fInX = 0;
    // Not using H/W flow control
    dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcb.fDsrSensitivity = 0;
    dcb.fOutxDsrFlow = 0;
    dcb.fOutxCtsFlow = 0;
    
    if (SetCommState(portDataWindows[port_num].serial_handle_, &dcb) == FALSE)
        goto DXL_HAL_OPEN_ERROR;
    
    if (SetCommMask(portDataWindows[port_num].serial_handle_, 0) == FALSE) // Not using Comm event
        goto DXL_HAL_OPEN_ERROR;
    if (SetupComm(portDataWindows[port_num].serial_handle_, 4096, 4096) == FALSE) // Buffer size (Rx,Tx)
        goto DXL_HAL_OPEN_ERROR;
    if (PurgeComm(portDataWindows[port_num].serial_handle_, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR) == FALSE) // Clear buffer
        goto DXL_HAL_OPEN_ERROR;
    if (ClearCommError(portDataWindows[port_num].serial_handle_, &dwError, NULL) == FALSE)
        goto DXL_HAL_OPEN_ERROR;
    
    if (GetCommTimeouts(portDataWindows[port_num].serial_handle_, &timeouts) == FALSE)
        goto DXL_HAL_OPEN_ERROR;
    // Timeout (Not using timeout)
    // Immediatly return
    timeouts.ReadIntervalTimeout = 0;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 1; // must not be zero.
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;
    if (SetCommTimeouts(portDataWindows[port_num].serial_handle_, &timeouts) == FALSE)
        goto DXL_HAL_OPEN_ERROR;
    
    portDataWindows[port_num].tx_time_per_byte_ = (1000.0 / (double)portDataWindows[port_num].baudrate_) * 10.0;
    return true;
    
    DXL_HAL_OPEN_ERROR:
        ClosePortWindows(port_num);

    return false;
}