1 /*
   2  * $RCSfile: sample2.c,v $
   3  * $Revision: 1.3 $
   4  * $Date: 2009/12/22 22:23:35 $
   5  * jEdit:tabSize=4:indentSize=4:collapseFolds=1:
   6  *
   7  * AIOUSB library sample program
   8  */
   9 
  10 
  11 // {{{ notes and build instructions
  12 /*
  13  * This source code looks best with a tab width of 4.
  14  *
  15  * All the API functions that DO NOT begin "AIOUSB_" are standard API functions, largely
  16  * documented in http://accesio.com/MANUALS/USB%20Software%20Reference.pdf. The functions
  17  * that DO begin with "AIOUSB_" are "extended" API functions added to the Linux
  18  * implementation. Source code lines in this sample program that are prefixed with the
  19  * comment "/ * API * /" highlight calls to the AIOUSB API.
  20  *
  21  * LIBUSB (http://www.libusb.org/) must be installed on the Linux box (the AIOUSB code
  22  * was developed using libusb version 1.0.3). After installing libusb, it may also be
  23  * necessary to set an environment variable so that the libusb and aiousb header files can
  24  * be located:
  25  *
  26  *     export CPATH=/usr/local/include/libusb-1.0/:/usr/local/include/aiousb/
  27  *
  28  * Once libusb is installed properly, it should be possible to compile the sample program
  29  * using the simple command:
  30  *
  31  *     make
  32  *
  33  * Alternatively, one can "manually" compile the sample program using the command:
  34  *
  35  *     gcc -std=gnu99 -D_GNU_SOURCE -Wall -pthread -fPIC sample2.c -laiousb -lusb-1.0 -lm -o sample2
  36  *
  37  * or, to enable debug features
  38  *
  39  *     gcc -ggdb -std=gnu99 -D_GNU_SOURCE -Wall -pthread -fPIC sample2.c -laiousbdbg -lusb-1.0 -lm -o sample2
  40  */
  41 // }}}
  42 
  43 // {{{ includes
  44 #include <aiousb.h>
  45 #include <math.h>
  46 #include <stdio.h>
  47 #include <unistd.h>
  48 // }}}
  49 
  50 int main( int argc, char **argv ) {
  51     printf(
  52         "USB-AI16-16A sample program version 1.3, 22 December 2009\n"
  53         "  AIOUSB library version %s, %s\n"
  54         "  This program demonstrates controlling a USB-AI16-16A device on\n"
  55         "  the USB bus. For simplicity, it uses the first such device found\n"
  56         "  on the bus.\n"
  57 /*API*/ , AIOUSB_GetVersion(), AIOUSB_GetVersionDate()
  58     );
  59 
  60     /*
  61      * MUST call AIOUSB_Init() before any meaningful AIOUSB functions;
  62      * AIOUSB_GetVersion() above is an exception
  63      */
  64 /*API*/ unsigned long result = AIOUSB_Init();
  65     if( result == AIOUSB_SUCCESS ) {
  66         /*
  67          * call GetDevices() to obtain "list" of devices found on the bus
  68          */
  69 /*API*/ unsigned long deviceMask = GetDevices();
  70         if( deviceMask != 0 ) {
  71             /*
  72              * at least one ACCES device detected, but we want one of a specific type
  73              */
  74 /*API*/     AIOUSB_ListDevices();               // print list of all devices found on the bus
  75             const int MAX_NAME_SIZE = 20;
  76             char name[ MAX_NAME_SIZE + 2 ];
  77             unsigned long productID, nameSize, numDIOBytes, numCounters;
  78             unsigned long deviceIndex = 0;
  79             AIOUSB_BOOL deviceFound = AIOUSB_FALSE;
  80             while( deviceMask != 0 ) {
  81                 if( ( deviceMask & 1 ) != 0 ) {
  82                     // found a device, but is it the correct type?
  83                     nameSize = MAX_NAME_SIZE;
  84 /*API*/             result = QueryDeviceInfo( deviceIndex, &productID, &nameSize, name, &numDIOBytes, &numCounters );
  85                     if( result == AIOUSB_SUCCESS ) {
  86                         if(
  87                             productID >= USB_AI16_16A
  88                             && productID <= USB_AI12_128E
  89                         ) {
  90                             // found a USB-AI16-16A family device
  91                             deviceFound = AIOUSB_TRUE;
  92                             break;              // from while()
  93                         }   // if( productID ...
  94                     } else
  95                         printf( "Error '%s' querying device at index %lu\n"
  96 /*API*/                     , AIOUSB_GetResultCodeAsString( result ), deviceIndex );
  97                 }   // if( ( deviceMask ...
  98                 deviceIndex++;
  99                 deviceMask >>= 1;
 100             }   // while( deviceMask ...
 101             if( deviceFound ) {
 102 /*API*/         AIOUSB_Reset( deviceIndex );
 103 /*API*/         AIOUSB_SetCommTimeout( deviceIndex, 1000 );
 104 /*API*/         AIOUSB_SetDiscardFirstSample( deviceIndex, AIOUSB_TRUE );
 105 
 106                 __uint64_t serialNumber;
 107 /*API*/         result = GetDeviceSerialNumber( deviceIndex, &serialNumber );
 108                 if( result == AIOUSB_SUCCESS )
 109                     printf( "Serial number of device at index %lu: %llx\n", deviceIndex, ( long long ) serialNumber );
 110                 else
 111                     printf( "Error '%s' getting serial number of device at index %lu\n"
 112 /*API*/                 , AIOUSB_GetResultCodeAsString( result ), deviceIndex );
 113 
 114                 /*
 115                  * demonstrate A/D configuration; there are two ways to configure the A/D;
 116                  * one way is to create an ADConfigBlock instance and configure it, and then
 117                  * send the whole thing to the device using ADC_SetConfig(); the other way
 118                  * is to use the discrete API functions such as ADC_SetScanLimits(), which
 119                  * send the new settings to the device immediately; here we demonstrate the
 120                  * ADConfigBlock technique; below we demonstrate use of the discrete functions
 121                  */
 122                 ADConfigBlock configBlock;
 123 /*API*/         AIOUSB_InitConfigBlock( &configBlock, deviceIndex, AIOUSB_FALSE );
 124 /*API*/         AIOUSB_SetAllGainCodeAndDiffMode( &configBlock, AD_GAIN_CODE_10V, AIOUSB_FALSE );
 125 /*API*/         AIOUSB_SetCalMode( &configBlock, AD_CAL_MODE_NORMAL );
 126 /*API*/         AIOUSB_SetTriggerMode( &configBlock, 0 );
 127 /*API*/         AIOUSB_SetScanRange( &configBlock, 2, 13 );
 128 /*API*/         AIOUSB_SetOversample( &configBlock, 0 );
 129 /*API*/         result = ADC_SetConfig( deviceIndex, configBlock.registers, &configBlock.size );
 130                 if( result == AIOUSB_SUCCESS ) {
 131                     const int CAL_CHANNEL = 5;
 132                     const int MAX_CHANNELS = 128;
 133                     const int NUM_CHANNELS = 16;
 134                     unsigned short counts[ MAX_CHANNELS ];
 135                     double volts[ MAX_CHANNELS ];
 136                     unsigned char gainCodes[ NUM_CHANNELS ];
 137                     printf( "A/D settings successfully configured\n" );
 138 
 139                     /*
 140                      * demonstrate automatic A/D calibration
 141                      */
 142 /*API*/             result = ADC_SetCal( deviceIndex, ":AUTO:" );
 143                     if( result == AIOUSB_SUCCESS )
 144                         printf( "Automatic calibration completed successfully\n" );
 145                     else
 146                         printf( "Error '%s' performing automatic A/D calibration\n"
 147 /*API*/                     , AIOUSB_GetResultCodeAsString( result ) );
 148 
 149                     /*
 150                      * verify that A/D ground calibration is correct
 151                      */
 152 /*API*/             ADC_SetOversample( deviceIndex, 0 );
 153 /*API*/             ADC_SetScanLimits( deviceIndex, CAL_CHANNEL, CAL_CHANNEL );
 154 /*API*/             ADC_ADMode( deviceIndex, 0 /* TriggerMode */, AD_CAL_MODE_GROUND );
 155 /*API*/             result = ADC_GetScan( deviceIndex, counts );
 156                     if( result == AIOUSB_SUCCESS )
 157                         printf( "Ground counts = %u (should be approx. 0)\n", counts[ CAL_CHANNEL ] );
 158                     else
 159                         printf( "Error '%s' attempting to read ground counts\n"
 160 /*API*/                     , AIOUSB_GetResultCodeAsString( result ) );
 161 
 162                     /*
 163                      * verify that A/D reference calibration is correct
 164                      */
 165 /*API*/             ADC_ADMode( deviceIndex, 0 /* TriggerMode */, AD_CAL_MODE_REFERENCE );
 166 /*API*/             result = ADC_GetScan( deviceIndex, counts );
 167                     if( result == AIOUSB_SUCCESS )
 168                         printf( "Reference counts = %u (should be approx. 65130)\n", counts[ CAL_CHANNEL ] );
 169                     else
 170                         printf( "Error '%s' attempting to read reference counts\n"
 171 /*API*/                     , AIOUSB_GetResultCodeAsString( result ) );
 172 
 173                     /*
 174                      * demonstrate scanning channels and measuring voltages
 175                      */
 176                     for( int channel = 0; channel < NUM_CHANNELS; channel++ )
 177                         gainCodes[ channel ] = AD_GAIN_CODE_0_10V;
 178 /*API*/             ADC_RangeAll( deviceIndex, gainCodes, AIOUSB_TRUE );
 179 /*API*/             ADC_SetOversample( deviceIndex, 10 );
 180 /*API*/             ADC_SetScanLimits( deviceIndex, 0, NUM_CHANNELS - 1 );
 181 /*API*/             ADC_ADMode( deviceIndex, 0 /* TriggerMode */, AD_CAL_MODE_NORMAL );
 182 /*API*/             result = ADC_GetScanV( deviceIndex, volts );
 183                     if( result == AIOUSB_SUCCESS ) {
 184                         printf( "Volts read:\n" );
 185                         for( int channel = 0; channel < NUM_CHANNELS; channel++ )
 186                             printf( "  Channel %2d = %f\n", channel, volts[ channel ] );
 187                     } else
 188                         printf( "Error '%s' performing A/D channel scan\n"
 189 /*API*/                     , AIOUSB_GetResultCodeAsString( result ) );
 190 
 191                     /*
 192                      * demonstrate reading a single channel in volts
 193                      */
 194 /*API*/             result = ADC_GetChannelV( deviceIndex, CAL_CHANNEL, &volts[ CAL_CHANNEL ] );
 195                     if( result == AIOUSB_SUCCESS )
 196                         printf( "Volts read from A/D channel %d = %f\n", CAL_CHANNEL, volts[ CAL_CHANNEL ] );
 197                     else
 198                         printf( "Error '%s' reading A/D channel %d\n"
 199 /*API*/                     , AIOUSB_GetResultCodeAsString( result )
 200                             , CAL_CHANNEL );
 201 
 202                     /*
 203                      * demonstrate bulk acquire
 204                      */
 205 /*API*/             AIOUSB_Reset( deviceIndex );
 206 /*API*/             ADC_SetOversample( deviceIndex, 10 );
 207 /*API*/             ADC_SetScanLimits( deviceIndex, 0, NUM_CHANNELS - 1 );
 208 /*API*/             AIOUSB_SetStreamingBlockSize( deviceIndex, 100000 );
 209                     const int BULK_BYTES = 100000 /* scans */
 210                         * NUM_CHANNELS
 211                         * sizeof( unsigned short ) /* bytes / sample */
 212                         * 11 /* 1 sample + 10 oversamples */;
 213                     const double CLOCK_SPEED = 100000;  // Hz
 214                     unsigned short *const dataBuf = ( unsigned short * ) malloc( BULK_BYTES );
 215                     if( dataBuf != 0 ) {
 216                         /*
 217                          * make sure counter is stopped
 218                          */
 219                         double clockHz = 0;
 220 /*API*/                 CTR_StartOutputFreq( deviceIndex, 0, &clockHz );
 221 
 222                         /*
 223                          * configure A/D for timer-triggered acquisition
 224                          */
 225 /*API*/                 ADC_ADMode( deviceIndex, AD_TRIGGER_SCAN | AD_TRIGGER_TIMER, AD_CAL_MODE_NORMAL );
 226 
 227                         /*
 228                          * start bulk acquire; ADC_BulkAcquire() will take care of starting
 229                          * and stopping the counter; but we do have to tell it what clock
 230                          * speed to use, which is why we call AIOUSB_SetMiscClock()
 231                          */
 232 /*API*/                 AIOUSB_SetMiscClock( deviceIndex, CLOCK_SPEED );
 233 /*API*/                 result = ADC_BulkAcquire( deviceIndex, BULK_BYTES, dataBuf );
 234                         if( result == AIOUSB_SUCCESS )
 235                             printf( "Started bulk acquire of %d bytes\n", BULK_BYTES );
 236                         else
 237                             printf( "Error '%s' attempting to start bulk acquire of %d bytes\n"
 238 /*API*/                         , AIOUSB_GetResultCodeAsString( result )
 239                                 , BULK_BYTES );
 240 
 241                         /*
 242                          * use bulk poll to monitor progress
 243                          */
 244                         if( result == AIOUSB_SUCCESS ) {
 245                             unsigned long bytesRemaining = BULK_BYTES;
 246                             for( int seconds = 0; seconds < 100; seconds++ ) {
 247                                 sleep( 1 );
 248 /*API*/                         result = ADC_BulkPoll( deviceIndex, &bytesRemaining );
 249                                 if( result == AIOUSB_SUCCESS ) {
 250                                     printf( "  %lu bytes remaining\n", bytesRemaining );
 251                                     if( bytesRemaining == 0 )
 252                                         break;  // from for()
 253                                 } else {
 254                                     printf( "Error '%s' polling bulk acquire progress\n"
 255 /*API*/                                 , AIOUSB_GetResultCodeAsString( result ) );
 256                                     break;      // from for()
 257                                 }   // if( result ...
 258                             }   // for( int seconds ...
 259 
 260                             /*
 261                              * turn off timer-triggered mode
 262                              */
 263 /*API*/                     ADC_ADMode( deviceIndex, 0, AD_CAL_MODE_NORMAL );
 264 
 265                             /*
 266                              * if all the data was apparently received, scan it for zeros; it's
 267                              * unlikely that any of the data would be zero, so any zeros, particularly
 268                              * a large block of zeros would suggest that the data is not valid
 269                              */
 270                             if(
 271                                 result == AIOUSB_SUCCESS
 272                                 && bytesRemaining == 0
 273                             ) {
 274                                 AIOUSB_BOOL anyZeroData = AIOUSB_FALSE;
 275                                 int zeroIndex = -1;
 276                                 for( int index = 0; index < BULK_BYTES / ( int ) sizeof( unsigned short ); index++ ) {
 277                                     if( dataBuf[ index ] == 0 ) {
 278                                         anyZeroData = AIOUSB_TRUE;
 279                                         if( zeroIndex < 0 )
 280                                             zeroIndex = index;
 281                                     } else {
 282                                         if( zeroIndex >= 0 ) {
 283                                             printf( "  Zero data from index %d to %d\n", zeroIndex, index - 1 );
 284                                             zeroIndex = -1;
 285                                         }   // if( zeroIndex ...
 286                                     }   // if( dataBuf[ ...
 287                                 }   // for( int index ...
 288                                 if( anyZeroData == AIOUSB_FALSE )
 289                                     printf( "Successfully bulk acquired %d bytes\n", BULK_BYTES );
 290                             } else
 291                                 printf( "Failed to bulk acquire %d bytes\n", BULK_BYTES );
 292                         }   // if( result ...
 293 
 294                         free( dataBuf );
 295                     }   // if( dataBuf ...
 296                 } else
 297                     printf( "Error '%s' setting A/D configuration\n"
 298 /*API*/                 , AIOUSB_GetResultCodeAsString( result ) );
 299             } else
 300                 printf( "Failed to find USB-AI16-16A device\n" );
 301         } else
 302             printf( "No ACCES devices found on USB bus\n" );
 303 
 304         /*
 305          * MUST call AIOUSB_Exit() before program exits,
 306          * but only if AIOUSB_Init() succeeded
 307          */
 308 /*API*/ AIOUSB_Exit();
 309     }   // if( result ...
 310     return ( int ) result;
 311 }   // main()
 312 
 313 
 314 /* end of file */