mirror of
				https://github.com/stargieg/bacnet-stack
				synced 2025-10-26 23:35:52 +08:00 
			
		
		
		
	 ab7db70c0b
			
		
	
	ab7db70c0b
	
	
	
		
			
			Changed info prints to go to stdout rather than stderr to clean up Wireshark extcap usage. ........
		
			
				
	
	
		
			631 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			631 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*####COPYRIGHTBEGIN####
 | |
|  -------------------------------------------
 | |
|  Copyright (C) 2004 Steve Karg
 | |
| 
 | |
|  This program is free software; you can redistribute it and/or
 | |
|  modify it under the terms of the GNU General Public License
 | |
|  as published by the Free Software Foundation; either version 2
 | |
|  of the License, or (at your option) any later version.
 | |
| 
 | |
|  This program is distributed in the hope that it will be useful,
 | |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  GNU General Public License for more details.
 | |
| 
 | |
|  You should have received a copy of the GNU General Public License
 | |
|  along with this program; if not, write to:
 | |
|  The Free Software Foundation, Inc.
 | |
|  59 Temple Place - Suite 330
 | |
|  Boston, MA  02111-1307
 | |
|  USA.
 | |
| 
 | |
|  As a special exception, if other files instantiate templates or
 | |
|  use macros or inline functions from this file, or you compile
 | |
|  this file and link it with other works to produce a work based
 | |
|  on this file, this file does not by itself cause the resulting
 | |
|  work to be covered by the GNU General Public License. However
 | |
|  the source code for this file must still be made available in
 | |
|  accordance with section (3) of the GNU General Public License.
 | |
| 
 | |
|  This exception does not invalidate any other reasons why a work
 | |
|  based on this file might be covered by the GNU General Public
 | |
|  License.
 | |
|  -------------------------------------------
 | |
| ####COPYRIGHTEND####*/
 | |
| 
 | |
| /** @file win32/rs485.c  Provides Windows-specific functions for RS-485 */
 | |
| 
 | |
| /* Suggested USB to RS485 devices:
 | |
|    B&B Electronics USOPTL4
 | |
|    SerialGear USB-COMi-SI-M
 | |
|    USB-RS485-WE-1800-BT
 | |
| */
 | |
| 
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| #include "mstp.h"
 | |
| #include "dlmstp.h"
 | |
| #define WIN32_LEAN_AND_MEAN
 | |
| #define STRICT 1
 | |
| #include <windows.h>
 | |
| #include "rs485.h"
 | |
| #include "fifo.h"
 | |
| 
 | |
| /* details from Serial Communications in Win32 at MSDN */
 | |
| 
 | |
| /* Win32 handle for the port */
 | |
| HANDLE RS485_Handle;
 | |
| /* Original COM Timeouts */
 | |
| static COMMTIMEOUTS RS485_Timeouts;
 | |
| /* COM port name COM1, COM2, etc  */
 | |
| static char RS485_Port_Name[256] = "COM4";
 | |
| /* baud rate - MS enumerated
 | |
|     CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
 | |
|     CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400,
 | |
|     CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 */
 | |
| static DWORD RS485_Baud = CBR_38400;
 | |
| /* ByteSize in bits: 5, 6, 7, 8 are valid */
 | |
| static DWORD RS485_ByteSize = 8;
 | |
| /* Parity - MS enumerated:
 | |
|     NOPARITY, EVENPARITY, ODDPARITY, MARKPARITY, SPACEPARITY */
 | |
| static DWORD RS485_Parity = NOPARITY;
 | |
| /* StopBits - MS enumerated:
 | |
|     ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS */
 | |
| static DWORD RS485_StopBits = ONESTOPBIT;
 | |
| /* DTRControl - MS enumerated:
 | |
|     DTR_CONTROL_ENABLE, DTR_CONTROL_DISABLE, DTR_CONTROL_HANDSHAKE */
 | |
| static DWORD RS485_DTRControl = DTR_CONTROL_DISABLE;
 | |
| /* RTSControl - MS enumerated:
 | |
|     RTS_CONTROL_ENABLE, RTS_CONTROL_DISABLE,
 | |
|     RTS_CONTROL_HANDSHAKE, RTS_CONTROL_TOGGLE */
 | |
