An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers plunger.h Source File

plunger.h

00001 // Plunger Sensor Interface
00002 //
00003 // This module defines the abstract interface to the plunger sensors.
00004 // We support several different physical sensor types, so we need a
00005 // common interface for use in the main code.
00006 //
00007 // In case it's helpful in developing code for new sensor types, I've
00008 // measured the maximum instantaneous speed of a plunger at .175 inches
00009 // per millisecond, or 4.46 mm/ms.  (I measured that with an AEDR-8300;
00010 // see that code for more details.)
00011 //
00012 
00013 #ifndef PLUNGER_H
00014 #define PLUNGER_H
00015 
00016 #include "config.h"
00017 
00018 // Plunger reading with timestamp
00019 struct PlungerReading
00020 {
00021     // Raw sensor reading, normalied to 0x0000..0xFFFF range
00022     int pos;
00023     
00024     // Rimestamp of reading, in microseconds, relative to an arbitrary
00025     // zero point.  Note that a 32-bit int can only represent about 71.5
00026     // minutes worth of microseconds, so this value is only meaningful
00027     // to compute a delta from other recent readings.  As long as two
00028     // readings are within 71.5 minutes of each other, the time difference
00029     // calculated from the timestamps using 32-bit math will be correct
00030     // *even if a rollover occurs* between the two readings, since the
00031     // calculation is done mod 2^32-1.
00032     uint32_t t;
00033 };
00034 
00035 class PlungerSensor
00036 {
00037 public:
00038     PlungerSensor(int nativeScale)
00039     {
00040         // use the joystick scale as our native scale by default
00041         this->nativeScale = nativeScale;
00042         
00043         // figure the scaling factor
00044         scalingFactor = (65535UL*65536UL) / nativeScale;
00045         
00046         // presume no jitter filter
00047         jfWindow = 0;
00048         
00049         // initialize the jitter filter
00050         jfLo = jfHi = jfLast = 0;
00051         
00052         // presume normal orientation
00053         reverseOrientation = false;
00054     }
00055 
00056     // ---------- Abstract sensor interface ----------
00057     
00058     // Initialize the physical sensor device.  This is called at startup
00059     // to set up the device for first use.
00060     virtual void init() { }
00061     
00062     // Auto-zero the plunger.  Relative sensor types, such as quadrature
00063     // sensors, can lose sync with the absolute position over time if they
00064     // ever miss any motion.  We can automatically correct for this by
00065     // resetting to the park position after periods of inactivity.  It's
00066     // usually safe to assume that the plunger is at the park position if it 
00067     // hasn't moved in a long time, since the spring always returns it to 
00068     // that position when it isn't being manipulated.  The main loop monitors
00069     // for motion, and calls this after a long enough time goes by without
00070     // seeing any movement.  Sensor types that are inherently absolute
00071     // (TSL1410, potentiometers) shouldn't do anything here.
00072     virtual void autoZero() { }
00073 
00074     // Is the sensor ready to take a reading?  The optical sensor requires
00075     // a fairly long time (2.5ms) to transfer the data for each reading, but 
00076     // this is done via DMA, so we can carry on other work while the transfer
00077     // takes place.  This lets us poll the sensor to see if it's still busy
00078     // working on the current reading's data transfer.
00079     virtual bool ready() { return true; }
00080     
00081     // Read the sensor position, if possible.  Returns true on success,
00082     // false if it wasn't possible to take a reading.  On success, fills
00083     // in 'r' with the current reading and timestamp and returns true.
00084     // Returns false if a reading couldn't be taken.
00085     //
00086     // r.pos is set to the normalized position reading, and r.t is set to
00087     // the timestamp of the reading.
00088     //
00089     // The normalized position is the sensor reading, corrected for jitter,
00090     // and adjusted to the abstract 0x0000..0xFFFF range.
00091     // 
00092     // The timestamp is the time the sensor reading was taken, relative to
00093     // an arbitrary zero point.  The arbitrary zero point makes this useful
00094     // only for calculating the time between readings.  Note that the 32-bit
00095     // timestamp rolls over about every 71 minutes, so it should only be
00096     // used for time differences between readings taken fairly close together.
00097     // In practice, the higher level code only uses this for a few consecutive
00098     // readings to calculate (nearly) instantaneous velocities, so the time
00099     // spans are only tens of milliseconds.
00100     //
00101     // Timing requirements:  for best results, readings should be taken
00102     // in well under 5ms.  The release motion of the physical plunger
00103     // takes from 30ms to 50ms, so we need to collect samples much faster
00104     // than that to avoid aliasing during the bounce.
00105     bool read(PlungerReading &r)
00106     {
00107         // fail if the hardware scan isn't ready
00108         if (!ready())
00109             return false;
00110         
00111         // get the raw reading
00112         if (readRaw(r))
00113         {
00114             // adjust for orientation
00115             r.pos = applyOrientation(r.pos);
00116 
00117             // process it through the jitter filter
00118             r.pos = postJitterFilter(r.pos);
00119             
00120             // adjust to the abstract scale via the scaling factor
00121             r.pos = uint16_t(uint32_t((scalingFactor * r.pos) + 32768) >> 16);
00122             
00123             // success
00124             return true;
00125         }
00126         else
00127         {
00128             // no reading is available
00129             return false;
00130         }
00131     }
00132 
00133     // Get a raw plunger reading.  This gets the raw sensor reading with
00134     // timestamp, without jitter filtering and without any scale adjustment.
00135     virtual bool readRaw(PlungerReading &r) = 0;
00136     
00137     // Restore the saved calibration data from the configuration.  The main 
00138     // loop calls this at startup to let us initialize internals from the
00139     // saved calibration data.  This is called even if the plunger isn't 
00140     // calibrated, which is flagged in the config.
00141     virtual void restoreCalibration(Config &) { }
00142     
00143     // Begin calibration.  The main loop calls this when the user activates
00144     // calibration mode.  Sensors that work in terms of relative positions,
00145     // such as quadrature-based sensors, can use this to set the reference
00146     // point for the park position internally.
00147     virtual void beginCalibration(Config &) { }
00148     
00149     // End calibration.  The main loop calls this when calibration mode is
00150     // completed.
00151     virtual void endCalibration(Config &) { }
00152     
00153     // Send a sensor status report to the host, via the joystick interface.
00154     // This provides some common information for all sensor types, and also
00155     // includes a full image snapshot of the current sensor pixels for
00156     // imaging sensor types.
00157     //
00158     // The default implementation here sends the common information
00159     // packet, with the pixel size set to 0.
00160     //
00161     // 'flags' is a combination of bit flags:
00162     //   0x01  -> low-res scan (default is high res scan)
00163     //
00164     // Low-res scan mode means that the sensor should send a scaled-down
00165     // image, at a reduced size determined by the sensor subtype.  The
00166     // default if this flag isn't set is to send the full image, at the
00167     // sensor's native pixel size.  The low-res version is a reduced size
00168     // image in the normal sense of scaling down a photo image, keeping the
00169     // image intact but at reduced resolution.  Note that low-res mode
00170     // doesn't affect the ongoing sensor operation at all.  It only applies
00171     // to this single pixel report.  The purpose is simply to reduce the USB 
00172     // transmission time for the image, to allow for a faster frame rate for 
00173     // displaying the sensor image in real time on the PC.  For a high-res
00174     // sensor like the TSL1410R, sending the full pixel array by USB takes 
00175     // so long that the frame rate is way below regular video rates.
00176     //
00177     virtual void sendStatusReport(class USBJoystick &js, uint8_t flags)
00178     {
00179         // read the current position
00180         int pos = 0xFFFF;
00181         PlungerReading r;
00182         if (readRaw(r))
00183         {
00184             // adjust for reverse orientation
00185             r.pos = applyOrientation(r.pos);
00186 
00187             // success - apply the jitter filter
00188             pos = postJitterFilter(r.pos);
00189         }
00190         
00191         // Send the common status information, indicating 0 pixels, standard
00192         // sensor orientation, and zero processing time.  Non-imaging sensors 
00193         // usually don't have any way to detect the orientation, so assume
00194         // normal orientation (flag 0x01).  Also assume zero analysis time,
00195         // as most non-image sensors don't have to do anything CPU-intensive
00196         // with the raw readings (all they usually have to do is scale the
00197         // value to the abstract reporting range).
00198         js.sendPlungerStatus(0, pos, 0x01, getAvgScanTime(), 0);
00199         js.sendPlungerStatus2(nativeScale, jfLo, jfHi, r.pos, 0);
00200     }
00201     
00202     // Set extra image integration time, in microseconds.  This is only 
00203     // meaningful for image-type sensors.  This allows the PC client to
00204     // manually adjust the exposure time for testing and debugging
00205     // purposes.
00206     virtual void setExtraIntegrationTime(uint32_t us) { }
00207     
00208     // Get the average sensor scan time in microseconds
00209     virtual uint32_t getAvgScanTime() = 0;
00210     
00211     // Apply the orientation filter.  The position is in unscaled
00212     // native sensor units.
00213     int applyOrientation(int pos)
00214     {
00215         return (reverseOrientation ? nativeScale - pos : pos);
00216     }
00217     
00218     // Post-filter a raw reading through the mitter filter.  Most plunger
00219     // sensor subclasses can use this default implementation, since the
00220     // jitter filter is usually applied to the raw position reading.
00221     // However, for some sensor types, it might be better to apply the
00222     // jitter filtering to the underlying physical sensor reading, before
00223     // readRaw() translates the reading into distance units.  In that
00224     // case, the subclass can override this to simply return the argument
00225     // unchanged.  This allows subclasses to use jitterFilter() if desired
00226     // on their physical sensor readings.  It's not either/or, though; a
00227     // subclass that overrides jitter post-filtering isn't could use an
00228     // entirely different noise filtering algorithm on its sensor data.
00229     virtual int postJitterFilter(int pos) { return jitterFilter(pos); }
00230         
00231     // Apply the jitter filter.  The position is in unscaled native 
00232     // sensor units.
00233     int jitterFilter(int pos)
00234     {
00235         // Check to see where the new reading is relative to the
00236         // current window
00237         if (pos < jfLo)
00238         {
00239             // the new position is below the current window, so move
00240             // the window down such that the new point is at the bottom 
00241             // of the window
00242             jfLo = pos;
00243             jfHi = pos + jfWindow;
00244             
00245             // figure the new position as the centerpoint of the new window
00246             jfLast = pos = (jfHi + jfLo)/2;
00247             return pos;
00248         }
00249         else if (pos > jfHi)
00250         {
00251             // the new position is above the current window, so move
00252             // the window up such that the new point is at the top of
00253             // the window
00254             jfHi = pos;
00255             jfLo = pos - jfWindow;
00256 
00257             // figure the new position as the centerpoint of the new window
00258             jfLast = pos = (jfHi + jfLo)/2;
00259             return pos;
00260         }
00261         else
00262         {
00263             // the new position is inside the current window, so repeat
00264             // the last reading
00265             return jfLast;
00266         }
00267     }
00268     
00269     // Process a configuration variable change.  'varno' is the
00270     // USB protocol variable number being updated; 'cfg' is the
00271     // updated configuration.
00272     virtual void onConfigChange(int varno, Config &cfg)
00273     {
00274         switch (varno)
00275         {
00276         case 19:
00277             // Plunger filters - jitter window and reverse orientation.
00278             setJitterWindow(cfg.plunger.jitterWindow);
00279             setReverseOrientation((cfg.plunger.reverseOrientation & 0x01) != 0);
00280             break;
00281         }
00282     }
00283     
00284     // Set the jitter filter window size.  This is specified in native
00285     // sensor units.
00286     void setJitterWindow(int w)
00287     {
00288         // set the new window size
00289         jfWindow = w;
00290         
00291         // reset the running window
00292         jfHi = jfLo = jfLast;
00293     }
00294     
00295     // Set reverse orientation
00296     void setReverseOrientation(bool f) { reverseOrientation = f; }
00297         
00298 protected:
00299     // Native scale of the device.  This is the scale used for the position
00300     // reading in status reports.  This lets us report the position in the
00301     // same units the sensor itself uses, to avoid any rounding error 
00302     // converting to an abstract scale.
00303     //
00304     // The nativeScale value is the number of units in the range of raw
00305     // sensor readings returned from readRaw().  Raw readings thus have a
00306     // valid range of 0 to nativeScale-1.
00307     //
00308     // Image edge detection sensors use the pixel size of the image, since
00309     // the position is determined by the pixel position of the shadow in
00310     // the image.  Quadrature sensors and other sensors that report the
00311     // distance in terms of physical distance units should use the number
00312     // of quanta in the approximate total plunger travel distance of 3".
00313     // For example, the VL6180X uses millimeter quanta, so can report
00314     // about 77 quanta over 3"; a quadrature sensor that reports at 1/300"
00315     // intervals has about 900 quanta over 3".  Absolute encoders (e.g., 
00316     // bar code sensors) should use the bar code range.
00317     //
00318     // Sensors that are inherently analog (e.g., potentiometers, analog
00319     // distance sensors) can quantize on any arbitrary scale.  In most cases,
00320     // it's best to use the same 0..65535 scale used for the regular plunger
00321     // reports.
00322     uint16_t nativeScale;
00323     
00324     // Scaling factor to convert native readings to abstract units on the
00325     // 0x0000..0xFFFF scale used in the higher level sensor-independent
00326     // code.  Multiply a raw sensor position reading by this value to
00327     // get the equivalent value on the abstract scale.  This is expressed 
00328     // as a fixed-point real number with a scale of 65536: calculate it as
00329     //
00330     //   (65535U*65536U) / (nativeScale - 1);
00331     uint32_t scalingFactor;
00332     
00333     // Jitter filtering
00334     int jfWindow;                // window size, in native sensor units
00335     int jfLo, jfHi;              // bounds of current window
00336     int jfLast;                  // last filtered reading
00337     
00338     // Reverse the raw reading orientation.  If set, raw readings will be
00339     // switched to the opposite orientation.  This allows flipping the sensor
00340     // orientation virtually to correct for installing the physical device
00341     // backwards.
00342     bool reverseOrientation;
00343 };
00344 
00345 
00346 // --------------------------------------------------------------------------
00347 //
00348 // Generic image sensor interface for image-based plungers.
00349 //
00350 // This interface is designed to allow the underlying sensor code to work
00351 // asynchronously to transfer pixels from the sensor into memory using
00352 // multiple buffers arranged in a circular list.  We have a "ready" state,
00353 // which lets the sensor tell us when a buffer is available, and we have
00354 // the notion of "ownership" of the buffer.  When the client is done with
00355 // a frame, it must realease the frame back to the sensor so that the sensor
00356 // can use it for a subsequent frame transfer.
00357 //
00358 class PlungerSensorImageInterface
00359 {
00360 public:
00361     PlungerSensorImageInterface(int npix)
00362     {
00363         native_npix = npix;
00364     }
00365     
00366     // initialize the sensor
00367     virtual void init() = 0;
00368 
00369     // is the sensor ready?
00370     virtual bool ready() = 0;
00371     
00372     // Read the image.  This retrieves a pointer to the current frame
00373     // buffer, which is in memory space managed by the sensor.  This
00374     // MUST only be called when ready() returns true.  The buffer is
00375     // locked for the client's use until the client calls releasePix().
00376     // The client MUST call releasePix() when done with the buffer, so
00377     // that the sensor can reuse it for another frame.
00378     virtual void readPix(uint8_t* &pix, uint32_t &t) = 0;
00379     
00380     // Release the current frame buffer back to the sensor.  
00381     virtual void releasePix() = 0;
00382     
00383     // get the average sensor pixel scan time (the time it takes on average
00384     // to read one image frame from the sensor)
00385     virtual uint32_t getAvgScanTime() = 0;
00386     
00387     // Set the minimum integration time (microseconds)
00388     virtual void setMinIntTime(uint32_t us) = 0;
00389     
00390 protected:
00391     // number of pixels on sensor
00392     int native_npix;
00393 };
00394 
00395 
00396 // ----------------------------------------------------------------------------
00397 //
00398 // Plunger base class for image-based sensors
00399 //
00400 template<typename ProcessResult>
00401 class PlungerSensorImage: public PlungerSensor
00402 {
00403 public:
00404     PlungerSensorImage(PlungerSensorImageInterface &sensor, 
00405         int npix, int nativeScale, bool negativeImage = false) :
00406         PlungerSensor(nativeScale), 
00407         sensor(sensor),
00408         native_npix(npix),
00409         negativeImage(negativeImage),
00410         axcTime(0),
00411         extraIntTime(0)
00412     {
00413     }
00414     
00415     // initialize the sensor
00416     virtual void init() { sensor.init(); }
00417 
00418     // is the sensor ready?
00419     virtual bool ready() { return sensor.ready(); }
00420     
00421     // get the pixel transfer time
00422     virtual uint32_t getAvgScanTime() { return sensor.getAvgScanTime(); }
00423 
00424     // set extra integration time
00425     virtual void setExtraIntegrationTime(uint32_t us) { extraIntTime = us; }
00426     
00427     // read the plunger position
00428     virtual bool readRaw(PlungerReading &r)
00429     {
00430         // read pixels from the sensor
00431         uint8_t *pix;
00432         uint32_t tpix;
00433         sensor.readPix(pix, tpix);
00434         
00435         // process the pixels
00436         int pixpos;
00437         ProcessResult res;
00438         bool ok = process(pix, native_npix, pixpos, res);
00439         
00440         // release the buffer back to the sensor
00441         sensor.releasePix();
00442         
00443         // adjust the exposure time
00444         sensor.setMinIntTime(axcTime + extraIntTime);
00445 
00446         // if we successfully processed the frame, read the position
00447         if (ok)
00448         {            
00449             r.pos = pixpos;
00450             r.t = tpix;
00451         }
00452         
00453         // return the result
00454         return ok;
00455     }
00456 
00457     // Send a status report to the joystick interface.
00458     // See plunger.h for details on the arguments.
00459     virtual void sendStatusReport(USBJoystick &js, uint8_t flags)
00460     {
00461         // start a timer to measure the processing time
00462         Timer pt;
00463         pt.start();
00464 
00465         // get pixels
00466         uint8_t *pix;
00467         uint32_t t;
00468         sensor.readPix(pix, t);
00469 
00470         // process the pixels and read the position
00471         int pos, rawPos;
00472         int n = native_npix;
00473         ProcessResult res;
00474         if (process(pix, n, rawPos, res))
00475         {
00476             // success - apply the post jitter filter
00477             pos = postJitterFilter(rawPos);
00478         }
00479         else
00480         {
00481             // report 0xFFFF to indicate that the position wasn't read
00482             pos = 0xFFFF;
00483             rawPos = 0xFFFF;
00484         }
00485         
00486         // adjust the exposure time
00487         sensor.setMinIntTime(axcTime + extraIntTime);
00488 
00489         // note the processing time
00490         uint32_t processTime = pt.read_us();
00491         
00492         // If a low-res scan is desired, reduce to a subset of pixels.  Ignore
00493         // this for smaller sensors (below 512 pixels)
00494         if ((flags & 0x01) && n >= 512)
00495         {
00496             // figure how many sensor pixels we combine into each low-res pixel
00497             const int group = 8;
00498             int lowResPix = n / group;
00499             
00500             // combine the pixels
00501             int src, dst;
00502             for (src = dst = 0 ; dst < lowResPix ; ++dst)
00503             {
00504                 // average this block of pixels
00505                 int a = 0;
00506                 for (int j = 0 ; j < group ; ++j)
00507                     a += pix[src++];
00508                         
00509                 // we have the sum, so get the average
00510                 a /= group;
00511 
00512                 // store the down-res'd pixel in the array
00513                 pix[dst] = uint8_t(a);
00514             }
00515             
00516             // update the pixel count to the reduced array size
00517             n = lowResPix;
00518         }
00519         
00520         // figure the report flags
00521         int jsflags = 0;
00522         
00523         // add flags for the detected orientation: 0x01 for normal orientation,
00524         // 0x02 for reversed orientation; no flags if orientation is unknown
00525         int dir = getOrientation();
00526         if (dir == 1) 
00527             jsflags |= 0x01; 
00528         else if (dir == -1)
00529             jsflags |= 0x02;
00530             
00531         // send the sensor status report headers
00532         js.sendPlungerStatus(n, pos, jsflags, sensor.getAvgScanTime(), processTime);
00533         js.sendPlungerStatus2(nativeScale, jfLo, jfHi, rawPos, axcTime);
00534         
00535         // send any extra status headers for subclasses
00536         extraStatusHeaders(js, res);
00537         
00538         // If we're not in calibration mode, send the pixels
00539         extern bool plungerCalMode;
00540         if (!plungerCalMode)
00541         {
00542             // If the sensor uses a negative image format (brighter pixels are
00543             // represented by lower numbers in the pixel array), invert the scale
00544             // back to a normal photo-positive scale, so that the client doesn't
00545             // have to know these details.
00546             if (negativeImage)
00547             {
00548                 // Invert the photo-negative 255..0 scale to a normal,
00549                 // photo-positive 0..255 scale.  This is just a matter of
00550                 // calculating pos_pixel = 255 - neg_pixel for each pixel.
00551                 //
00552                 // There's a shortcut we can use here to make this loop go a
00553                 // lot faster than the naive approach.  Note that 255 decimal
00554                 // is 1111111 binary.  Subtracting any number in (0..255) from
00555                 // 255 is the same as inverting the bits in the other number.
00556                 // That is, 255 - X == ~X for all X in 0..255.  That's useful
00557                 // because it means that we can compute (255-X) as a purely
00558                 // bitwise operation, which means that we can perform it on
00559                 // blocks of bytes instead of individual bytes.  On ARM, we
00560                 // can perform bitwise operations four bytes at a time via
00561                 // DWORD instructions.  This lets us compute (255-X) for N
00562                 // bytes using N/4 loop iterations.
00563                 //
00564                 // One other small optimization we can apply is to notice that
00565                 // ~X == X ^ ~0, and that X ^= ~0 can be performed with a
00566                 // single ARM instruction.  So we can make the ARM C++ compiler
00567                 // translate the loop body into just three instructions:  XOR 
00568                 // with immediate data and auto-increment pointer, decrement 
00569                 // the counter, and jump if not zero.  That's as fast we could
00570                 // do it in hand-written assembly.  I clocked this loop at 
00571                 // 60us for the 1536-pixel TCD1103 array.
00572                 //
00573                 // Note two important constraints:
00574                 //
00575                 //  - 'pix' must be aligned on a DWORD (4-byte) boundary.
00576                 //    This is REQUIRED, because the XOR in the loop uses a
00577                 //    DWORD memory operand, which will halt the MCU with a
00578                 //    bus error if the pointer isn't DWORD-aligned.
00579                 //
00580                 //  - 'n' must be a multiple of 4 bytes.  This isn't strictly
00581                 //    required, but if it's not observed, the last (N - N/4)
00582                 //    bytes won't be inverted.
00583                 //
00584                 // The only sensor that uses a negative image is the TCD1103.
00585                 // Its buffer is DWORD-aligned because it's allocated via
00586                 // malloc(), which always does worst-case alignment.  Its
00587                 // buffer is 1546 bytes long, which violates the multiple-of-4
00588                 // rule, but inconsequentially, as the last 14 bytes represent
00589                 // dummy pixels that can be ignored (so it's okay that we'll 
00590                 // miss inverting the last two bytes).
00591                 //
00592                 uint32_t *pix32 = reinterpret_cast<uint32_t*>(pix);
00593                 for (int i = n/4; i != 0; --i)
00594                     *pix32++ ^= 0xFFFFFFFF;
00595             }            
00596 
00597             // send the pixels in report-sized chunks until we get them all
00598             int idx = 0;
00599             while (idx < n)
00600                 js.sendPlungerPix(idx, n, pix);
00601         }
00602         
00603         // release the pixel buffer
00604         sensor.releasePix();
00605     }
00606     
00607 protected:
00608     // process an image to read the plunger position
00609     virtual bool process(const uint8_t *pix, int npix, int &rawPos, ProcessResult &res) = 0;
00610     
00611     // send extra status headers, following the standard headers (types 0 and 1)
00612     virtual void extraStatusHeaders(USBJoystick &js, ProcessResult &res) { }
00613     
00614     // get the detected orientation
00615     virtual int getOrientation() const { return 0; }
00616     
00617     // underlying hardware sensor interface
00618     PlungerSensorImageInterface &sensor;
00619     
00620     // number of pixels
00621     int native_npix;
00622     
00623     // Does the sensor report a "negative" image?  This is like a photo
00624     // negative, where brighter pixels are represented by lower numbers in
00625     // the pixel array.
00626     bool negativeImage;
00627     
00628     // Auto-exposure time.  This is for use by process() in the subclass.
00629     // On each frame processing iteration, it can adjust this to optimize
00630     // the image quality.
00631     uint32_t axcTime;
00632     
00633     // Extra exposure time.  This is for use by the PC side, mostly for
00634     // debugging use to allow the PC user to manually adjust the exposure
00635     // when inspecting captured frames.
00636     uint32_t extraIntTime;
00637 };
00638 
00639 
00640 #endif /* PLUNGER_H */