Using the Labview_DLL.dll to access the USB datastream

Post Reply
djw

Using the Labview_DLL.dll to access the USB datastream

Post by djw »

Dear Forum,

I am currently writing some software to read the datastream coming from the USB Receiver. via the PCs USB port. This data will then be used to control some real-time EEG software. I have written a small program that will aquire a block of data from the USB port and write it to a file for further inspection.

Here is the code:

Code: Select all








__declspec(dllimport) HANDLE OPEN_DRIVER_ASYNC(void);
__declspec(dllimport) bool USB_WRITE(HANDLE hdevice, PCHAR usbdata);
__declspec(dllimport) bool READ_MULTIPLE_SWEEPS(HANDLE hdevice, PCHAR data, DWORD nBytesToRead);
__declspec(dllimport) bool READ_POINTER(HANDLE hdevice, PDWORD pointer);
__declspec(dllimport) CLOSE_DRIVER_ASYNC(HANDLE hdevice);

//---------------------------------------------------------------------------

char usbdata[63];
char data[33554432];
DWORD nBytesToRead = 33554432;


int main(int argc, char* argv[])
{
        HANDLE hdevice=OPEN_DRIVER_ASYNC();
        for (int i=0; i<64; i++)
        {
                usbdata[i]=0;
        }

        USB_WRITE(hdevice, usbdata);

        READ_MULTIPLE_SWEEPS(hdevice, data, nBytesToRead);

        usbdata[0]=1;
        USB_WRITE(hdevice, usbdata);
        usbdata[0]=0;

        USB_WRITE(hdevice, usbdata);
        CLOSE_DRIVER_ASYNC(hdevice);

        return 0;
}

When I run the above code I get "Linker Error : Unresolved external..... " . I am using Borland C++ Builder v6.0 and have added the DLL file to the project using the "Add to project..." menu option. Any assistance you could give me will be greatly appreciated :D .

Many thanks
Dave.

diz
Posts: 5
Joined: Sat Oct 16, 2004 8:54 am
Location: St.-Petersburg, Russia
Contact:

Re: Using the Labview_DLL.dll to access the USB datastream

Post by diz »

Hi, djw.

It seems to me your are trying to use 'static link' with the DLL, but it's hardly possible withour the proper .Lib.

I would prefer to use 'dinamic loading'. Sorry, but following example will be in VC++

Code: Select all

typedef HANDLE (*dOPEN_DRIVER_ASYNC)(void);
typedef unsigned char (*dUSB_WRITE)(HANDLE HAN, unsigned char *data);
typedef unsigned char (*dREAD_MULTIPLE_SWEEPS)(HANDLE HAN, long *data, long nBytesToRead);
typedef unsigned char (*dREAD_POINTER)(HANDLE HAN, unsigned long *Pointer);
typedef unsigned char (*dCLOSE_DRIVER_ASYNC)(HANDLE HAN);
typedef unsigned char (*dCLOSE_DRIVER)(HANDLE HAN);


dOPEN_DRIVER_ASYNC OPEN_DRIVER_ASYNC;
dUSB_WRITE USB_WRITE;
dREAD_MULTIPLE_SWEEPS READ_MULTIPLE_SWEEPS;
dREAD_POINTER READ_POINTER;
dCLOSE_DRIVER_ASYNC CLOSE_DRIVER_ASYNC;
dCLOSE_DRIVER CLOSE_DRIVER;


try 
{
		//init
		OPEN_DRIVER_ASYNC = 0;
		USB_WRITE = 0;
		READ_MULTIPLE_SWEEPS = 0;
		READ_POINTER = 0;
		CLOSE_DRIVER_ASYNC = 0;
		CLOSE_DRIVER = 0;

		// To begin with - Load Library
		if(!(hLib = LoadLibrary("Labview_DLL.dll")))
			throw("Drivers are not present");
		if ( !(OPEN_DRIVER_ASYNC = (dOPEN_DRIVER_ASYNC)GetProcAddress(hLib,"OPEN_DRIVER_ASYNC")) ) throw("No OPEN_DRIVER_ASYNC");
		if ( !(USB_WRITE = (dUSB_WRITE)GetProcAddress(hLib,"USB_WRITE")) ) throw("No USB_WRITE");
		if ( !(READ_MULTIPLE_SWEEPS = (dREAD_MULTIPLE_SWEEPS)GetProcAddress(hLib,"READ_MULTIPLE_SWEEPS")) ) throw("No READ_MULTIPLE_SWEEPS");
		if ( !(READ_POINTER = (dREAD_POINTER)GetProcAddress(hLib,"READ_POINTER")) ) throw("No READ_POINTER");
		if ( !(CLOSE_DRIVER_ASYNC = (dCLOSE_DRIVER_ASYNC)GetProcAddress(hLib,"CLOSE_DRIVER_ASYNC")) ) throw("No CLOSE_DRIVER_ASYNC");
		if ( !(CLOSE_DRIVER = (dCLOSE_DRIVER)GetProcAddress(hLib,"CLOSE_DRIVER")) ) throw("No CLOSE_DRIVER");

}
	catch (char* cp) {
		MessageBox(0,cp,"Error",MB_OK|MB_ICONERROR);
	}