| static DWORD RS485_RTSControl = RTS_CONTROL_DISABLE;
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Change the characters in a string to uppercase
 | |
| * RETURN:      nothing
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| static void strupper(
 | |
|     char *str)
 | |
| {
 | |
|     char *p;
 | |
|     for (p = str; *p != '\0'; ++p) {
 | |
|         *p = (char) toupper(*p);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
 | |
| *              receive mode.
 | |
| * RETURN:      none
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       expects a constant char ifname, or char from the heap
 | |
| *****************************************************************************/
 | |
| void RS485_Set_Interface(
 | |
|     char *ifname)
 | |
| {
 | |
|     /* For COM ports greater than 9 you have to use a special syntax
 | |
|        for CreateFile. The syntax also works for COM ports 1-9. */
 | |
|     /* http://support.microsoft.com/kb/115831 */
 | |
|     if (ifname) {
 | |
|         strupper(ifname);
 | |
|         if (strncmp("COM", ifname, 3) == 0) {
 | |
|             if (strlen(ifname) > 3) {
 | |
|                 sprintf(RS485_Port_Name, "\\\\.\\COM%i", atoi(ifname + 3));
 | |
|                 fprintf(stdout, "Adjusted interface name to %s\r\n",
 | |
|                     RS485_Port_Name);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Check the serial port to see if port exists
 | |
| * RETURN:      true if port exists
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| bool RS485_Interface_Valid(
 | |
|     unsigned port_number)
 | |
| {
 | |
|     HANDLE h = 0;
 | |
|     DWORD err = 0;
 | |
|     bool status = false;
 | |
|     char ifname[255] = "";
 | |
| 
 | |
|     sprintf(ifname, "\\\\.\\COM%u", port_number);
 | |
|     h = CreateFile(ifname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
 | |
|         OPEN_EXISTING, 0, NULL);
 | |
|     if (h == INVALID_HANDLE_VALUE) {
 | |
|         err = GetLastError();
 | |
|         if ((err == ERROR_ACCESS_DENIED) || (err == ERROR_GEN_FAILURE) ||
 | |
|             (err == ERROR_SHARING_VIOLATION) || (err == ERROR_SEM_TIMEOUT)) {
 | |
|             status = true;
 | |
|         }
 | |
|     } else {
 | |
|         status = true;
 | |
|         CloseHandle(h);
 | |
|     }
 | |
| 
 | |
|     return status;
 | |
| }
 | |
| 
 | |
| const char *RS485_Interface(
 | |
|     void)
 | |
| {
 | |
|     return RS485_Port_Name;
 | |
| }
 | |
| 
 | |
| void RS485_Print_Error(
 | |
|     void)
 | |
| {
 | |
|     LPVOID lpMsgBuf;
 | |
| 
 | |
|     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
 | |
|         NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | |
|         (LPTSTR) & lpMsgBuf, 0, NULL);
 | |
|     MessageBox(NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION);
 | |
|     LocalFree(lpMsgBuf);
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static void RS485_Configure_Status(
 | |
|     void)
 | |
| {
 | |
|     DCB dcb = { 0 };
 | |
|     COMMTIMEOUTS ctNew;
 | |
| 
 | |
| 
 | |
|     dcb.DCBlength = sizeof(dcb);
 | |
|     /* get current DCB settings */
 | |
|     if (!GetCommState(RS485_Handle, &dcb)) {
 | |
|         fprintf(stderr, "Unable to get status from %s\n", RS485_Port_Name);
 | |
|         RS485_Print_Error();
 | |
|         exit(1);
 | |
|     }
 | |
| 
 | |
|     /* update DCB rate, byte size, parity, and stop bits size */
 | |
|     dcb.BaudRate = RS485_Baud;
 | |
|     dcb.ByteSize = (unsigned char) RS485_ByteSize;
 | |
|     dcb.Parity = (unsigned char) RS485_Parity;
 | |
|     dcb.StopBits = (unsigned char) RS485_StopBits;
 | |
| 
 | |
|     /* update flow control settings */
 | |
|     dcb.fDtrControl = RS485_DTRControl;
 | |
|     dcb.fRtsControl = RS485_RTSControl;
 | |
|     /*
 | |
|        dcb.fOutxCtsFlow    = CTSOUTFLOW(TTYInfo);
 | |
|        dcb.fOutxDsrFlow    = DSROUTFLOW(TTYInfo);
 | |
|        dcb.fDsrSensitivity = DSRINFLOW(TTYInfo);
 | |
|        dcb.fOutX           = XONXOFFOUTFLOW(TTYInfo);
 | |
|        dcb.fInX            = XONXOFFINFLOW(TTYInfo);
 | |
|        dcb.fTXContinueOnXoff = TXAFTERXOFFSENT(TTYInfo);
 | |
|        dcb.XonChar         = XONCHAR(TTYInfo);
 | |
|        dcb.XoffChar        = XOFFCHAR(TTYInfo);
 | |
|        dcb.XonLim          = XONLIMIT(TTYInfo);
 | |
|        dcb.XoffLim         = XOFFLIMIT(TTYInfo);
 | |
|        // DCB settings not in the user's control
 | |
|        dcb.fParity = TRUE;
 | |
|      */
 | |
|     if (!SetCommState(RS485_Handle, &dcb)) {
 | |
|         fprintf(stderr, "Unable to set status on %s\n", RS485_Port_Name);
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
|     /* configure the COM port timeout values */
 | |
|     ctNew.ReadIntervalTimeout = MAXDWORD;
 | |
|     ctNew.ReadTotalTimeoutMultiplier = MAXDWORD;
 | |
|     ctNew.ReadTotalTimeoutConstant = 1000;
 | |
|     ctNew.WriteTotalTimeoutMultiplier = 0;
 | |
|     ctNew.WriteTotalTimeoutConstant = 0;
 | |
|     if (!SetCommTimeouts(RS485_Handle, &ctNew)) {
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
|     /* Get rid of any stray characters */
 | |
|     if (!PurgeComm(RS485_Handle, PURGE_TXABORT | PURGE_RXABORT)) {
 | |
|         fprintf(stderr, "Unable to purge %s\n", RS485_Port_Name);
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
|     /* Set the Comm buffer size */
 | |
|     SetupComm(RS485_Handle, MAX_MPDU, MAX_MPDU);
 | |
|     /* raise DTR */
 | |
|     if (!EscapeCommFunction(RS485_Handle, SETDTR)) {
 | |
|         fprintf(stderr, "Unable to set DTR on %s\n", RS485_Port_Name);
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Cleans up any handles that were created at startup.
 | |
| * RETURN:      none
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| static void RS485_Cleanup(
 | |
|     void)
 | |
| {
 | |
|     if (!EscapeCommFunction(RS485_Handle, CLRDTR)) {
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
| 
 | |
|     if (!SetCommTimeouts(RS485_Handle, &RS485_Timeouts)) {
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
| 
 | |
|     CloseHandle(RS485_Handle);
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Initializes the RS485 hardware and variables, and starts in
 | |
| *              receive mode.
 | |
| * RETURN:      none
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| void RS485_Initialize(
 | |
|     void)
 | |
| {
 | |
|     RS485_Handle =
 | |
|         CreateFile(RS485_Port_Name, GENERIC_READ | GENERIC_WRITE, 0, 0,
 | |
|         OPEN_EXISTING,
 | |
|         /*FILE_FLAG_OVERLAPPED */ 0,
 | |
|         0);
 | |
|     if (RS485_Handle == INVALID_HANDLE_VALUE) {
 | |
|         fprintf(stderr, "Unable to open %s\n", RS485_Port_Name);
 | |
|         RS485_Print_Error();
 | |
|         exit(1);
 | |
|     }
 | |
|     if (!GetCommTimeouts(RS485_Handle, &RS485_Timeouts)) {
 | |
|         RS485_Print_Error();
 | |
|     }
 | |
|     RS485_Configure_Status();
 | |
| #if PRINT_ENABLED
 | |
|     fprintf(stdout, "RS485 Interface: %s\n", RS485_Port_Name);
 | |
| #endif
 | |
| 
 | |
|     atexit(RS485_Cleanup);
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Returns the baud rate that we are currently running at
 | |
| * RETURN:      none
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| uint32_t RS485_Get_Baud_Rate(
 | |
|     void)
 | |
| {
 | |
|     switch (RS485_Baud) {
 | |
|         case CBR_19200:
 | |
|             return 19200;
 | |
|         case CBR_38400:
 | |
|             return 38400;
 | |
|         case CBR_57600:
 | |
|             return 57600;
 | |
|         case CBR_115200:
 | |
|             return 115200;
 | |
|         case CBR_110:
 | |
|             return 110;
 | |
|         case CBR_300:
 | |
|             return 300;
 | |
|         case CBR_600:
 | |
|             return 600;
 | |
|         case CBR_1200:
 | |
|             return 1200;
 | |
|         case CBR_2400:
 | |
|             return 2400;
 | |
|         case CBR_4800:
 | |
|             return 4800;
 | |
|         case CBR_14400:
 | |
|             return 14400;
 | |
|         case CBR_56000:
 | |
|             return 56000;
 | |
|         case CBR_128000:
 | |
|             return 128000;
 | |
|         case CBR_256000:
 | |
|             return 256000;
 | |
|         case 76800:
 | |
|             /* See comments in RS485_Set_Baud_Rate() below
 | |
|              * also look at definition of CBR_xx in winbase.h
 | |
|              * some serial drivers will only support the defined
 | |
|              * baud rates but others will try and configure the
 | |
|              * requested baud rate (or as close as they can get)
 | |
|              */
 | |
|             return 76800;
 | |
|         case CBR_9600:
 | |
|         default:
 | |
|             return 9600;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
| * DESCRIPTION: Sets the baud rate for the chip USART
 | |
| * RETURN:      none
 | |
| * ALGORITHM:   none
 | |
| * NOTES:       none
 | |
| *****************************************************************************/
 | |
| bool RS485_Set_Baud_Rate(
 | |
|     uint32_t baud)
 | |
| {
 | |
|     bool valid = true;
 | |
| 
 | |
|     switch (baud) {
 | |
|         case 9600:
 | |
|             RS485_Baud = CBR_9600;
 | |
|             break;
 | |
|         case 19200:
 | |
|             RS485_Baud = CBR_19200;
 | |
|             break;
 | |
|         case 38400:
 | |
|             RS485_Baud = CBR_38400;
 | |
|             break;
 | |
|         case 57600:
 | |
|             RS485_Baud = CBR_57600;
 | |
|             break;
 | |
|         case 115200:
 | |
|             RS485_Baud = CBR_115200;
 | |
|             break;
 | |
|         case 110:
 | |
|             RS485_Baud = CBR_110;
 | |
|             break;
 | |
|         case 300:
 | |
|             RS485_Baud = CBR_300;
 | |
|             break;
 | |
|         case 600:
 | |
|             RS485_Baud = CBR_600;
 | |
|             break;
 | |
|         case 1200:
 | |
|             RS485_Baud = CBR_1200;
 | |
|             break;
 | |
|         case 2400:
 | |
|             RS485_Baud = CBR_2400;
 | |
|             break;
 | |
|         case 4800:
 | |
|             RS485_Baud = CBR_4800;
 | |
|             break;
 | |
|         case 14400:
 | |
|             RS485_Baud = CBR_14400;
 | |
|             break;
 | |
|         case 56000:
 | |
|             RS485_Baud = CBR_56000;
 | |
|             break;
 | |
|         case 128000:
 | |
|             RS485_Baud = CBR_128000;
 | |
|             break;
 | |
|         case 256000:
 | |
|             RS485_Baud = CBR_256000;
 | |
|             break;
 | |
|         case 76800:
 | |
|             /* I'm using the B&B Electronics USOPTL4 USB RS485 adapter
 | |
|              * on Win 7 and building with VS2008 Express Edition and it
 | |
|              * seems to work for the most part if I use the following.
 | |
|              * I get the occasional data errors especially if the devices
 | |
|              * are transmitting with 1 stop bit (some devices receive with
 | |
|              * 1 stop bit but effectivly end up transmitting with 2 stop
 | |
|              * bits, usually because of synchroisation issues in some UARTs
 | |
|              * which mean that if you wait until the serialiser has finished
 | |
|              * with the current character and then load the TX buffer it has
 | |
|              * to wait until the next bit boundary to start transmitting.
 | |
|              * PMcS
 | |
|              */
 | |
|             RS485_Baud = 76800;
 | |
|             break;
 | |
|         default:
 | |
|             valid = false;
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     if (valid) {
 | |
|         /* FIXME: store the baud rate */
 | |
|     }
 | |
| 
 | |
|     return valid;
 | |
| }
 | |
| 
 | |
| /* Transmits a Frame on the wire */
 | |
| void RS485_Send_Frame(
 | |
|     volatile struct mstp_port_struct_t *mstp_port,      /* port specific data */
 | |
|     uint8_t * buffer,   /* frame to send (up to 501 bytes of data) */
 | |
|     uint16_t nbytes)
 | |
| {       /* number of bytes of data (up to 501) */
 | |
|     DWORD dwWritten = 0;
 | |
| 
 | |
|     if (mstp_port) {
 | |
|         uint32_t baud;
 | |
|         uint8_t turnaround_time;
 | |
|         baud = RS485_Get_Baud_Rate();
 | |
|         /* wait about 40 bit times since reception */
 | |
|         if (baud == 9600)
 | |
|             turnaround_time = 4;
 | |
|         else if (baud == 19200)
 | |
|             turnaround_time = 2;
 | |
|         else
 | |
|             turnaround_time = 2;
 | |
|         while (mstp_port->SilenceTimer(NULL) < turnaround_time) {
 | |
|             /* do nothing - wait for timer to increment */
 | |
|         };
 | |
|     }
 | |
|     WriteFile(RS485_Handle, buffer, nbytes, &dwWritten, NULL);
 | |
| 
 | |
|     /* per MSTP spec, reset SilenceTimer after each byte is sent */
 | |
|     if (mstp_port) {
 | |
|         mstp_port->SilenceTimerReset(NULL);
 | |
|     }
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| /* called by timer, interrupt(?) or other thread */
 | |
| void RS485_Check_UART_Data(
 | |
|     volatile struct mstp_port_struct_t *mstp_port)
 | |
| {
 | |
|     char lpBuf[1];
 | |
|     DWORD dwRead = 0;
 | |
| 
 | |
|     if (mstp_port->ReceiveError == true) {
 | |
|         /* wait for state machine to clear this */
 | |
|     }
 | |
|     /* wait for state machine to read from the DataRegister */
 | |
|     else if (mstp_port->DataAvailable == false) {
 | |
|         /* check for data */
 | |
|         if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) {
 | |
|             if (GetLastError() != ERROR_IO_PENDING) {
 | |
|                 mstp_port->ReceiveError = TRUE;
 | |
|             }
 | |
|         } else {
 | |
|             if (dwRead) {
 | |
|                 mstp_port->DataRegister = lpBuf[0];
 | |
|                 mstp_port->DataAvailable = TRUE;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*************************************************************************
 | |
| * Description: print available COM ports
 | |
| * Returns: none
 | |
| * Notes: none
 | |
| **************************************************************************/
 | |
| void RS485_Print_Ports(
 | |
|     void)
 | |
| {
 | |
|     unsigned i = 0;
 | |
| 
 | |
|     /* try to open all 255 COM ports */
 | |
|     for (i = 1; i < 256; i++) {
 | |
|         if (RS485_Interface_Valid(i)) {
 | |
|             /* note: format for Wireshark ExtCap */
 | |
|             printf("interface {value=COM%u}"
 | |
|                 "{display=BACnet MS/TP on COM%u}\n", i, i);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #ifdef TEST_RS485
 | |
| 
 | |
| #include "mstpdef.h"
 | |
| 
 | |
| 
 | |
| static void test_transmit_task(
 | |
|     void *pArg)
 | |
| {
 | |
|     char *TxBuf = "BACnet MS/TP";
 | |
|     size_t len = strlen(TxBuf) + 1;
 | |
| 
 | |
|     while (TRUE) {
 | |
|         Sleep(1000);
 | |
|         RS485_Send_Frame(NULL, &TxBuf[0], len);
 | |
|     }
 | |
| }
 | |
| 
 | |
| #if defined(_WIN32)
 | |
| static BOOL WINAPI CtrlCHandler(
 | |
|     DWORD dwCtrlType)
 | |
| {
 | |
|     dwCtrlType = dwCtrlType;
 | |
|     exit(0);
 | |
|     return TRUE;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int ascii_hex_to_int(
 | |
|     char ch)
 | |
| {
 | |
|     int rv = -1;
 | |
| 
 | |
|     if ((ch >= '0') && (ch <= '9')) {
 | |
|         rv = ch - '0';
 | |
|     } else if ((ch >= 'a') && (ch <= 'f')) {
 | |
|         rv = 10 + ch - 'a';
 | |
|     } else if ((ch >= 'A') && (ch <= 'F')) {
 | |
|         rv = 10 + ch - 'a';
 | |
|     }
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| int main(
 | |
|     int argc,
 | |
|     char *argv[])
 | |
| {
 | |
|     unsigned long hThread = 0;
 | |
|     uint32_t arg_value = 0;
 | |
|     char lpBuf[1];
 | |
|     DWORD dwRead = 0;
 | |
|     unsigned i = 0, len = 0, count = 0;
 | |
|     char hex_pair[5] = "0xff";
 | |
|     char ch = ' ';
 | |
|     int lsb = 0, msb = 0;
 | |
|     long my_baud = 38400;
 | |
|     uint8_t buffer[501] = { 0 };
 | |
| 
 | |
|     if (argc > 1) {
 | |
|         RS485_Set_Interface(argv[1]);
 | |
|     }
 | |
|     if (argc > 2) {
 | |
|         my_baud = strtol(argv[2], NULL, 0);
 | |
|     }
 | |
|     RS485_Set_Baud_Rate(my_baud);
 | |
|     RS485_Initialize();
 | |
| #if defined(_WIN32)
 | |
|     SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
 | |
|     SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE);
 | |
| #endif
 | |
| #ifdef TEST_RS485_TRANSMIT
 | |
|     /* read a stream of characters from stdin or argument */
 | |
|     if (argc > 3) {
 | |
|         len = strlen(argv[3]);
 | |
|         for (i = 0; i < len; i++) {
 | |
|             /* grab pairs of hex characters, skip spaces */
 | |
|             ch = argv[3][i];
 | |
|             if (ch == ' ') {
 | |
|                 continue;
 | |
|             }
 | |
|             msb = ascii_hex_to_int(ch);
 | |
|             if (msb >= 0) {
 | |
|                 i++;
 | |
|                 ch = argv[3][i];
 | |
|                 lsb = ascii_hex_to_int(ch);
 | |
|                 if (lsb >= 0) {
 | |
|                     buffer[count] = msb << 4 | lsb;
 | |
|                 } else {
 | |
|                     buffer[count] = msb;
 | |
|                 }
 | |
|                 count++;
 | |
|                 if (count >= sizeof(buffer)) {
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         RS485_Send_Frame(NULL, buffer, count);
 | |
|     }
 | |
| #endif
 | |
| #ifdef TEST_RS485_RECEIVE
 | |
|     /* receive task */
 | |
|     for (;;) {
 | |
|         if (!ReadFile(RS485_Handle, lpBuf, sizeof(lpBuf), &dwRead, NULL)) {
 | |
|             if (GetLastError() != ERROR_IO_PENDING) {
 | |
|                 RS485_Print_Error();
 | |
|             }
 | |
|         } else {
 | |
|             /* print any characters received */
 | |
|             if (dwRead) {
 | |
|                 for (i = 0; i < dwRead; i++) {
 | |
|                     fprintf(stderr, "%02X ", lpBuf[i]);
 | |
|                 }
 | |
|             }
 | |
|             dwRead = 0;
 | |
|         }
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| #endif
 |