Short answer = Yes.
You can use either D2XX or standard Win32 COM-port programming in the PC.
I'm not sure what you intend by an interrupt, but I assume you want your program
to do something else until Arduino sends something.
That's easy, just write a separate thread that waits on the comm device,
pretty much the same way for both D2XX and standard serial.
You definitely don't need to modify your Arduino for this.
One advantage of using standard serial port is that you can get notifications
from the system when a port is added or removed, e.g when you connect Arduino,
but since you seem to favor D2XX I'll stick to that.
I share a piece of code you can use as a starting point with D2XX.
NOTE: I just cut this from my project and removed some stuff that's
irrelevant to the issue, so you might need to fix a thing or two.
The tedious setup before the thread's loop is just because I was checking out
all the available options. Just remove what you don't need
HTH // L.
#include <ftd2xx.h>
// Generic definitions
#define THREADPROC DWORD WINAPI // Thread function
#define DIALOGPROC BOOL CALLBACK // Dialog function
#define STOP_CODE 999 // IO completion port termination command
#define BAD_HANDLE INVALID_HANDLE_VALUE // Finger saver ;)
#define GENERIC_RW GENERIC_READ | GENERIC_WRITE // -"-
#define SHARE_NONE 0 // Semantic clarity
#define DEF_PORT "FT232R USB UART"
// Xon/Xoff chars
#define XON_CH STX
#define XOFF_CH ETX
// D2XX Modem status
#define MDM_CTS 0x10 // Clear To Send
#define MDM_DSR 0x20 // Data Set Ready
#define MDM_RI 0x40 // Ring Indicator
#define MDM_DCD 0x80 // Data Carrier Detect
// D2XX Line status
#define LIN_OE 0x02 // Overrun Error
#define LIN_PE 0x04 // Parity Error
#define LIN_FE 0x08 // Framing Error
#define LIN_BI 0x10 // Break Interrupt
typedef struct _thrData { // Thread data
PSTR ComPort; // IN - Port name e.g "FT232R USB UART", "COM19"
BOOL Run; // Thread loop condition
HANDLE hThr; // Thread handle
DWORD idThr; // Thread ID
HANDLE hPort; // COM or D2XX port file handle
HANDLE hSignal; // Event handle
} TThrData, *PThrData;
THREADPROC Ftd2Thread( PVOID arg )
{
FT_STATUS rc; // result code
DWORD dwStat;
BYTE lineStat, modmStat;
PThrData pd = (PThrData) arg; // Thread data (Handles, run-flag and what-not)
pd->Run = TRUE;
// pd->ComPort is smth like "FT232R USB UART"
rc = FT_OpenEx( pd->ComPort, FT_OPEN_BY_DESCRIPTION, &pd->hPort );
if (rc != 0) {
DPrint( "FT_OpenEx('%s') failed. Error %d\n",pd->ComPort,rc );
__fterr_Bye:
pd->Run = FALSE;
return rc;
}
rc = FT_SetUSBParameters( pd->hPort,64,64 );
if (rc != 0) {
DPrint( "FT_SetUSBParameters(64,64) failed. Error %d\n",rc );
}
rc = FT_SetLatencyTimer( pd->hPort,2 );
if (rc != 0) {
UCHAR msLtc = 0;
DPrint( "FT_SetLatencyTimer(2ms) failed. Error %d\n",rc );
rc = FT_GetLatencyTimer( pd->hPort,&msLtc );
if (rc == 0) DPrint( "FT_GetLatencyTimer returned %d (ms)\n",(int)msLtc );
}
rc = FT_SetDataCharacteristics( pd->hPort, 8,0,0 ); // 8 data, 1 stop, no parity
// or use FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE
if (rc != 0) {
DPrint( "FT_SetDataCharacteristics failed. Error %d\n",rc );
__fterr_ClosePort:
FT_Close( pd->hPort );
pd->hPort = NULL;
goto __fterr_Bye;
}
rc = FT_SetBaudRate( pd->hPort,BAUD_RATE ); // 921600 (115200*8)
if (rc != 0) {
DPrint( "FT_SetBaudRate failed. Error %d\n",rc );
goto __fterr_ClosePort;
}
rc = FT_SetFlowControl( pd->hPort,FT_FLOW_DTR_DSR, XON_CH, XOFF_CH ); // FT_FLOW_NONE
if (rc != 0) {
DPrint( "FT_SetFlowControl failed. Error %d\n",rc );
goto __fterr_ClosePort;
}
pd->hSignal = CreateEvent( NULL,TRUE,FALSE,NULL );
if (pd->hSignal == NULL) {
rc = GetLastError();
DPrint( "CreateEvent failed: %s\n",ErrorMsg( rc ));
goto __fterr_ClosePort;
}
#define EVENT_MASK FT_EVENT_RXCHAR| FT_EVENT_MODEM_STATUS
rc = FT_SetEventNotification( pd->hPort, EVENT_MASK, pd->hSignal );
if (rc != 0) {
DPrint( "FT_SetEventNotification failed. Error %d\n",rc );
pd->hSignal = MyCloseHandle( pd->hSignal );
goto __fterr_ClosePort;
}
DPrint( "Enter Fdt2Thread loop\n" );
while( pd->Run )
{
DWORD cbRcv=0;
if (WaitForSingleObject( pd->hSignal,1000 ) == WAIT_OBJECT_0)
{
DWORD cbRx,cbRd,cbTx,dwEvt;
BYTE Buffer[64];
ResetEvent( pd->hSignal );
rc = FT_GetStatus( pd->hPort, &cbRx, &cbTx, &dwEvt );
TRACE(( "RX Chars = %d, TX Chars = %d\n",cbRx,cbTx ));
if (dwEvt & FT_EVENT_MODEM_STATUS) {
rc = FT_GetModemStatus( pd->hPort, &dwStat );
modmStat = LOBYTE( LOWORD( dwStat ));
lineStat = HIBYTE( LOWORD( dwStat ));
TRACE(( "Modem Status: %s%s%s%s\n",
(modmStat & MDM_CTS) ? "CTS ":"",
(modmStat & MDM_DSR) ? "DSR ":"",
(modmStat & MDM_RI) ? "RING ":"",
(modmStat & MDM_DCD) ? "CARRIER":""
));
TRACE(( "Line Status: %s%s%s%s\n",
(lineStat & LIN_OE) ? "OVRUN ":"", // Overrun Error
(lineStat & LIN_PE) ? "PARITY ":"", // Parity Error
(lineStat & LIN_FE) ? "FRAME ":"", // Framing Error
(lineStat & LIN_BI) ? "BREAK":"" // Break Interrupt
));
}
else if (dwEvt & FT_EVENT_RXCHAR) {
//rc = FT_GetQueueStatus( pd->hPort, &cbRx );
rc = FT_Read( pd->hPort, &Buffer[cbRcv], cbRx, &cbRd );
if (rc == 0) {
cbRcv += cbRx;
/*
Process your data
*/
}
}
}
}
DPrint( "Leave Fdt2Thread loop\n" );
CloseHandle( pd->hSignal );
rc = FT_Close( pd->hPort );
pd->hSignal = NULL;
pd->hPort = NULL;
pd->hMidiOut = NULL;
return rc;
}
bool StartCOMThread( PThrData pd )
{
DWORD flg = CREATE_SUSPENDED;
if (pd->hThr) return true; // Already running
if (!pd->ComPort) pd->ComPort = DEF_PORT; // Use default
pd->hThr = CreateThread( &DefSec,0, Ftd2Thread, PVOID(pd), flg, &pd->idThr );
if (pd->hThr == NULL) {
GEN_ERROR( "Create ComThread" );
return false;
}
// Boost process and thread priority *to the hilt* to avoid latency.
// Also decrease priority for the UI thread, since whole process gets boosted.
// NOTE: Realtime priority processes pre-empt even system tasks and must
// be used sparingly and with great care, since they can preempt mouse etc..
if (!SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ))
GEN_ERROR( "SetPriorityClass" );
if (!SetThreadPriority( pd->hThr, THREAD_PRIORITY_TIME_CRITICAL ))
GEN_ERROR( "SetThreadPriority (COM)" );
if (!SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_LOWEST ))
GEN_ERROR( "SetThreadPriority (UI)" );
ResumeThread( pd->hThr ); // Go
return true;
}
void StopCOMThread( PThrData pd )
{
if (!pd->hThr) return; // Not running, nothing to do
// Relax the priorities
SetPriorityClass( GetCurrentProcess(), NORMAL_PRIORITY_CLASS );
SetThreadPriority( pd->hThr, THREAD_PRIORITY_NORMAL );
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
#ifdef USE_FTDI_D2XX
BOOL ok = pd->Run;
pd->Run = FALSE;
#else
BOOL ok = SetCommMask( pd->hPort, 0 );
// Calling SetCommMask while an overlapped WaitCommEvent is in progress
// cause WaitCommEvent/GetOverlappedResult to return TRUE immediately,
// with the event mask set to zero.
#endif
// Wait a bit for the thread to exit.
if (ok) ok = (WaitForSingleObject( pd->hThr,5000 ) == WAIT_OBJECT_0);
if (!ok) {
// If it didn't, kill it by force.
DPrint( "Forced termination of unresponsive thread.\n" );
TerminateThread( pd->hThr, EXIT_FAILURE );
// Do the cleanup we bypassed when killing the thread
#ifdef USE_FTDI_D2XX
if (pd->hSignal) CloseHandle( pd->hSignal );
if (pd->hPort) FT_Close( pd->hPort );
#else
if (pd->hPort) CloseHandle( pd->hPort );
#endif
pd->hSignal = NULL;
pd->hPort = NULL;
}
pd->hThr = MyCloseHandle( pd->hThr ); // MyCloseHandle rtn NULL on success
if (pd->hThr == NULL) pd->idThr = 0; // Closed okay
}