djw wrote:Dear Forum,

I am currently writing some software to read the datastream coming from the USB Receiver. via the PCs USB port. This data will then be used to control some real-time EEG software. I have written a small program that will aquire a block of data from the USB port and write it to a file for further inspection.
.........................................
When I run the above code I get "Linker Error : Unresolved external..... " . I am using Borland C++ Builder v6.0 and have added the DLL file to the project using the "Add to project..." menu option. Any assistance you could give me will be greatly appreciated :D .

Many thanks
Dave.
WBR - DIZ

djw

Accessing the USB port/Receiver

Post by djw »

Hi diz,

Thank you for your help. I have implemented the dynamic link method of calling the DLL as you suggest.

My next problem is in acquiring data from the USB Receiver. When I run my program a small amount of data is collected from the Receiver (green LED comes on) then about 1 second later data collection stops and the green LED goes off. I have set both the data buffer and the nBytestoRead variable to be 32000000.

Is there any way to continually get data from the USB port and stream it into a file or display it onscreen? Have you tried accessing the USB port directly yourself, if so do you have a code fragment to show how this can be achieved or do you know of anyone who may be able to assist me with this.

Thanks
Dave.

diz
Posts: 5
Joined: Sat Oct 16, 2004 8:54 am
Location: St.-Petersburg, Russia
Contact:

Re: Accessing the USB port/Receiver

Post by diz »

Hi, djw!

It seems to me it would be easier to locate an error in your code... There may be many courses...

First, I think that USB driver likes 'round numbers' (in binary sense): so, try to use something like
1024*1024*8 - for 32K buffer (but 8 Ksamples of long words)

Second advice will be to look into the 'LabView' code of the ActiView (it is possible to do it with the DemoVeriosn of LabView) - and reproduce the alogorithms...

Code: Select all

          if (m_hAct2 == INVALID_HANDLE_VALUE) {
          //open driver
               m_hAct2 = OPEN_DRIVER_ASYNC();
               if (m_hAct2 == INVALID_HANDLE_VALUE) 
                    return 0; //throw("Driver not opened!");

               m_ucUsb[0] = 0;
               USB_WRITE(m_hAct2,m_ucUsb);

               

               READ_MULTIPLE_SWEEPS(m_hAct2,m_pBuf,BUFSIZE*4);
               //stop???
               m_ucUsb[0] = 0xFF;
               USB_WRITE(m_hAct2,m_ucUsb);
               m_dwPointer = 0;
               m_i64Buf = m_i64Disp = 0;
          }

          for (i = 0; i < 10; i++) {
               READ_POINTER(m_hAct2,&lPointer);
               if (lPointer/4 != m_dwPointer) break;
               Sleep(10);
          }

          lPointer /= 4;
          if (m_dwPointer == lPointer) 
               throw("No cord or power");
          ULONG lNew;
          if (lPointer >= m_dwPointer)
               lNew = lPointer - m_dwPointer;
          else {
               lNew = lPointer + BUFSIZE - m_dwPointer;
          }

          dwMode = m_pBuf[m_i64Disp%(BUFSIZE)+1];
          m_aMode= actMode(dwMode);
          m_nActChans = m_aMode.GetBufChans();

               m_i64Buf += lNew;


               DWORD dwDispBufPos = (DWORD)(m_i64Disp%(BUFSIZE));
               DWORD dwSynch = m_pBuf[dwDispBufPos];
               if (dwSynch != 0xFFFFFF00) throw("Synch lost");
               m_dwPointer = lPointer;


[/code]
WBR - DIZ

diz
Posts: 5
Joined: Sat Oct 16, 2004 8:54 am
Location: St.-Petersburg, Russia
Contact:

Re: Accessing the USB port/Receiver

Post by diz »

As I've received a number of questions concerning the С++ code, I'll try to answer them here....

The working (I hope) example of this code usage together with brief explanation can be found at
http://diz-vara.mail333.com/ActiveD.zip

Code: Select all

              READ_MULTIPLE_SWEEPS(m_hAct2,m_pBuf,BUFSIZE*4);  
// BUFSIZE is 8M , right ? I follow the step in the forum, and m_pBuf is a PCHAR, size 32M, and the third parameter is nbyteToRead, Is it the same as BUFSIZE ? I have no idea...
I use

Code: Select all


long *m_pBuf;
m_pBuf = new long[BUFSIZE];
So, the size of the buffer is 8 MegaSamples - but 32 MB

Code: Select all

          for (i = 0; i < 10; i++) { 
               READ_POINTER(m_hAct2,&lPointer);       //I initial the lPointer as 0 , is it right? 
               if (lPointer/4 != m_dwPointer) break;  //each time it break here... because after READ_POINTER, lpointer is never 0 again, is it means I have synchronize or mean something else ? 
               Sleep(10); 
          } 

        lPointer /= 4; 
        if (m_dwPointer == lPointer) 
               throw("No cord or power"); 


Yes, lPointer is initialized with 0.

This portion is just a 'translation' of ActiView code into C++. It tests for the new data 10 times (with 10ms interval). If the device is switched off (or the cable is not connected), the pointer will not change - and the corresponding exception will be thrown. Otherwise you'll receive new value of the pointer.

Code: Select all

          ULONG lNew; 
          if (lPointer >= m_dwPointer) 
               lNew = lPointer - m_dwPointer; 
          else { 
               lNew = lPointer + BUFSIZE - m_dwPointer; 
          } 

Code: Select all

        m_i64Buf += lNew;       // so , m_i64Buf is how many data had been buffer ? 
Yes, it's the total amount of data buffered. I've decided that one _int64 counter will be enough for my purposes... You can calculate the possibility to overflow this counter youself :D

Code: Select all

             DWORD dwDispBufPos = (DWORD)(m_i64Disp%(BUFSIZE)); 
             DWORD dwSynch = m_pBuf[dwDispBufPos]; 
             if (dwSynch != 0xFFFFFF00) throw("Synch lost");          //Never get Sysch here... ,Because Never Change m_i64Disp ....it's just zero...
Yes, I change m_i64Disp at the end of timer function (after processing of new data). The real code for detecting synchonization problems will be like that:

Code: Select all

		DWORD dwDispBufPos = (DWORD)(m_i64Disp%(BUFSIZE));
		DWORD dwSynch(0);

		int a,b;
		_int64 One(2);
		long lTmp;

		b = (dwDispBufPos+1)&(BUFSIZE-1);
    dwMode = (m_pBuf[b] & 0x2E000000) >> 25;
		m_aMode= actMode(dwMode);
		m_nActChans = m_aMode.GetBufChans();

    if (m_nActChans)
      nDisp = (int)(m_i64Buf-m_i64Disp)/m_nActChans;
    else
      nDisp = 0;

		for (i = 0; i < nDisp; i++)
		{
			a = (dwDispBufPos+i*m_nActChans)&(BUFSIZE-1);
			b = (dwDispBufPos+i*m_nActChans+1)&(BUFSIZE-1);
			dwSynch = m_pBuf[a];
      dwMode = (m_pBuf[b] & 0x7E000000) >> 25;
  		if (dwSynch != 0xFFFFFF00) 
      { 
        dwMode = -1;
        throw("Synch lost");
      }
      if ( (dwMode & 0x17) != m_dwMode)
        throw("Mode changed");
    }

///////////////////////////////////////////////////////////////////
// !!!!!!!!!!!!!!!!!!!!!! ////////////////////
// Here follows long (more than 500 lines) and really complex 
// critical section  that remap channels, detects trigger events,
//  performs filtering and decimation, calculates mean channel offsets
// and post messages to other  threads (to display and store data)
////////////////////////////////////////////////////////////////////////////


   //update display pointer	
   m_i64Disp += nDisp * m_nActChans;

Of course, the main problem is not the code itself (it does not differ from any other acquisition program) - but the design of the project: how to divide the flow between severla threads, how to organize inter-thread synchronization and communication - etc... But I hope this small will help you with the 'core' part of the program.
WBR - DIZ

Coen
Site Admin
Posts: 1124
Joined: Fri Mar 26, 2004 7:00 pm
Location: Amsterdam, Netherlands
Contact:

Post by Coen »

Just an update on the information given on http://www.biosemi.com/faq/make_own_acq ... ftware.htm:

Most of the currently shipped ActiveTwo systems are of the Mk2 version. This recent version has the option for 24 extra channels, and consequently a higher data throughput is used. Please refer to the table below, and also check http://www.biosemi.com/faq/adjust_samplerate.htm:

Mk1:
Speedmode 0 and 4: 258
Speedmode 1 and 5: 130
Speedmode 2 and 6: 66
Speedmode 3 and 7: 34
Speedmode 8: 290 (2+256+32)

Mk2:
Speedmode 0, 1, 2 and 3: 610 (2+4*152)
Speedmode 4: 282
Speedmode 5: 154
Speedmode 6: 90
Speedmode 7: 58
Speedmode 8: 314 (2+280+32)

Like current ActiView, your software can be made to recognize whether a Mk1 or Mk 2 is connected by checking bit 23 (MSB) of the trigger/status channel (channel 2), see http://www.biosemi.com/faq/trigger_signals.htm

Best regards, Coen (BioSemi)

diz
Posts: 5
Joined: Sat Oct 16, 2004 8:54 am
Location: St.-Petersburg, Russia
Contact:

Post by diz »

Thanks. information about number of buffered channels in Mk2 system clears things a little :) There still remain a lot of questions on 'daisy chained' mode (the real sequence of pin-channels, touchproofs and sensors from different boxes, the way the system will behave in case of only one box, but in mode, for example, 2 etc) - but I do not thing they are relevant now.

Diference of Mk2 system from 'original' Active2 affects only two lines of the code I cited. First of all, the mask for extraction of 'mode' shold me changed from 0x2E000000 to 0xAE000000 (to include MSB)

Code: Select all

      dwMode = (m_pBuf[b] & 0x2E000000) >> 25; 
      m_aMode= actMode(dwMode); 
      m_nActChans = m_aMode.GetBufChans(); 
(here m_nActChans is the number of buffered channels, including trigger and synchro channels)

the same must be done in line that compares current mode (dwMode) with saved one (m_dwMode) - here mask must be changed from 0x17 to 0x57

Code: Select all

     if ( (dwMode & 0x57) != m_dwMode) 
        throw("Mode changed"); 
What is really affected by the difference between Mk1 and Mk2 systems, is the place where 'mode' is decoded into 'real' parameters of the Active2 - number of buffered channels, sampling frequency etc. I use for this purpose separete class, and perform such a 'decoding' in it's constructor:

Code: Select all

actMode::actMode(long lSpeed) : bMk2(false)
{

  if (lSpeed < 0)
  {
    nSpeed = -1;
    nBufChans = 0;
    nPinChans = 0;
    nFrq = 0;
   nTouch = 0;
   return;
  }

  bMk2 = ((lSpeed & 0x40) != 0);
  nSpeed = (short)( (lSpeed & 7)+(lSpeed>>1 & 8));
  if (bMk2) nSpeed += 16;

  switch(nSpeed) {
    case 0:  // Mk1, mode 0
       nBufChans = 258;   //number of buffered channels
       nPinChans = 256;   //numbef of 'Pin' chanels
       nFrq = 2048;          //sampling frequency
       nTouch = 0;           //number of touchproofs
       break;
    case 1: //Mk1, mode 1
       nBufChans = 130;
       nPinChans = 128;
       nFrq = 4096;
       nTouch = 0;
       break;
/////////////////////////////////////////////////////////////
// here I'll skip some cases
///////////////////////////////////////////////////////////
    case 4:   //Mk1, mode 4
       nBufChans = 258;
       nPinChans = 232;
       nFrq = 2048;
       nTouch = 8;
       break;
/////////////////////////////////////////////////////////////
// here I'll skip some cases
///////////////////////////////////////////////////////////
    case 4+16:   //Mk2, mode 4
       nBufChans = 282;
       nPinChans = 256;
       nFrq = 2048;
       nTouch = 8;
       break;
/////////////////////////////////////////////////////////////
// here I'll skip some cases
///////////////////////////////////////////////////////////
    default:   //unknown mode
       nBufChans = 0;
       nPinChans = 0;
       nFrq = 0;
       nTouch =0;
       break;
}
 
Now I've inserted Mk2-recognition code into my program - but I have no possibility to test it and catch the bugs :x

New version can be found at
http://diz-vara.narod.ru
- under ActiveD project link. (updated 29-Nov-2005)

It would be interesting to hear reports from Active2 Mk2 users.
WBR - DIZ

kking
Posts: 1
Joined: Wed Aug 08, 2007 7:51 pm
Location: NY

Post by kking »

I am currently developing a device that uses the BioSemi ActiveTwo unit, and was wondering if the DLL code was available. I am interested in embedding the acquisition code onto a seperate chip, and the current format limits the DLL to being run on a windows system.

Can you give me any more information on the DLL, or the source code behind it?

-Thanks
Kevin

Coen
Site Admin
Posts: 1124
Joined: Fri Mar 26, 2004 7:00 pm
Location: Amsterdam, Netherlands
Contact:

Post by Coen »

We do not see commercial advantages in making the driver code available to the public.

Best regards, Coen (BioSemi)

HvD
Posts: 2
Joined: Mon Dec 24, 2007 5:00 pm
Location: The Netherlands

Read pointer

Post by HvD »

I am writing my own C program using the DLL.
All data comes in fine except when I restart the acquisition.
I restart by enabling the hadshake again but it seems the data is no longer in sync with anymore. So data comes in fine, just the first channel is no longer at that same place as it was before.
I suggestions why this is so?

thanks
hans

//START

initbuffer[0]=0xFF;
USB_WRITE(ActHndl, initbuffer);

...

// read

READ_POINTER(ActHndl,&ReadPtr);

Post Reply