An input/output controller for virtual pinball machines, with plunger position tracking, accelerometer-based nudge sensing, button input encoding, and feedback device control.

Dependencies:   USBDevice mbed FastAnalogIn FastIO FastPWM SimpleDMA

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* Copyright 2014 M J Roberts, MIT License
00002 *
00003 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004 * and associated documentation files (the "Software"), to deal in the Software without
00005 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
00006 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
00007 * Software is furnished to do so, subject to the following conditions:
00008 *
00009 * The above copyright notice and this permission notice shall be included in all copies or
00010 * substantial portions of the Software.
00011 *
00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017 */
00018 
00019 //
00020 // Pinscape Controller
00021 //
00022 // "Pinscape" is the name of my custom-built virtual pinball cabinet, so I call this
00023 // software the Pinscape Controller.  I wrote it to handle several tasks that I needed
00024 // for my cabinet.  It runs on a Freescale KL25Z microcontroller, which is a small and 
00025 // inexpensive device that attaches to the cabinet PC via a USB cable, and can attach
00026 // via custom wiring to sensors, buttons, and other devices in the cabinet.
00027 //
00028 // I designed the software and hardware in this project especially for my own
00029 // cabinet, but it uses standard interfaces in Windows and Visual Pinball, so it should
00030 // work in any VP-based cabinet, as long as you're using the usual VP software suite.  
00031 // I've tried to document the hardware in enough detail for anyone else to duplicate 
00032 // the entire project, and the full software is open source.
00033 //
00034 // The Freescale board appears to the host PC as a standard USB joystick.  This works 
00035 // with the built-in Windows joystick device drivers, so there's no need to install any
00036 // new drivers or other software on the PC.  Windows should recognize the Freescale
00037 // as a joystick when you plug it into the USB port, and Windows shouldn't ask you to 
00038 // install any drivers.  If you bring up the Windows control panel for USB Game 
00039 // Controllers, this device will appear as "Pinscape Controller".  *Don't* do any 
00040 // calibration with the Windows control panel or third-part calibration tools.  The 
00041 // software calibrates the accelerometer portion automatically, and has its own special
00042 // calibration procedure for the plunger sensor, if you're using that (see below).
00043 //
00044 // This software provides a whole bunch of separate features.  You can use any of these 
00045 // features individually or all together.  If you're not using a particular feature, you
00046 // can simply omit the extra wiring and/or hardware for that feature.  You can use
00047 // the nudging feature by itself without any extra hardware attached, since the
00048 // accelerometer is built in to the KL25Z board.
00049 //
00050 //  - Nudge sensing via the KL25Z's on-board accelerometer.  Nudging the cabinet
00051 //    causes small accelerations that the accelerometer can detect; these are sent to
00052 //    Visual Pinball via the joystick interface so that VP can simulate the effect
00053 //    of the real physical nudges on its simulated ball.  VP has native handling for
00054 //    this type of input, so all you have to do is set some preferences in VP to tell 
00055 //    it that an accelerometer is attached.
00056 //
00057 //  - Plunger position sensing via an attached TAOS TSL 1410R CCD linear array sensor.  
00058 //    To use this feature, you need to buy the TAOS device (it's not built in to the
00059 //    KL25Z, obviously), wire it to the KL25Z (5 wire connections between the two
00060 //    devices are required), and mount the TAOS sensor in your cabinet so that it's
00061 //    positioned properly to capture images of the physical plunger shooter rod.
00062 //
00063 //    The physical mounting and wiring details are desribed in the project 
00064 //    documentation.  
00065 //
00066 //    If the CCD is attached, the software constantly captures images from the CCD
00067 //    and analyzes them to determine how far back the plunger is pulled.  It reports
00068 //    this to Visual Pinball via the joystick interface.  This allows VP to make the
00069 //    simulated on-screen plunger track the motion of the physical plunger in real
00070 //    time.  As with the nudge data, VP has native handling for the plunger input, 
00071 //    so you just need to set the VP preferences to tell it that an analog plunger 
00072 //    device is attached.  One caveat, though: although VP itself has built-in 
00073 //    support for an analog plunger, not all existing tables take advantage of it.  
00074 //    Many existing tables have their own custom plunger scripting that doesn't
00075 //    cooperate with the VP plunger input.  All tables *can* be made to work with
00076 //    the plunger, and in most cases it only requires some simple script editing,
00077 //    but in some cases it requires some more extensive surgery.
00078 //
00079 //    For best results, the plunger sensor should be calibrated.  The calibration
00080 //    is stored in non-volatile memory on board the KL25Z, so it's only necessary
00081 //    to do the calibration once, when you first install everything.  (You might
00082 //    also want to re-calibrate if you physically remove and reinstall the CCD 
00083 //    sensor or the mechanical plunger, since their alignment shift change slightly 
00084 //    when you put everything back together.)  You can optionally install a
00085 //    dedicated momentary switch or pushbutton to activate the calibration mode;
00086 //    this is describe in the project documentation.  If you don't want to bother
00087 //    with the extra button, you can also trigger calibration using the Windows 
00088 //    setup software, which you can find on the Pinscape project page.
00089 //
00090 //    The calibration procedure is described in the project documentation.  Briefly,
00091 //    when you trigger calibration mode, the software will scan the CCD for about
00092 //    15 seconds, during which you should simply pull the physical plunger back
00093 //    all the way, hold it for a moment, and then slowly return it to the rest
00094 //    position.  (DON'T just release it from the retracted position, since that
00095 //    let it shoot forward too far.  We want to measure the range from the park
00096 //    position to the fully retracted position only.)
00097 //
00098 //  - Button input wiring.  24 of the KL25Z's GPIO ports are mapped as digital inputs
00099 //    for buttons and switches.  The software reports these as joystick buttons when
00100 //    it sends reports to the PC.  These can be used to wire physical pinball-style
00101 //    buttons in the cabinet (e.g., flipper buttons, the Start button) and miscellaneous 
00102 //    switches (such as a tilt bob) to the PC.  Visual Pinball can use joystick buttons
00103 //    for input - you just have to assign a VP function to each button using VP's
00104 //    keyboard options dialog.  To wire a button physically, connect one terminal of
00105 //    the button switch to the KL25Z ground, and connect the other terminal to the
00106 //    the GPIO port you wish to assign to the button.  See the buttonMap[] array
00107 //    below for the available GPIO ports and their assigned joystick button numbers.
00108 //    If you're not using a GPIO port, you can just leave it unconnected - the digital
00109 //    inputs have built-in pull-up resistors, so an unconnected port is the same as
00110 //    an open switch (an "off" state for the button).
00111 //
00112 //  - LedWiz emulation.  The KL25Z can appear to the PC as an LedWiz device, and will
00113 //    accept and process LedWiz commands from the host.  The software can turn digital
00114 //    output ports on and off, and can set varying PWM intensitiy levels on a subset
00115 //    of ports.  (The KL25Z can only provide 6 PWM ports.  Intensity level settings on
00116 //    other ports is ignored, so non-PWM ports can only be used for simple on/off
00117 //    devices such as contactors and solenoids.)  The KL25Z can only supply 4mA on its
00118 //    output ports, so external hardware is required to take advantage of the LedWiz
00119 //    emulation.  Many different hardware designs are possible, but there's a simple
00120 //    reference design in the documentation that uses a Darlington array IC to
00121 //    increase the output from each port to 500mA (the same level as the LedWiz),
00122 //    plus an extended design that adds an optocoupler and MOSFET to provide very
00123 //    high power handling, up to about 45A or 150W, with voltages up to 100V.
00124 //    That will handle just about any DC device directly (wtihout relays or other
00125 //    amplifiers), and switches fast enough to support PWM devices.
00126 //
00127 //    The device can report any desired LedWiz unit number to the host, which makes
00128 //    it possible to use the LedWiz emulation on a machine that also has one or more
00129 //    actual LedWiz devices intalled.  The LedWiz design allows for up to 16 units
00130 //    to be installed in one machine - each one is invidually addressable by its
00131 //    distinct unit number.
00132 //
00133 //    The LedWiz emulation features are of course optional.  There's no need to 
00134 //    build any of the external port hardware (or attach anything to the output 
00135 //    ports at all) if the LedWiz features aren't needed.  Most people won't have
00136 //    any use for the LedWiz features.  I built them mostly as a learning exercise,
00137 //    but with a slight practical need for a handful of extra ports (I'm using the
00138 //    cutting-edge 10-contactor setup, so my real LedWiz is full!).
00139 //
00140 //  - Enhanced LedWiz emulation with TLC5940 PWM controller chips.  You can attach
00141 //    external PWM controller chips for controlling device outputs, instead of using
00142 //    the limited LedWiz emulation through the on-board GPIO ports as described above. 
00143 //    The software can control a set of daisy-chained TLC5940 chips, which provide
00144 //    16 PWM outputs per chip.  Two of these chips give you the full complement
00145 //    of 32 output ports of an actual LedWiz, and four give you 64 ports, which
00146 //    should be plenty for nearly any virtual pinball project.  A private, extended
00147 //    version of the LedWiz protocol lets the host control the extra outputs, up to
00148 //    128 outputs per KL25Z (8 TLC5940s).  To take advantage of the extra outputs
00149 //    on the PC side, you need software that knows about the protocol extensions,
00150 //    which means you need the latest version of DirectOutput Framework (DOF).  VP
00151 //    uses DOF for its output, so VP will be able to use the added ports without any
00152 //    extra work on your part.  Older software (e.g., Future Pinball) that doesn't
00153 //    use DOF will still be able to use the LedWiz-compatible protocol, so it'll be
00154 //    able to control your first 32 ports (numbered 1-32 in the LedWiz scheme), but
00155 //    older software won't be able to address higher-numbered ports.  That shouldn't
00156 //    be a problem because older software wouldn't know what to do with the extra
00157 //    devices anyway - FP, for example, is limited to a pre-defined set of outputs.
00158 //    As long as you put the most common devices on the first 32 outputs, and use
00159 //    higher numbered ports for the less common devices that older software can't
00160 //    use anyway, you'll get maximum functionality out of software new and old.
00161 //
00162 // STATUS LIGHTS:  The on-board LED on the KL25Z flashes to indicate the current 
00163 // device status.  The flash patterns are:
00164 //
00165 //    two short red flashes = the device is powered but hasn't successfully
00166 //        connected to the host via USB (either it's not physically connected
00167 //        to the USB port, or there was a problem with the software handshake
00168 //        with the USB device driver on the computer)
00169 //
00170 //    short red flash = the host computer is in sleep/suspend mode
00171 //
00172 //    long red/green = the LedWiz unti number has been changed, so a reset
00173 //        is needed.  You can simply unplug the device and plug it back in,
00174 //        or presss and hold the reset button on the device for a few seconds.
00175 //
00176 //    long yellow/green = everything's working, but the plunger hasn't
00177 //        been calibrated; follow the calibration procedure described above.
00178 //        This flash mode won't appear if the CCD has been disabled.  Note
00179 //        that the device can't tell whether a CCD is physically attached;
00180 //        if you don't have a CCD attached, you can set the appropriate option 
00181 //        in config.h or use the  Windows config tool to disable the CCD 
00182 //        software features.
00183 //
00184 //    alternating blue/green = everything's working
00185 //
00186 // Software configuration: you can some change option settings by sending special
00187 // USB commands from the PC.  I've provided a Windows program for this purpose;
00188 // refer to the documentation for details.  For reference, here's the format
00189 // of the USB command for option changes:
00190 //
00191 //    length of report = 8 bytes
00192 //    byte 0 = 65 (0x41)
00193 //    byte 1 = 1  (0x01)
00194 //    byte 2 = new LedWiz unit number, 0x01 to 0x0f
00195 //    byte 3 = feature enable bit mask:
00196 //             0x01 = enable CCD (default = on)
00197 //
00198 // Plunger calibration mode: the host can activate plunger calibration mode
00199 // by sending this packet.  This has the same effect as pressing and holding
00200 // the plunger calibration button for two seconds, to allow activating this
00201 // mode without attaching a physical button.
00202 //
00203 //    length = 8 bytes
00204 //    byte 0 = 65 (0x41)
00205 //    byte 1 = 2  (0x02)
00206 //
00207 // Exposure reports: the host can request a report of the full set of pixel
00208 // values for the next frame by sending this special packet:
00209 //
00210 //    length = 8 bytes
00211 //    byte 0 = 65 (0x41)
00212 //    byte 1 = 3  (0x03)
00213 //
00214 // We'll respond with a series of special reports giving the exposure status.
00215 // Each report has the following structure:
00216 //
00217 //    bytes 0:1 = 11-bit index, with high 5 bits set to 10000.  For 
00218 //                example, 0x04 0x80 indicates index 4.  This is the 
00219 //                starting pixel number in the report.  The first report 
00220 //                will be 0x00 0x80 to indicate pixel #0.  
00221 //    bytes 2:3 = 16-bit unsigned int brightness level of pixel at index
00222 //    bytes 4:5 = brightness of pixel at index+1
00223 //    etc for the rest of the packet
00224 //
00225 // This still has the form of a joystick packet at the USB level, but
00226 // can be differentiated by the host via the status bits.  It would have
00227 // been cleaner to use a different Report ID at the USB level, but this
00228 // would have necessitated a different container structure in the report
00229 // descriptor, which would have broken LedWiz compatibility.  Given that
00230 // constraint, we have to re-use the joystick report type, making for
00231 // this somewhat kludgey approach.
00232 //
00233 // Configuration query: the host can request a full report of our hardware
00234 // configuration with this message.
00235 //
00236 //    length = 8 bytes
00237 //    byte 0 = 65 (0x41)
00238 //    byte 1 = 4  (0x04)
00239 //
00240 // We'll response with one report containing the configuration status:
00241 //
00242 //    bytes 0:1 = 0x8800.  This has the bit pattern 10001 in the high
00243 //                5 bits, which distinguishes it from regular joystick
00244 //                reports and from exposure status reports.
00245 //    bytes 2:3 = number of outputs
00246 //    remaining bytes = reserved for future use; set to 0 in current version
00247 //
00248 // Turn off all outputs: this message tells the device to turn off all
00249 // outputs and restore power-up LedWiz defaults.  This sets outputs #1-32
00250 // to profile 48 (full brightness) and switch state Off, sets all extended
00251 // outputs (#33 and above) to brightness 0, and sets the LedWiz flash rate
00252 // to 2.
00253 //
00254 //    length = 8 bytes
00255 //    byte 0 = 65 (0x41)
00256 //    byte 1 = 5  (0x05)
00257 
00258 
00259 #include "mbed.h"
00260 #include "math.h"
00261 #include "USBJoystick.h"
00262 #include "MMA8451Q.h"
00263 #include "tsl1410r.h"
00264 #include "FreescaleIAP.h"
00265 #include "crc32.h"
00266 #include "TLC5940.h"
00267 #include "74HC595.h"
00268 
00269 #define DECL_EXTERNS
00270 #include "config.h"
00271 
00272 
00273 // ---------------------------------------------------------------------------
00274 // utilities
00275 
00276 // number of elements in an array
00277 #define countof(x) (sizeof(x)/sizeof((x)[0]))
00278 
00279 // floating point square of a number
00280 inline float square(float x) { return x*x; }
00281 
00282 // floating point rounding
00283 inline float round(float x) { return x > 0 ? floor(x + 0.5) : ceil(x - 0.5); }
00284 
00285 
00286 // --------------------------------------------------------------------------
00287 // 
00288 // USB product version number
00289 //
00290 const uint16_t USB_VERSION_NO = 0x0007;
00291 
00292 
00293 //
00294 // Build the full USB product ID.  If we're using the LedWiz compatible
00295 // vendor ID, the full product ID is the combination of the LedWiz base
00296 // product ID (0x00F0) and the 0-based unit number (0-15).  If we're not
00297 // trying to be LedWiz compatible, we just use the exact product ID
00298 // specified in config.h.
00299 #define MAKE_USB_PRODUCT_ID(vid, pidbase, unit) \
00300     ((vid) == 0xFAFA && (pidbase) == 0x00F0 ? (pidbase) | (unit) : (pidbase))
00301 
00302 
00303 // --------------------------------------------------------------------------
00304 //
00305 // Joystick axis report range - we report from -JOYMAX to +JOYMAX
00306 //
00307 #define JOYMAX 4096
00308 
00309 // --------------------------------------------------------------------------
00310 //
00311 // Set up mappings for the joystick X and Y reports based on the mounting
00312 // orientation of the KL25Z in the cabinet.  Visual Pinball and other 
00313 // pinball software effectively use video coordinates to define the axes:
00314 // positive X is to the right of the table, negative Y to the left, positive
00315 // Y toward the front of the table, negative Y toward the back.  The KL25Z
00316 // accelerometer is mounted on the board with positive Y toward the USB
00317 // ports and positive X toward the right side of the board with the USB
00318 // ports pointing up.  It's a simple matter to remap the KL25Z coordinate
00319 // system to match VP's coordinate system for mounting orientations at
00320 // 90-degree increments...
00321 //
00322 #if defined(ORIENTATION_PORTS_AT_FRONT)
00323 # define JOY_X(x, y)   (y)
00324 # define JOY_Y(x, y)   (x)
00325 #elif defined(ORIENTATION_PORTS_AT_LEFT)
00326 # define JOY_X(x, y)   (-(x))
00327 # define JOY_Y(x, y)   (y)
00328 #elif defined(ORIENTATION_PORTS_AT_RIGHT)
00329 # define JOY_X(x, y)   (x)
00330 # define JOY_Y(x, y)   (-(y))
00331 #elif defined(ORIENTATION_PORTS_AT_REAR)
00332 # define JOY_X(x, y)   (-(y))
00333 # define JOY_Y(x, y)   (-(x))
00334 #else
00335 # error Please define one of the ORIENTATION_PORTS_AT_xxx macros to establish the accelerometer orientation in your cabinet
00336 #endif
00337 
00338 
00339 
00340 // --------------------------------------------------------------------------
00341 //
00342 // Define a symbol to tell us whether any sort of plunger sensor code
00343 // is enabled in this build.  Note that this doesn't tell us that a
00344 // plunger device is actually attached or *currently* enabled; it just
00345 // tells us whether or not the code for plunger sensing is enabled in 
00346 // the software build.  This lets us leave out some unnecessary code
00347 // on installations where no physical plunger is attached.
00348 //
00349 const int PLUNGER_CODE_ENABLED =
00350 #if defined(ENABLE_CCD_SENSOR) || defined(ENABLE_POT_SENSOR)
00351     1;
00352 #else
00353     0;
00354 #endif
00355 
00356 // ---------------------------------------------------------------------------
00357 //
00358 // On-board RGB LED elements - we use these for diagnostic displays.
00359 //
00360 // Note that LED3 (the blue segment) is hard-wired on the KL25Z to PTD1,
00361 // so PTD1 shouldn't be used for any other purpose (e.g., as a keyboard
00362 // input or a device output).  (This is kind of unfortunate in that it's 
00363 // one of only two ports exposed on the jumper pins that can be muxed to 
00364 // SPI0 SCLK.  This effectively limits us to PTC5 if we want to use the 
00365 // SPI capability.)
00366 //
00367 DigitalOut ledR(LED1), ledG(LED2), ledB(LED3);
00368 
00369 
00370 // ---------------------------------------------------------------------------
00371 //
00372 // LedWiz emulation, and enhanced TLC5940 output controller
00373 //
00374 // There are two modes for this feature.  The default mode uses the on-board
00375 // GPIO ports to implement device outputs - each LedWiz software port is
00376 // connected to a physical GPIO pin on the KL25Z.  The KL25Z only has 10
00377 // PWM channels, so in this mode only 10 LedWiz ports will be dimmable; the
00378 // rest are strictly on/off.  The KL25Z also has a limited number of GPIO
00379 // ports overall - not enough for the full complement of 32 LedWiz ports
00380 // and 24 VP joystick inputs, so it's necessary to trade one against the
00381 // other if both features are to be used.
00382 //
00383 // The alternative, enhanced mode uses external TLC5940 PWM controller
00384 // chips to control device outputs.  In this mode, each LedWiz software
00385 // port is mapped to an output on one of the external TLC5940 chips.
00386 // Two 5940s is enough for the full set of 32 LedWiz ports, and we can
00387 // support even more chips for even more outputs (although doing so requires
00388 // breaking LedWiz compatibility, since the LedWiz USB protocol is hardwired
00389 // for 32 outputs).  Every port in this mode has full PWM support.
00390 //
00391 
00392 
00393 // Current starting output index for "PBA" messages from the PC (using
00394 // the LedWiz USB protocol).  Each PBA message implicitly uses the
00395 // current index as the starting point for the ports referenced in
00396 // the message, and increases it (by 8) for the next call.
00397 static int pbaIdx = 0;
00398 
00399 // Generic LedWiz output port interface.  We create a cover class to 
00400 // virtualize digital vs PWM outputs, and on-board KL25Z GPIO vs external 
00401 // TLC5940 outputs, and give them all a common interface.  
00402 class LwOut
00403 {
00404 public:
00405     // Set the output intensity.  'val' is 0.0 for fully off, 1.0 for
00406     // fully on, and fractional values for intermediate intensities.
00407     virtual void set(float val) = 0;
00408 };
00409 
00410 // LwOut class for unmapped ports.  The LedWiz protocol is hardwired
00411 // for 32 ports, but we might not want to assign all 32 software ports
00412 // to physical output pins - the KL25Z has a limited number of GPIO
00413 // ports, so we might not have enough available GPIOs to fill out the
00414 // full LedWiz complement after assigning GPIOs for other functions.
00415 // This class is used to populate the LedWiz mapping array for ports
00416 // that aren't connected to physical outputs; it simply ignores value 
00417 // changes.
00418 class LwUnusedOut: public LwOut
00419 {
00420 public:
00421     LwUnusedOut() { }
00422     virtual void set(float val) { }
00423 };
00424 
00425 // Active Low out.  For any output marked as active low, we layer this
00426 // on top of the physical pin interface.  This simply inverts the value of
00427 // the output value, so that 1.0 means fully off and 0.0 means fully on.
00428 class LwInvertedOut: public LwOut
00429 {
00430 public:
00431     LwInvertedOut(LwOut *o) : out(o) { }
00432     virtual void set(float val) { out->set(1.0 - val); }
00433     
00434 private:
00435     LwOut *out;
00436 };
00437 
00438 
00439 #if TLC5940_NCHIPS
00440 //
00441 // The TLC5940 interface object.  Set this up with the port assignments
00442 // set in config.h.
00443 //
00444 TLC5940 tlc5940(TLC5940_SCLK, TLC5940_SIN, TLC5940_GSCLK, TLC5940_BLANK,
00445     TLC5940_XLAT, TLC5940_NCHIPS);
00446 
00447 // LwOut class for TLC5940 outputs.  These are fully PWM capable.
00448 // The 'idx' value in the constructor is the output index in the
00449 // daisy-chained TLC5940 array.  0 is output #0 on the first chip,
00450 // 1 is #1 on the first chip, 15 is #15 on the first chip, 16 is
00451 // #0 on the second chip, 32 is #0 on the third chip, etc.
00452 class Lw5940Out: public LwOut
00453 {
00454 public:
00455     Lw5940Out(int idx) : idx(idx) { prv = -1; }
00456     virtual void set(float val)
00457     {
00458         if (val != prv)
00459            tlc5940.set(idx, (int)((prv = val) * 4095));
00460     }
00461     int idx;
00462     float prv;
00463 };
00464 
00465 #else
00466 // No TLC5940 chips are attached, so we shouldn't encounter any ports
00467 // in the map marked for TLC5940 outputs.  If we do, treat them as unused.
00468 class Lw5940Out: public LwUnusedOut
00469 {
00470 public:
00471     Lw5940Out(int idx) { }
00472 };
00473 
00474 // dummy tlc5940 interface
00475 class Dummy5940
00476 {
00477 public:
00478     void start() { }
00479 };
00480 Dummy5940 tlc5940;
00481 
00482 #endif // TLC5940_NCHIPS
00483 
00484 #if HC595_NCHIPS
00485 // 74HC595 interface object.  Set this up with the port assignments in
00486 // config.h.
00487 HC595 hc595(HC595_NCHIPS, HC595_SIN, HC595_SCLK, HC595_LATCH, HC595_ENA);
00488 
00489 // LwOut class for 74HC595 outputs.  These are simple digial outs.
00490 // The 'idx' value in the constructor is the output index in the
00491 // daisy-chained 74HC595 array.  0 is output #0 on the first chip,
00492 // 1 is #1 on the first chip, 7 is #7 on the first chip, 8 is
00493 // #0 on the second chip, etc.
00494 class Lw595Out: public LwOut
00495 {
00496 public:
00497     Lw595Out(int idx) : idx(idx) { prv = -1; }
00498     virtual void set(float val)
00499     {
00500         if (val != prv)
00501            hc595.set(idx, (prv = val) == 0.0 ? 0 : 1);
00502     }
00503     int idx;
00504     float prv;
00505 };
00506 
00507 #else // HC595_NCHIPS
00508 // No 74HC595 chips are attached, so we shouldn't encounter any ports
00509 // in the map marked for these outputs.  If we do, treat them as unused.
00510 class Lw595Out: public LwUnusedOut
00511 {
00512 public:
00513     Lw595Out(int idx) { }
00514 };
00515 
00516 // dummy placeholder class
00517 class DummyHC595 
00518 {
00519 public:
00520     void init() { }
00521     void update() { }
00522 };
00523 DummyHC595 hc595;
00524 
00525 #endif // HC595_NCHIPS
00526 
00527 // 
00528 // Default LedWiz mode - using on-board GPIO ports.  In this mode, we
00529 // assign a KL25Z GPIO port to each LedWiz output.  We have to use a
00530 // mix of PWM-capable and Digital-Only ports in this configuration, 
00531 // since the KL25Z hardware only has 10 PWM channels, which isn't
00532 // enough to fill out the full complement of 32 LedWiz outputs.
00533 //
00534 
00535 // LwOut class for a PWM-capable GPIO port
00536 class LwPwmOut: public LwOut
00537 {
00538 public:
00539     LwPwmOut(PinName pin) : p(pin) { prv = -1; }
00540     virtual void set(float val) 
00541     { 
00542         if (val != prv)
00543             p.write(prv = val); 
00544     }
00545     PwmOut p;
00546     float prv;
00547 };
00548 
00549 // LwOut class for a Digital-Only (Non-PWM) GPIO port
00550 class LwDigOut: public LwOut
00551 {
00552 public:
00553     LwDigOut(PinName pin) : p(pin) { prv = -1; }
00554     virtual void set(float val) 
00555     {
00556          if (val != prv)
00557             p.write((prv = val) == 0.0 ? 0 : 1); 
00558     }
00559     DigitalOut p;
00560     float prv;
00561 };
00562 
00563 // Array of output physical pin assignments.  This array is indexed
00564 // by LedWiz logical port number - lwPin[n] is the maping for LedWiz
00565 // port n (0-based).  If we're using GPIO ports to implement outputs,
00566 // we initialize the array at start-up to map each logical port to the 
00567 // physical GPIO pin for the port specified in the ledWizPortMap[] 
00568 // array in config.h.  If we're using TLC5940 chips for the outputs,
00569 // we map each logical port to the corresponding TLC5940 output.
00570 static int numOutputs;
00571 static LwOut **lwPin;
00572 
00573 // Current absolute brightness level for an output.  This is a float
00574 // value from 0.0 for fully off to 1.0 for fully on.  This is the final
00575 // derived value for the port.  For outputs set by LedWiz messages, 
00576 // this is derived from the LedWiz state, and is updated on each pulse 
00577 // timer interrupt for lights in flashing states.  For outputs set by 
00578 // extended protocol messages, this is simply the brightness last set.
00579 static float *outLevel;
00580 
00581 // initialize the output pin array
00582 void initLwOut()
00583 {
00584     // Figure out how many outputs we have.  We always have at least
00585     // 32 outputs, since that's the number fixed by the original LedWiz
00586     // protocol.  If we're using TLC5940 chips, each chip provides 16
00587     // outputs.  Likewise, each 74HC595 provides 8 outputs.
00588     
00589     // start with 16 ports per TLC5940 and 8 per 74HC595
00590     numOutputs = TLC5940_NCHIPS*16 + HC595_NCHIPS*8;
00591     
00592     // add outputs explicitly assigned to GPIO pins or not connected
00593     int i;
00594     for (i = 0 ; i < countof(ledWizPortMap) ; ++i)
00595     {
00596         switch (ledWizPortMap[i].typ)
00597         {
00598         case DIG_GPIO:
00599         case PWM_GPIO:
00600         case NO_PORT:
00601             // count an explicitly GPIO port
00602             ++numOutputs;
00603             break;
00604             
00605         default:
00606             // DON'T count TLC5940 or 74HC595 ports, as we've already
00607             // counted all of these above
00608             break;
00609         }
00610     }
00611     
00612     // always set up at least 32 outputs, so that we don't have to
00613     // check bounds on commands from the basic LedWiz protocol
00614     if (numOutputs < 32)
00615         numOutputs = 32;
00616         
00617     // allocate the pin array
00618     lwPin = new LwOut*[numOutputs];    
00619     
00620     // allocate the current brightness array
00621     outLevel = new float[numOutputs];
00622     
00623     // allocate a temporary array to keep track of which physical 
00624     // TLC5940 ports we've assigned so far
00625     char *tlcasi = new char[TLC5940_NCHIPS*16+1];
00626     memset(tlcasi, 0, TLC5940_NCHIPS*16);
00627 
00628     // likewise for the 74HC595 ports
00629     char *hcasi = new char[HC595_NCHIPS*8+1];
00630     memset(hcasi, 0, HC595_NCHIPS*8);
00631 
00632     // assign all pins from the explicit port map in config.h
00633     for (i = 0 ; i < countof(ledWizPortMap) ; ++i)
00634     {
00635         int pin = ledWizPortMap[i].pin;
00636         LWPortType typ = ledWizPortMap[i].typ;
00637         int flags = ledWizPortMap[i].flags;
00638         int activeLow = flags & PORT_ACTIVE_LOW;
00639         switch (typ)
00640         {
00641         case DIG_GPIO:
00642             lwPin[i] = new LwDigOut((PinName)pin);
00643             break;
00644         
00645         case PWM_GPIO:
00646             // PWM GPIO port
00647             lwPin[i] = new LwPwmOut((PinName)pin);
00648             break;
00649         
00650         case TLC_PORT:
00651             // TLC5940 port (note that the nominal pin in the map is 1-based, so we
00652             // have to decrement it to get the real pin index)
00653             lwPin[i] = new Lw5940Out(pin-1);
00654             tlcasi[pin-1] = 1;
00655             break;
00656         
00657         case HC595_PORT:
00658             // 74HC595 port (the pin in the map is 1-based, so decrement it to get the 
00659             // real pin index)
00660             lwPin[i] = new Lw595Out(pin-1);
00661             hcasi[pin-1] = 1;
00662             break;
00663             
00664         default:
00665             lwPin[i] = new LwUnusedOut();
00666             break;
00667         }
00668         
00669         // if it's Active Low, layer an inverter
00670         if (activeLow)
00671             lwPin[i] = new LwInvertedOut(lwPin[i]);
00672 
00673         // turn it off initially      
00674         lwPin[i]->set(0);
00675     }
00676     
00677     // If we haven't assigned all of the LedWiz ports to physical pins,
00678     // fill out the unassigned LedWiz ports with any unassigned TLC5940
00679     // pins, then with any unassigned 74HC595 ports.
00680     int tlcnxt, hcnxt;
00681     for (tlcnxt = 0 ; tlcnxt < TLC5940_NCHIPS*16 && tlcasi[tlcnxt] ; ++tlcnxt) ;
00682     for (hcnxt = 0 ; hcnxt < HC595_NCHIPS*8 && hcasi[hcnxt] ; ++hcnxt) ;
00683     for ( ; i < numOutputs ; ++i)
00684     {
00685         // If we have any more unassigned TLC5940 outputs, assign this LedWiz
00686         // port to the next available TLC5940 output, or the next 74HC595 output
00687         // if we're out of TLC5940 outputs.  Leave it unassigned if there are
00688         // no more unassigned ports of any type.
00689         if (tlcnxt < TLC5940_NCHIPS*16)
00690         {
00691             // assign this available TLC5940 pin, and find the next unused one
00692             lwPin[i] = new Lw5940Out(tlcnxt);
00693             for (++tlcnxt ; tlcnxt < TLC5940_NCHIPS*16 && tlcasi[tlcnxt] ; ++tlcnxt) ;
00694         }
00695         else if (hcnxt < HC595_NCHIPS*8)
00696         {
00697             // assign this available 74HC595 pin, and find the next unused one
00698             lwPin[i] = new Lw595Out(hcnxt);
00699             for (++hcnxt ; hcnxt < HC595_NCHIPS*8 && hcasi[hcnxt] ; ++hcnxt) ;
00700         }
00701         else
00702         {
00703             // no more ports available - set up this port as unconnected
00704             lwPin[i] = new LwUnusedOut();
00705         }
00706     }
00707     
00708     // done with the temporary TLC5940 and 74HC595 port assignment lists
00709     delete [] tlcasi;
00710     delete [] hcasi;
00711 }
00712 
00713 // LedWiz output states.
00714 //
00715 // The LedWiz protocol has two separate control axes for each output.
00716 // One axis is its on/off state; the other is its "profile" state, which
00717 // is either a fixed brightness or a blinking pattern for the light.
00718 // The two axes are independent.
00719 //
00720 // Note that the LedWiz protocol can only address 32 outputs, so the
00721 // wizOn and wizVal arrays have fixed sizes of 32 elements no matter
00722 // how many physical outputs we're using.
00723 
00724 // on/off state for each LedWiz output
00725 static uint8_t wizOn[32];
00726 
00727 // Profile (brightness/blink) state for each LedWiz output.  If the
00728 // output was last updated through an LedWiz protocol message, it
00729 // will have one of these values:
00730 //
00731 //   0-48 = fixed brightness 0% to 100%
00732 //   129 = ramp up / ramp down
00733 //   130 = flash on / off
00734 //   131 = on / ramp down
00735 //   132 = ramp up / on
00736 //
00737 // Special value 255:  If the output was updated through the 
00738 // extended protocol, we'll set the wizVal entry to 255, which has 
00739 // no meaning in the LedWiz protocol.  This tells us that the value 
00740 // in outLevel[] was set directly from the extended protocol, so it 
00741 // shouldn't be derived from wizVal[].
00742 //
00743 static uint8_t wizVal[32] = {
00744     48, 48, 48, 48, 48, 48, 48, 48,
00745     48, 48, 48, 48, 48, 48, 48, 48,
00746     48, 48, 48, 48, 48, 48, 48, 48,
00747     48, 48, 48, 48, 48, 48, 48, 48
00748 };
00749 
00750 // LedWiz flash speed.  This is a value from 1 to 7 giving the pulse
00751 // rate for lights in blinking states.
00752 static uint8_t wizSpeed = 2;
00753 
00754 // Current LedWiz flash cycle counter.
00755 static uint8_t wizFlashCounter = 0;
00756 
00757 // Get the current brightness level for an LedWiz output.
00758 static float wizState(int idx)
00759 {
00760     // if the output was last set with an extended protocol message,
00761     // use the value set there, ignoring the output's LedWiz state
00762     if (wizVal[idx] == 255)
00763         return outLevel[idx];
00764     
00765     // if it's off, show at zero intensity
00766     if (!wizOn[idx])
00767         return 0;
00768 
00769     // check the state
00770     uint8_t val = wizVal[idx];
00771     if (val <= 48)
00772     {
00773         // PWM brightness/intensity level.  Rescale from the LedWiz
00774         // 0..48 integer range to our internal PwmOut 0..1 float range.
00775         // Note that on the actual LedWiz, level 48 is actually about
00776         // 98% on - contrary to the LedWiz documentation, level 49 is 
00777         // the true 100% level.  (In the documentation, level 49 is
00778         // simply not a valid setting.)  Even so, we treat level 48 as
00779         // 100% on to match the documentation.  This won't be perfectly
00780         // ocmpatible with the actual LedWiz, but it makes for such a
00781         // small difference in brightness (if the output device is an
00782         // LED, say) that no one should notice.  It seems better to
00783         // err in this direction, because while the difference in
00784         // brightness when attached to an LED won't be noticeable, the
00785         // difference in duty cycle when attached to something like a
00786         // contactor *can* be noticeable - anything less than 100%
00787         // can cause a contactor or relay to chatter.  There's almost
00788         // never a situation where you'd want values other than 0% and
00789         // 100% for a contactor or relay, so treating level 48 as 100%
00790         // makes us work properly with software that's expecting the
00791         // documented LedWiz behavior and therefore uses level 48 to
00792         // turn a contactor or relay fully on.
00793         return val/48.0;
00794     }
00795     else if (val == 49)
00796     {
00797         // 49 is undefined in the LedWiz documentation, but actually
00798         // means 100% on.  The documentation says that levels 1-48 are
00799         // the full PWM range, but empirically it appears that the real
00800         // range implemented in the firmware is 1-49.  Some software on
00801         // the PC side (notably DOF) is aware of this and uses level 49
00802         // to mean "100% on".  To ensure compatibility with existing 
00803         // PC-side software, we need to recognize level 49.
00804         return 1.0;
00805     }
00806     else if (val == 129)
00807     {
00808         //   129 = ramp up / ramp down
00809         return wizFlashCounter < 128 
00810             ? wizFlashCounter/128.0 
00811             : (256 - wizFlashCounter)/128.0;
00812     }
00813     else if (val == 130)
00814     {
00815         //   130 = flash on / off
00816         return wizFlashCounter < 128 ? 1.0 : 0.0;
00817     }
00818     else if (val == 131)
00819     {
00820         //   131 = on / ramp down
00821         return wizFlashCounter < 128 ? 1.0 : (255 - wizFlashCounter)/128.0;
00822     }
00823     else if (val == 132)
00824     {
00825         //   132 = ramp up / on
00826         return wizFlashCounter < 128 ? wizFlashCounter/128.0 : 1.0;
00827     }
00828     else
00829     {
00830         // Other values are undefined in the LedWiz documentation.  Hosts
00831         // *should* never send undefined values, since whatever behavior an
00832         // LedWiz unit exhibits in response is accidental and could change
00833         // in a future version.  We'll treat all undefined values as equivalent 
00834         // to 48 (fully on).
00835         return 1.0;
00836     }
00837 }
00838 
00839 // LedWiz flash timer pulse.  This fires periodically to update 
00840 // LedWiz flashing outputs.  At the slowest pulse speed set via
00841 // the SBA command, each waveform cycle has 256 steps, so we
00842 // choose the pulse time base so that the slowest cycle completes
00843 // in 2 seconds.  This seems to roughly match the real LedWiz
00844 // behavior.  We run the pulse timer at the same rate regardless
00845 // of the pulse speed; at higher pulse speeds, we simply use
00846 // larger steps through the cycle on each interrupt.  Running
00847 // every 1/127 of a second = 8ms seems to be a pretty light load.
00848 Timeout wizPulseTimer;
00849 #define WIZ_PULSE_TIME_BASE  (1.0/127.0)
00850 static void wizPulse()
00851 {
00852     // increase the counter by the speed increment, and wrap at 256
00853     wizFlashCounter += wizSpeed;
00854     wizFlashCounter &= 0xff;
00855     
00856     // if we have any flashing lights, update them
00857     int ena = false;
00858     for (int i = 0 ; i < 32 ; ++i)
00859     {
00860         if (wizOn[i])
00861         {
00862             uint8_t s = wizVal[i];
00863             if (s >= 129 && s <= 132)
00864             {
00865                 lwPin[i]->set(wizState(i));
00866                 ena = true;
00867             }
00868         }
00869     }    
00870 
00871     // Set up the next timer pulse only if we found anything flashing.
00872     // To minimize overhead from this feature, we only enable the interrupt
00873     // when we need it.  This eliminates any performance penalty to other
00874     // features when the host software doesn't care about the flashing 
00875     // modes.  For example, DOF never uses these modes, so there's no 
00876     // need for them when running Visual Pinball.
00877     if (ena)
00878         wizPulseTimer.attach(wizPulse, WIZ_PULSE_TIME_BASE);
00879 }
00880 
00881 // Update the physical outputs connected to the LedWiz ports.  This is 
00882 // called after any update from an LedWiz protocol message.
00883 static void updateWizOuts()
00884 {
00885     // update each output
00886     int pulse = false;
00887     for (int i = 0 ; i < 32 ; ++i)
00888     {
00889         pulse |= (wizVal[i] >= 129 && wizVal[i] <= 132);
00890         lwPin[i]->set(wizState(i));
00891     }
00892     
00893     // if any outputs are set to flashing mode, and the pulse timer
00894     // isn't running, turn it on
00895     if (pulse)
00896         wizPulseTimer.attach(wizPulse, WIZ_PULSE_TIME_BASE);
00897         
00898     // flush changes to 74HC595 chips, if attached
00899     hc595.update();
00900 }
00901         
00902 // ---------------------------------------------------------------------------
00903 //
00904 // Button input
00905 //
00906 
00907 // button input map array
00908 DigitalIn *buttonDigIn[32];
00909 
00910 // button state
00911 struct ButtonState
00912 {
00913     // current on/off state
00914     int pressed;
00915     
00916     // Sticky time remaining for current state.  When a
00917     // state transition occurs, we set this to a debounce
00918     // period.  Future state transitions will be ignored
00919     // until the debounce time elapses.
00920     int t;
00921 } buttonState[32];
00922 
00923 // timer for button reports
00924 static Timer buttonTimer;
00925 
00926 // initialize the button inputs
00927 void initButtons()
00928 {
00929     // create the digital inputs
00930     for (int i = 0 ; i < countof(buttonDigIn) ; ++i)
00931     {
00932         if (i < countof(buttonMap) && buttonMap[i] != NC)
00933             buttonDigIn[i] = new DigitalIn(buttonMap[i]);
00934         else
00935             buttonDigIn[i] = 0;
00936     }
00937     
00938     // start the button timer
00939     buttonTimer.start();
00940 }
00941 
00942 
00943 // read the button input state
00944 uint32_t readButtons()
00945 {
00946     // start with all buttons off
00947     uint32_t buttons = 0;
00948     
00949     // figure the time elapsed since the last scan
00950     int dt = buttonTimer.read_ms();
00951     
00952     // reset the timef for the next scan
00953     buttonTimer.reset();
00954     
00955     // scan the button list
00956     uint32_t bit = 1;
00957     DigitalIn **di = buttonDigIn;
00958     ButtonState *bs = buttonState;
00959     for (int i = 0 ; i < countof(buttonDigIn) ; ++i, ++di, ++bs, bit <<= 1)
00960     {
00961         // read this button
00962         if (*di != 0)
00963         {
00964             // deduct the elapsed time since the last update
00965             // from the button's remaining sticky time
00966             bs->t -= dt;
00967             if (bs->t < 0)
00968                 bs->t = 0;
00969             
00970             // If the sticky time has elapsed, note the new physical
00971             // state of the button.  If we still have sticky time
00972             // remaining, ignore the physical state; the last state
00973             // change persists until the sticky time elapses so that
00974             // we smooth out any "bounce" (electrical transients that
00975             // occur when the switch contact is opened or closed).
00976             if (bs->t == 0)
00977             {
00978                 // get the new physical state
00979                 int pressed = !(*di)->read();
00980                 
00981                 // update the button's logical state if this is a change
00982                 if (pressed != bs->pressed)
00983                 {
00984                     // store the new state
00985                     bs->pressed = pressed;
00986                     
00987                     // start a new sticky period for debouncing this
00988                     // state change
00989                     bs->t = 25;
00990                 }
00991             }
00992             
00993             // if it's pressed, OR its bit into the state
00994             if (bs->pressed)
00995                 buttons |= bit;
00996         }
00997     }
00998     
00999     // return the new button list
01000     return buttons;
01001 }
01002 
01003 // ---------------------------------------------------------------------------
01004 //
01005 // Customization joystick subbclass
01006 //
01007 
01008 class MyUSBJoystick: public USBJoystick
01009 {
01010 public:
01011     MyUSBJoystick(uint16_t vendor_id, uint16_t product_id, uint16_t product_release) 
01012         : USBJoystick(vendor_id, product_id, product_release, true)
01013     {
01014         suspended_ = false;
01015     }
01016     
01017     // are we connected?
01018     int isConnected()  { return configured(); }
01019     
01020     // Are we in suspend mode?
01021     int isSuspended() const { return suspended_; }
01022     
01023 protected:
01024     virtual void suspendStateChanged(unsigned int suspended)
01025         { suspended_ = suspended; }
01026 
01027     // are we suspended?
01028     int suspended_; 
01029 };
01030 
01031 // ---------------------------------------------------------------------------
01032 // 
01033 // Accelerometer (MMA8451Q)
01034 //
01035 
01036 // The MMA8451Q is the KL25Z's on-board 3-axis accelerometer.
01037 //
01038 // This is a custom wrapper for the library code to interface to the
01039 // MMA8451Q.  This class encapsulates an interrupt handler and 
01040 // automatic calibration.
01041 //
01042 // We install an interrupt handler on the accelerometer "data ready" 
01043 // interrupt to ensure that we fetch each sample immediately when it
01044 // becomes available.  The accelerometer data rate is fiarly high
01045 // (800 Hz), so it's not practical to keep up with it by polling.
01046 // Using an interrupt handler lets us respond quickly and read
01047 // every sample.
01048 //
01049 // We automatically calibrate the accelerometer so that it's not
01050 // necessary to get it exactly level when installing it, and so
01051 // that it's also not necessary to calibrate it manually.  There's
01052 // lots of experience that tells us that manual calibration is a
01053 // terrible solution, mostly because cabinets tend to shift slightly
01054 // during use, requiring frequent recalibration.  Instead, we
01055 // calibrate automatically.  We continuously monitor the acceleration
01056 // data, watching for periods of constant (or nearly constant) values.
01057 // Any time it appears that the machine has been at rest for a while
01058 // (about 5 seconds), we'll average the readings during that rest
01059 // period and use the result as the level rest position.  This is
01060 // is ongoing, so we'll quickly find the center point again if the 
01061 // machine is moved during play (by an especially aggressive bout
01062 // of nudging, say).
01063 //
01064 
01065 // I2C address of the accelerometer (this is a constant of the KL25Z)
01066 const int MMA8451_I2C_ADDRESS = (0x1d<<1);
01067 
01068 // SCL and SDA pins for the accelerometer (constant for the KL25Z)
01069 #define MMA8451_SCL_PIN   PTE25
01070 #define MMA8451_SDA_PIN   PTE24
01071 
01072 // Digital in pin to use for the accelerometer interrupt.  For the KL25Z,
01073 // this can be either PTA14 or PTA15, since those are the pins physically
01074 // wired on this board to the MMA8451 interrupt controller.
01075 #define MMA8451_INT_PIN   PTA15
01076 
01077 
01078 // accelerometer input history item, for gathering calibration data
01079 struct AccHist
01080 {
01081     AccHist() { x = y = d = 0.0; xtot = ytot = 0.0; cnt = 0; }
01082     void set(float x, float y, AccHist *prv)
01083     {
01084         // save the raw position
01085         this->x = x;
01086         this->y = y;
01087         this->d = distance(prv);
01088     }
01089     
01090     // reading for this entry
01091     float x, y;
01092     
01093     // distance from previous entry
01094     float d;
01095     
01096     // total and count of samples averaged over this period
01097     float xtot, ytot;
01098     int cnt;
01099 
01100     void clearAvg() { xtot = ytot = 0.0; cnt = 0; }    
01101     void addAvg(float x, float y) { xtot += x; ytot += y; ++cnt; }
01102     float xAvg() const { return xtot/cnt; }
01103     float yAvg() const { return ytot/cnt; }
01104     
01105     float distance(AccHist *p)
01106         { return sqrt(square(p->x - x) + square(p->y - y)); }
01107 };
01108 
01109 // accelerometer wrapper class
01110 class Accel
01111 {
01112 public:
01113     Accel(PinName sda, PinName scl, int i2cAddr, PinName irqPin)
01114         : mma_(sda, scl, i2cAddr), intIn_(irqPin)
01115     {
01116         // remember the interrupt pin assignment
01117         irqPin_ = irqPin;
01118 
01119         // reset and initialize
01120         reset();
01121     }
01122     
01123     void reset()
01124     {
01125         // clear the center point
01126         cx_ = cy_ = 0.0;
01127         
01128         // start the calibration timer
01129         tCenter_.start();
01130         iAccPrv_ = nAccPrv_ = 0;
01131         
01132         // reset and initialize the MMA8451Q
01133         mma_.init();
01134                 
01135         // set the initial integrated velocity reading to zero
01136         vx_ = vy_ = 0;
01137         
01138         // set up our accelerometer interrupt handling
01139         intIn_.rise(this, &Accel::isr);
01140         mma_.setInterruptMode(irqPin_ == PTA14 ? 1 : 2);
01141         
01142         // read the current registers to clear the data ready flag
01143         mma_.getAccXYZ(ax_, ay_, az_);
01144 
01145         // start our timers
01146         tGet_.start();
01147         tInt_.start();
01148     }
01149     
01150     void get(int &x, int &y) 
01151     {
01152          // disable interrupts while manipulating the shared data
01153          __disable_irq();
01154          
01155          // read the shared data and store locally for calculations
01156          float ax = ax_, ay = ay_;
01157          float vx = vx_, vy = vy_;
01158          
01159          // reset the velocity sum for the next run
01160          vx_ = vy_ = 0;
01161 
01162          // get the time since the last get() sample
01163          float dt = tGet_.read_us()/1.0e6;
01164          tGet_.reset();
01165          
01166          // done manipulating the shared data
01167          __enable_irq();
01168          
01169          // adjust the readings for the integration time
01170          vx /= dt;
01171          vy /= dt;
01172          
01173          // add this sample to the current calibration interval's running total
01174          AccHist *p = accPrv_ + iAccPrv_;
01175          p->addAvg(ax, ay);
01176 
01177          // check for auto-centering every so often
01178          if (tCenter_.read_ms() > 1000)
01179          {
01180              // add the latest raw sample to the history list
01181              AccHist *prv = p;
01182              iAccPrv_ = (iAccPrv_ + 1) % maxAccPrv;
01183              p = accPrv_ + iAccPrv_;
01184              p->set(ax, ay, prv);
01185 
01186              // if we have a full complement, check for stability
01187              if (nAccPrv_ >= maxAccPrv)
01188              {
01189                  // check if we've been stable for all recent samples
01190                  static const float accTol = .01;
01191                  AccHist *p0 = accPrv_;
01192                  if (p0[0].d < accTol
01193                      && p0[1].d < accTol
01194                      && p0[2].d < accTol
01195                      && p0[3].d < accTol
01196                      && p0[4].d < accTol)
01197                  {
01198                      // Figure the new calibration point as the average of
01199                      // the samples over the rest period
01200                      cx_ = (p0[0].xAvg() + p0[1].xAvg() + p0[2].xAvg() + p0[3].xAvg() + p0[4].xAvg())/5.0;
01201                      cy_ = (p0[0].yAvg() + p0[1].yAvg() + p0[2].yAvg() + p0[3].yAvg() + p0[4].yAvg())/5.0;
01202                  }
01203              }
01204              else
01205              {
01206                 // not enough samples yet; just up the count
01207                 ++nAccPrv_;
01208              }
01209              
01210              // clear the new item's running totals
01211              p->clearAvg();
01212             
01213              // reset the timer
01214              tCenter_.reset();
01215          }
01216          
01217          // report our integrated velocity reading in x,y
01218          x = rawToReport(vx);
01219          y = rawToReport(vy);
01220          
01221 #ifdef DEBUG_PRINTF
01222          if (x != 0 || y != 0)        
01223              printf("%f %f %d %d %f\r\n", vx, vy, x, y, dt);
01224 #endif
01225      }    
01226          
01227 private:
01228     // adjust a raw acceleration figure to a usb report value
01229     int rawToReport(float v)
01230     {
01231         // scale to the joystick report range and round to integer
01232         int i = int(round(v*JOYMAX));
01233         
01234         // if it's near the center, scale it roughly as 20*(i/20)^2,
01235         // to suppress noise near the rest position
01236         static const int filter[] = { 
01237             -18, -16, -14, -13, -11, -10, -8, -7, -6, -5, -4, -3, -2, -2, -1, -1, 0, 0, 0, 0,
01238             0,
01239             0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 16, 18
01240         };
01241         return (i > 20 || i < -20 ? i : filter[i+20]);
01242     }
01243 
01244     // interrupt handler
01245     void isr()
01246     {
01247         // Read the axes.  Note that we have to read all three axes
01248         // (even though we only really use x and y) in order to clear
01249         // the "data ready" status bit in the accelerometer.  The
01250         // interrupt only occurs when the "ready" bit transitions from
01251         // off to on, so we have to make sure it's off.
01252         float x, y, z;
01253         mma_.getAccXYZ(x, y, z);
01254         
01255         // calculate the time since the last interrupt
01256         float dt = tInt_.read_us()/1.0e6;
01257         tInt_.reset();
01258 
01259         // integrate the time slice from the previous reading to this reading
01260         vx_ += (x + ax_ - 2*cx_)*dt/2;
01261         vy_ += (y + ay_ - 2*cy_)*dt/2;
01262         
01263         // store the updates
01264         ax_ = x;
01265         ay_ = y;
01266         az_ = z;
01267     }
01268     
01269     // underlying accelerometer object
01270     MMA8451Q mma_;
01271     
01272     // last raw acceleration readings
01273     float ax_, ay_, az_;
01274     
01275     // integrated velocity reading since last get()
01276     float vx_, vy_;
01277         
01278     // timer for measuring time between get() samples
01279     Timer tGet_;
01280     
01281     // timer for measuring time between interrupts
01282     Timer tInt_;
01283 
01284     // Calibration reference point for accelerometer.  This is the
01285     // average reading on the accelerometer when in the neutral position
01286     // at rest.
01287     float cx_, cy_;
01288 
01289     // timer for atuo-centering
01290     Timer tCenter_;
01291 
01292     // Auto-centering history.  This is a separate history list that
01293     // records results spaced out sparesely over time, so that we can
01294     // watch for long-lasting periods of rest.  When we observe nearly
01295     // no motion for an extended period (on the order of 5 seconds), we
01296     // take this to mean that the cabinet is at rest in its neutral 
01297     // position, so we take this as the calibration zero point for the
01298     // accelerometer.  We update this history continuously, which allows
01299     // us to continuously re-calibrate the accelerometer.  This ensures
01300     // that we'll automatically adjust to any actual changes in the
01301     // cabinet's orientation (e.g., if it gets moved slightly by an
01302     // especially strong nudge) as well as any systematic drift in the
01303     // accelerometer measurement bias (e.g., from temperature changes).
01304     int iAccPrv_, nAccPrv_;
01305     static const int maxAccPrv = 5;
01306     AccHist accPrv_[maxAccPrv];
01307     
01308     // interurupt pin name
01309     PinName irqPin_;
01310     
01311     // interrupt router
01312     InterruptIn intIn_;
01313 };
01314 
01315 
01316 // ---------------------------------------------------------------------------
01317 //
01318 // Clear the I2C bus for the MMA8451Q.  This seems necessary some of the time
01319 // for reasons that aren't clear to me.  Doing a hard power cycle has the same
01320 // effect, but when we do a soft reset, the hardware sometimes seems to leave
01321 // the MMA's SDA line stuck low.  Forcing a series of 9 clock pulses through
01322 // the SCL line is supposed to clear this condition.  I'm not convinced this
01323 // actually works with the way this component is wired on the KL25Z, but it
01324 // seems harmless, so we'll do it on reset in case it does some good.  What
01325 // we really seem to need is a way to power cycle the MMA8451Q if it ever 
01326 // gets stuck, but this is simply not possible in software on the KL25Z. 
01327 // 
01328 // If the accelerometer does get stuck, and a software reboot doesn't reset
01329 // it, the only workaround is to manually power cycle the whole KL25Z by 
01330 // unplugging both of its USB connections.
01331 //
01332 void clear_i2c()
01333 {
01334     // assume a general-purpose output pin to the I2C clock
01335     DigitalOut scl(MMA8451_SCL_PIN);
01336     DigitalIn sda(MMA8451_SDA_PIN);
01337     
01338     // clock the SCL 9 times
01339     for (int i = 0 ; i < 9 ; ++i)
01340     {
01341         scl = 1;
01342         wait_us(20);
01343         scl = 0;
01344         wait_us(20);
01345     }
01346 }
01347  
01348 // ---------------------------------------------------------------------------
01349 //
01350 // Include the appropriate plunger sensor definition.  This will define a
01351 // class called PlungerSensor, with a standard interface that we use in
01352 // the main loop below.  This is *kind of* like a virtual class interface,
01353 // but it actually defines the methods statically, which is a little more
01354 // efficient at run-time.  There's no need for a true virtual interface
01355 // because we don't need to be able to change sensor types on the fly.
01356 //
01357 
01358 #if defined(ENABLE_CCD_SENSOR)
01359 #include "ccdSensor.h"
01360 #elif defined(ENABLE_POT_SENSOR)
01361 #include "potSensor.h"
01362 #else
01363 #include "nullSensor.h"
01364 #endif
01365 
01366 
01367 // ---------------------------------------------------------------------------
01368 //
01369 // Non-volatile memory (NVM)
01370 //
01371 
01372 // Structure defining our NVM storage layout.  We store a small
01373 // amount of persistent data in flash memory to retain calibration
01374 // data when powered off.
01375 struct NVM
01376 {
01377     // checksum - we use this to determine if the flash record
01378     // has been properly initialized
01379     uint32_t checksum;
01380 
01381     // signature and version, to verify that we saved the config
01382     // data to flash on a past run (as opposed to uninitialized
01383     // data from a firmware update)
01384     static const uint32_t SIGNATURE = 0x4D4A522A;
01385     static const uint16_t VERSION = 0x0003;
01386     
01387     // Is the data structure valid?  We test the signature and 
01388     // checksum to determine if we've been properly stored.
01389     int valid() const
01390     {
01391         return (d.sig == SIGNATURE 
01392                 && d.vsn == VERSION
01393                 && d.sz == sizeof(NVM)
01394                 && checksum == CRC32(&d, sizeof(d)));
01395     }
01396     
01397     // save to non-volatile memory
01398     void save(FreescaleIAP &iap, int addr)
01399     {
01400         // update the checksum and structure size
01401         d.sig = SIGNATURE;
01402         d.vsn = VERSION;
01403         d.sz = sizeof(NVM);
01404         checksum = CRC32(&d, sizeof(d));
01405         
01406         // erase the sector
01407         iap.erase_sector(addr);
01408 
01409         // save the data
01410         iap.program_flash(addr, this, sizeof(*this));
01411     }
01412     
01413     // reset calibration data for calibration mode
01414     void resetPlunger()
01415     {
01416         // set extremes for the calibration data
01417         d.plungerMax = 0;
01418         d.plungerZero = npix;
01419         d.plungerMin = npix;
01420     }
01421     
01422     // stored data (excluding the checksum)
01423     struct
01424     {
01425         // Signature, structure version, and structure size - further verification 
01426         // that we have valid initialized data.  The size is a simple proxy for a
01427         // structure version, as the most common type of change to the structure as
01428         // the software evolves will be the addition of new elements.  We also
01429         // provide an explicit version number that we can update manually if we
01430         // make any changes that don't affect the structure size but would affect
01431         // compatibility with a saved record (e.g., swapping two existing elements).
01432         uint32_t sig;
01433         uint16_t vsn;
01434         int sz;
01435         
01436         // has the plunger been manually calibrated?
01437         int plungerCal;
01438         
01439         // Plunger calibration min, zero, and max.  The zero point is the 
01440         // rest position (aka park position), where it's in equilibrium between 
01441         // the main spring and the barrel spring.  It can travel a small distance
01442         // forward of the rest position, because the barrel spring can be
01443         // compressed by the user pushing on the plunger or by the momentum
01444         // of a release motion.  The minimum is the maximum forward point where
01445         // the barrel spring can't be compressed any further.
01446         int plungerMin;
01447         int plungerZero;
01448         int plungerMax;
01449         
01450         // is the plunger sensor enabled?
01451         int plungerEnabled;
01452         
01453         // LedWiz unit number
01454         uint8_t ledWizUnitNo;
01455     } d;
01456 };
01457 
01458 // ---------------------------------------------------------------------------
01459 //
01460 // Simple binary (on/off) input debouncer.  Requires an input to be stable 
01461 // for a given interval before allowing an update.
01462 //
01463 class Debouncer
01464 {
01465 public:
01466     Debouncer(bool initVal, float tmin)
01467     {
01468         t.start();
01469         this->stable = this->prv = initVal;
01470         this->tmin = tmin;
01471     }
01472     
01473     // Get the current stable value
01474     bool val() const { return stable; }
01475 
01476     // Apply a new sample.  This tells us the new raw reading from the
01477     // input device.
01478     void sampleIn(bool val)
01479     {
01480         // If the new raw reading is different from the previous
01481         // raw reading, we've detected an edge - start the clock
01482         // on the sample reader.
01483         if (val != prv)
01484         {
01485             // we have an edge - reset the sample clock
01486             t.reset();
01487             
01488             // this is now the previous raw sample for nxt time
01489             prv = val;
01490         }
01491         else if (val != stable)
01492         {
01493             // The new raw sample is the same as the last raw sample,
01494             // and different from the stable value.  This means that
01495             // the sample value has been the same for the time currently
01496             // indicated by our timer.  If enough time has elapsed to
01497             // consider the value stable, apply the new value.
01498             if (t.read() > tmin)
01499                 stable = val;
01500         }
01501     }
01502     
01503 private:
01504     // current stable value
01505     bool stable;
01506 
01507     // last raw sample value
01508     bool prv;
01509     
01510     // elapsed time since last raw input change
01511     Timer t;
01512     
01513     // Minimum time interval for stability, in seconds.  Input readings 
01514     // must be stable for this long before the stable value is updated.
01515     float tmin;
01516 };
01517 
01518 
01519 // ---------------------------------------------------------------------------
01520 //
01521 // Turn off all outputs and restore everything to the default LedWiz
01522 // state.  This sets outputs #1-32 to LedWiz profile value 48 (full
01523 // brightness) and switch state Off, sets all extended outputs (#33
01524 // and above) to zero brightness, and sets the LedWiz flash rate to 2.
01525 // This effectively restores the power-on conditions.
01526 //
01527 void allOutputsOff()
01528 {
01529     // reset all LedWiz outputs to OFF/48
01530     for (int i = 0 ; i < 32 ; ++i)
01531     {
01532         outLevel[i] = 0;
01533         wizOn[i] = 0;
01534         wizVal[i] = 48;
01535         lwPin[i]->set(0);
01536     }
01537     
01538     // reset all extended outputs (ports >32) to full off (brightness 0)
01539     for (int i = 32 ; i < numOutputs ; ++i)
01540     {
01541         outLevel[i] = 0;
01542         lwPin[i]->set(0);
01543     }
01544     
01545     // restore default LedWiz flash rate
01546     wizSpeed = 2;
01547     
01548     // flush changes to hc595, if applicable
01549     hc595.update();
01550 }
01551 
01552 // ---------------------------------------------------------------------------
01553 //
01554 // TV ON timer.  If this feature is enabled, we toggle a TV power switch
01555 // relay (connected to a GPIO pin) to turn on the cab's TV monitors shortly
01556 // after the system is powered.  This is useful for TVs that don't remember
01557 // their power state and don't turn back on automatically after being
01558 // unplugged and plugged in again.  This feature requires external
01559 // circuitry, which is built in to the expansion board and can also be
01560 // built separately - see the Build Guide for the circuit plan.
01561 //
01562 // Theory of operation: to use this feature, the cabinet must have a 
01563 // secondary PC-style power supply (PSU2) for the feedback devices, and
01564 // this secondary supply must be plugged in to the same power strip or 
01565 // switched outlet that controls power to the TVs.  This lets us use PSU2
01566 // as a proxy for the TV power state - when PSU2 is on, the TV outlet is 
01567 // powered, and when PSU2 is off, the TV outlet is off.  We use a little 
01568 // latch circuit powered by PSU2 to monitor the status.  The latch has a 
01569 // current state, ON or OFF, that we can read via a GPIO input pin, and 
01570 // we can set the state to ON by pulsing a separate GPIO output pin.  As 
01571 // long as PSU2 is powered off, the latch stays in the OFF state, even if 
01572 // we try to set it by pulsing the SET pin.  When PSU2 is turned on after 
01573 // being off, the latch starts receiving power but stays in the OFF state, 
01574 // since this is the initial condition when the power first comes on.  So 
01575 // if our latch state pin is reading OFF, we know that PSU2 is either off 
01576 // now or *was* off some time since we last checked.  We use a timer to 
01577 // check the state periodically.  Each time we see the state is OFF, we 
01578 // try pulsing the SET pin.  If the state still reads as OFF, we know 
01579 // that PSU2 is currently off; if the state changes to ON, though, we 
01580 // know that PSU2 has gone from OFF to ON some time between now and the 
01581 // previous check.  When we see this condition, we start a countdown
01582 // timer, and pulse the TV switch relay when the countdown ends.
01583 //
01584 // This scheme might seem a little convoluted, but it neatly handles
01585 // all of the different cases that can occur:
01586 //
01587 // - Most cabinets systems are set up with "soft" PC power switches, 
01588 //   so that the PC goes into "Soft Off" mode (ACPI state S5, in Windows
01589 //   parlance) when the user turns off the cabinet.  In this state, the
01590 //   motherboard supplies power to USB devices, so the KL25Z continues
01591 //   running without interruption.  The latch system lets us monitor
01592 //   the power state even when we're never rebooted, since the latch
01593 //   will turn off when PSU2 is off regardless of what the KL25Z is doing.
01594 //
01595 // - Some cabinet builders might prefer to use "hard" power switches,
01596 //   cutting all power to the cabinet, including the PC motherboard (and
01597 //   thus the KL25Z) every time the machine is turned off.  This also
01598 //   applies to the "soft" switch case above when the cabinet is unplugged,
01599 //   a power outage occurs, etc.  In these cases, the KL25Z will do a cold
01600 //   boot when the PC is turned on.  We don't know whether the KL25Z
01601 //   will power up before or after PSU2, so it's not good enough to 
01602 //   observe the *current* state of PSU2 when we first check - if PSU2
01603 //   were to come on first, checking the current state alone would fool
01604 //   us into thinking that no action is required, because we would never
01605 //   have known that PSU2 was ever off.  The latch handles this case by
01606 //   letting us see that PSU2 *was* off before we checked.
01607 //
01608 // - If the KL25Z is rebooted while the main system is running, or the 
01609 //   KL25Z is unplugged and plugged back in, we will correctly leave the 
01610 //   TVs as they are.  The latch state is independent of the KL25Z's 
01611 //   power or software state, so it's won't affect the latch state when
01612 //   the KL25Z is unplugged or rebooted; when we boot, we'll see that 
01613 //   the latch is already on and that we don't have to turn on the TVs.
01614 //   This is important because TV ON buttons are usually on/off toggles,
01615 //   so we don't want to push the button on a TV that's already on.
01616 //   
01617 //
01618 #ifdef ENABLE_TV_TIMER
01619 
01620 // Current PSU2 state:
01621 //   1 -> default: latch was on at last check, or we haven't checked yet
01622 //   2 -> latch was off at last check, SET pulsed high
01623 //   3 -> SET pulsed low, ready to check status
01624 //   4 -> TV timer countdown in progress
01625 //   5 -> TV relay on
01626 //   
01627 int psu2_state = 1;
01628 DigitalIn psu2_status_sense(PSU2_STATUS_SENSE);
01629 DigitalOut psu2_status_set(PSU2_STATUS_SET);
01630 DigitalOut tv_relay(TV_RELAY_PIN);
01631 Timer tv_timer;
01632 void TVTimerInt()
01633 {
01634     // Check our internal state
01635     switch (psu2_state)
01636     {
01637     case 1:
01638         // Default state.  This means that the latch was on last
01639         // time we checked or that this is the first check.  In
01640         // either case, if the latch is off, switch to state 2 and
01641         // try pulsing the latch.  Next time we check, if the latch
01642         // stuck, it means that PSU2 is now on after being off.
01643         if (!psu2_status_sense)
01644         {
01645             // switch to OFF state
01646             psu2_state = 2;
01647             
01648             // try setting the latch
01649             psu2_status_set = 1;
01650         }
01651         break;
01652         
01653     case 2:
01654         // PSU2 was off last time we checked, and we tried setting
01655         // the latch.  Drop the SET signal and go to CHECK state.
01656         psu2_status_set = 0;
01657         psu2_state = 3;
01658         break;
01659         
01660     case 3:
01661         // CHECK state: we pulsed SET, and we're now ready to see
01662         // if that stuck.  If the latch is now on, PSU2 has transitioned
01663         // from OFF to ON, so start the TV countdown.  If the latch is
01664         // off, our SET command didn't stick, so PSU2 is still off.
01665         if (psu2_status_sense)
01666         {
01667             // The latch stuck, so PSU2 has transitioned from OFF
01668             // to ON.  Start the TV countdown timer.
01669             tv_timer.reset();
01670             tv_timer.start();
01671             psu2_state = 4;
01672         }
01673         else
01674         {
01675             // The latch didn't stick, so PSU2 was still off at
01676             // our last check.  Try pulsing it again in case PSU2
01677             // was turned on since the last check.
01678             psu2_status_set = 1;
01679             psu2_state = 2;
01680         }
01681         break;
01682         
01683     case 4:
01684         // TV timer countdown in progress.  If we've reached the
01685         // delay time, pulse the relay.
01686         if (tv_timer.read() >= TV_DELAY_TIME)
01687         {
01688             // turn on the relay for one timer interval
01689             tv_relay = 1;
01690             psu2_state = 5;
01691         }
01692         break;
01693         
01694     case 5:
01695         // TV timer relay on.  We pulse this for one interval, so
01696         // it's now time to turn it off and return to the default state.
01697         tv_relay = 0;
01698         psu2_state = 1;
01699         break;
01700     }
01701 }
01702 
01703 Ticker tv_ticker;
01704 void startTVTimer()
01705 {
01706     // Set up our time routine to run every 1/4 second.  
01707     tv_ticker.attach(&TVTimerInt, 0.25);
01708 }
01709 
01710 
01711 #else // ENABLE_TV_TIMER
01712 //
01713 // TV timer not used - just provide a dummy startup function
01714 void startTVTimer() { }
01715 //
01716 #endif // ENABLE_TV_TIMER
01717 
01718 
01719 // ---------------------------------------------------------------------------
01720 //
01721 // Main program loop.  This is invoked on startup and runs forever.  Our
01722 // main work is to read our devices (the accelerometer and the CCD), process
01723 // the readings into nudge and plunger position data, and send the results
01724 // to the host computer via the USB joystick interface.  We also monitor
01725 // the USB connection for incoming LedWiz commands and process those into
01726 // port outputs.
01727 //
01728 int main(void)
01729 {
01730     // turn off our on-board indicator LED
01731     ledR = 1;
01732     ledG = 1;
01733     ledB = 1;
01734     
01735     // start the TV timer, if applicable
01736     startTVTimer();
01737     
01738     // we're not connected/awake yet
01739     bool connected = false;
01740     time_t connectChangeTime = time(0);
01741 
01742     // initialize the LedWiz ports
01743     initLwOut();
01744     
01745     // initialize the button input ports
01746     initButtons();
01747 
01748     // start the TLC5940 clock, if present
01749     tlc5940.start();
01750 
01751     // enable the 74HC595 chips, if present
01752     hc595.init();
01753     hc595.update();
01754 
01755     // we don't need a reset yet
01756     bool needReset = false;
01757     
01758     // clear the I2C bus for the accelerometer
01759     clear_i2c();
01760     
01761     // set up a flash memory controller
01762     FreescaleIAP iap;
01763     
01764     // use the last sector of flash for our non-volatile memory structure
01765     int flash_addr = (iap.flash_size() - SECTOR_SIZE);
01766     NVM *flash = (NVM *)flash_addr;
01767     NVM cfg;
01768     
01769     // if the flash is valid, load it; otherwise initialize to defaults
01770     if (flash->valid()) {
01771         memcpy(&cfg, flash, sizeof(cfg));
01772         printf("Flash restored: plunger cal=%d, min=%d, zero=%d, max=%d\r\n", 
01773             cfg.d.plungerCal, cfg.d.plungerMin, cfg.d.plungerZero, cfg.d.plungerMax);
01774     }
01775     else {
01776         printf("Factory reset\r\n");
01777         cfg.d.plungerCal = 0;
01778         cfg.d.plungerMin = 0;        // assume we can go all the way forward...
01779         cfg.d.plungerMax = npix;     // ...and all the way back
01780         cfg.d.plungerZero = npix/6;  // the rest position is usually around 1/2" back
01781         cfg.d.ledWizUnitNo = DEFAULT_LEDWIZ_UNIT_NUMBER - 1;  // unit numbering starts from 0 internally
01782         cfg.d.plungerEnabled = PLUNGER_CODE_ENABLED;
01783     }
01784     
01785     // Create the joystick USB client.  Note that we use the LedWiz unit
01786     // number from the saved configuration.
01787     MyUSBJoystick js(
01788         USB_VENDOR_ID, 
01789         MAKE_USB_PRODUCT_ID(USB_VENDOR_ID, USB_PRODUCT_ID, cfg.d.ledWizUnitNo),
01790         USB_VERSION_NO);
01791         
01792     // last report timer - we use this to throttle reports, since VP
01793     // doesn't want to hear from us more than about every 10ms
01794     Timer reportTimer;
01795     reportTimer.start();
01796 
01797     // initialize the calibration buttons, if present
01798     DigitalIn *calBtn = (CAL_BUTTON_PIN == NC ? 0 : new DigitalIn(CAL_BUTTON_PIN));
01799     DigitalOut *calBtnLed = (CAL_BUTTON_LED == NC ? 0 : new DigitalOut(CAL_BUTTON_LED));
01800 
01801     // plunger calibration button debounce timer
01802     Timer calBtnTimer;
01803     calBtnTimer.start();
01804     int calBtnLit = false;
01805     
01806     // Calibration button state:
01807     //  0 = not pushed
01808     //  1 = pushed, not yet debounced
01809     //  2 = pushed, debounced, waiting for hold time
01810     //  3 = pushed, hold time completed - in calibration mode
01811     int calBtnState = 0;
01812     
01813     // set up a timer for our heartbeat indicator
01814     Timer hbTimer;
01815     hbTimer.start();
01816     int hb = 0;
01817     uint16_t hbcnt = 0;
01818     
01819     // set a timer for accelerometer auto-centering
01820     Timer acTimer;
01821     acTimer.start();
01822     
01823     // create the accelerometer object
01824     Accel accel(MMA8451_SCL_PIN, MMA8451_SDA_PIN, MMA8451_I2C_ADDRESS, MMA8451_INT_PIN);
01825     
01826 #ifdef ENABLE_JOYSTICK
01827     // last accelerometer report, in joystick units (we report the nudge
01828     // acceleration via the joystick x & y axes, per the VP convention)
01829     int x = 0, y = 0;
01830     
01831     // flag: send a pixel dump after the next read
01832     bool reportPix = false;
01833 #endif
01834 
01835     // create our plunger sensor object
01836     PlungerSensor plungerSensor;
01837 
01838     // last plunger report position, in 'npix' normalized pixel units
01839     int pos = 0;
01840     
01841     // last plunger report, in joystick units (we report the plunger as the
01842     // "z" axis of the joystick, per the VP convention)
01843     int z = 0;
01844     
01845     // most recent prior plunger readings, for tracking release events(z0 is
01846     // reading just before the last one we reported, z1 is the one before that, 
01847     // z2 the next before that)
01848     int z0 = 0, z1 = 0, z2 = 0;
01849     
01850     // Simulated "bounce" position when firing.  We model the bounce off of
01851     // the barrel spring when the plunger is released as proportional to the
01852     // distance it was retracted just before being released.
01853     int zBounce = 0;
01854     
01855     // Simulated Launch Ball button state.  If a "ZB Launch Ball" port is
01856     // defined for our LedWiz port mapping, any time that port is turned ON,
01857     // we'll simulate pushing the Launch Ball button if the player pulls 
01858     // back and releases the plunger, or simply pushes on the plunger from
01859     // the rest position.  This allows the plunger to be used in lieu of a
01860     // physical Launch Ball button for tables that don't have plungers.
01861     //
01862     // States:
01863     //   0 = default
01864     //   1 = cocked (plunger has been pulled back about 1" from state 0)
01865     //   2 = uncocked (plunger is pulled back less than 1" from state 1)
01866     //   3 = launching, plunger is forward beyond park position
01867     //   4 = launching, plunger is behind park position
01868     //   5 = pressed and holding (plunger has been pressed forward beyond 
01869     //       the park position from state 0)
01870     int lbState = 0;
01871     
01872     // Time since last lbState transition.  Some of the states are time-
01873     // sensitive.  In the "uncocked" state, we'll return to state 0 if
01874     // we remain in this state for more than a few milliseconds, since
01875     // it indicates that the plunger is being slowly returned to rest
01876     // rather than released.  In the "launching" state, we need to release 
01877     // the Launch Ball button after a moment, and we need to wait for 
01878     // the plunger to come to rest before returning to state 0.
01879     Timer lbTimer;
01880     lbTimer.start();
01881     
01882     // Launch Ball simulated push timer.  We start this when we simulate
01883     // the button push, and turn off the simulated button when enough time
01884     // has elapsed.
01885     Timer lbBtnTimer;
01886     
01887     // Simulated button states.  This is a vector of button states
01888     // for the simulated buttons.  We combine this with the physical
01889     // button states on each USB joystick report, so we will report
01890     // a button as pressed if either the physical button is being pressed
01891     // or we're simulating a press on the button.  This is used for the
01892     // simulated Launch Ball button.
01893     uint32_t simButtons = 0;
01894     
01895     // Firing in progress: we set this when we detect the start of rapid 
01896     // plunger movement from a retracted position towards the rest position.
01897     //
01898     // When we detect a firing event, we send VP a series of synthetic
01899     // reports simulating the idealized plunger motion.  The actual physical
01900     // motion is much too fast to report to VP; in the time between two USB
01901     // reports, the plunger can shoot all the way forward, rebound off of
01902     // the barrel spring, bounce back part way, and bounce forward again,
01903     // or even do all of this more than once.  This means that sampling the 
01904     // physical motion at the USB report rate would create a misleading 
01905     // picture of the plunger motion, since our samples would catch the 
01906     // plunger at random points in this oscillating motion.  From the 
01907     // user's perspective, the physical action that occurred is simply that 
01908     // the plunger was released from a particular distance, so it's this 
01909     // high-level event that we want to convey to VP.  To do this, we
01910     // synthesize a series of reports to convey an idealized version of
01911     // the release motion that's perfectly synchronized to the VP reports.  
01912     // Essentially we pretend that our USB position samples are exactly 
01913     // aligned in time with (1) the point of retraction just before the 
01914     // user released the plunger, (2) the point of maximum forward motion 
01915     // just after the user released the plunger (the point of maximum 
01916     // compression as the plunger bounces off of the barrel spring), and 
01917     // (3) the plunger coming to rest at the park position.  This series
01918     // of reports is synthetic in the sense that it's not what we actually
01919     // see on the CCD at the times of these reports - the true plunger
01920     // position is oscillating at high speed during this period.  But at
01921     // the same time it conveys a more faithful picture of the true physical
01922     // motion to VP, and allows VP to reproduce the true physical motion 
01923     // more faithfully in its simulation model, by correcting for the
01924     // relatively low sampling rate in the communication path between the
01925     // real plunger and VP's model plunger.
01926     //
01927     // If 'firing' is non-zero, it's the index of our current report in
01928     // the synthetic firing report series.
01929     int firing = 0;
01930 
01931     // start the first CCD integration cycle
01932     plungerSensor.init();
01933     
01934     // Device status.  We report this on each update so that the host config
01935     // tool can detect our current settings.  This is a bit mask consisting
01936     // of these bits:
01937     //    0x0001  -> plunger sensor enabled
01938     //    0x8000  -> RESERVED - must always be zero
01939     //
01940     // Note that the high bit (0x8000) must always be 0, since we use that
01941     // to distinguish special request reply packets.
01942     uint16_t statusFlags = (cfg.d.plungerEnabled ? 0x01 : 0x00);
01943     
01944     // we're all set up - now just loop, processing sensor reports and 
01945     // host requests
01946     for (;;)
01947     {
01948         // Look for an incoming report.  Process a few input reports in
01949         // a row, but stop after a few so that a barrage of inputs won't
01950         // starve our output event processing.  Also, pause briefly between
01951         // reads; allowing reads to occur back-to-back seems to occasionally 
01952         // stall the USB pipeline (for reasons unknown; I'd fix the underlying 
01953         // problem if I knew what it was).
01954         HID_REPORT report;
01955         for (int rr = 0 ; rr < 4 && js.readNB(&report) ; ++rr, wait_ms(1))
01956         {
01957             // all Led-Wiz reports are 8 bytes exactly
01958             if (report.length == 8)
01959             {
01960                 // LedWiz commands come in two varieties:  SBA and PBA.  An
01961                 // SBA is marked by the first byte having value 64 (0x40).  In
01962                 // the real LedWiz protocol, any other value in the first byte
01963                 // means it's a PBA message.  However, *valid* PBA messages
01964                 // always have a first byte (and in fact all 8 bytes) in the
01965                 // range 0-49 or 129-132.  Anything else is invalid.  We take
01966                 // advantage of this to implement private protocol extensions.
01967                 // So our full protocol is as follows:
01968                 //
01969                 // first byte =
01970                 //   0-48     -> LWZ-PBA
01971                 //   64       -> LWZ SBA 
01972                 //   65       -> private control message; second byte specifies subtype
01973                 //   129-132  -> LWZ-PBA
01974                 //   200-219  -> extended bank brightness set for outputs N to N+6, where
01975                 //               N is (first byte - 200)*7
01976                 //   other    -> reserved for future use
01977                 //
01978                 uint8_t *data = report.data;
01979                 if (data[0] == 64) 
01980                 {
01981                     // LWZ-SBA - first four bytes are bit-packed on/off flags
01982                     // for the outputs; 5th byte is the pulse speed (1-7)
01983                     //printf("LWZ-SBA %02x %02x %02x %02x ; %02x\r\n",
01984                     //       data[1], data[2], data[3], data[4], data[5]);
01985     
01986                     // update all on/off states
01987                     for (int i = 0, bit = 1, ri = 1 ; i < 32 ; ++i, bit <<= 1)
01988                     {
01989                         // figure the on/off state bit for this output
01990                         if (bit == 0x100) {
01991                             bit = 1;
01992                             ++ri;
01993                         }
01994                         
01995                         // set the on/off state
01996                         wizOn[i] = ((data[ri] & bit) != 0);
01997                         
01998                         // If the wizVal setting is 255, it means that this
01999                         // output was last set to a brightness value with the
02000                         // extended protocol.  Return it to LedWiz control by
02001                         // rescaling the brightness setting to the LedWiz range
02002                         // and updating wizVal with the result.  If it's any
02003                         // other value, it was previously set by a PBA message,
02004                         // so simply retain the last setting - in the normal
02005                         // LedWiz protocol, the "profile" (brightness) and on/off
02006                         // states are independent, so an SBA just turns an output
02007                         // on or off but retains its last brightness level.
02008                         if (wizVal[i] == 255)
02009                             wizVal[i] = (uint8_t)round(outLevel[i]*48);
02010                     }
02011                     
02012                     // set the flash speed - enforce the value range 1-7
02013                     wizSpeed = data[5];
02014                     if (wizSpeed < 1)
02015                         wizSpeed = 1;
02016                     else if (wizSpeed > 7)
02017                         wizSpeed = 7;
02018         
02019                     // update the physical outputs
02020                     updateWizOuts();
02021                     hc595.update();
02022                     
02023                     // reset the PBA counter
02024                     pbaIdx = 0;
02025                 }
02026                 else if (data[0] == 65)
02027                 {
02028                     // Private control message.  This isn't an LedWiz message - it's
02029                     // an extension for this device.  65 is an invalid PBA setting,
02030                     // and isn't used for any other LedWiz message, so we appropriate
02031                     // it for our own private use.  The first byte specifies the 
02032                     // message type.
02033                     if (data[1] == 1)
02034                     {
02035                         // 1 = Set Configuration:
02036                         //     data[2] = LedWiz unit number (0x00 to 0x0f)
02037                         //     data[3] = feature enable bit mask:
02038                         //               0x01 = enable plunger sensor
02039                         
02040                         // we'll need a reset if the LedWiz unit number is changing
02041                         uint8_t newUnitNo = data[2] & 0x0f;
02042                         needReset |= (newUnitNo != cfg.d.ledWizUnitNo);
02043                         
02044                         // set the configuration parameters from the message
02045                         cfg.d.ledWizUnitNo = newUnitNo;
02046                         cfg.d.plungerEnabled = data[3] & 0x01;
02047                         
02048                         // update the status flags
02049                         statusFlags = (statusFlags & ~0x01) | (data[3] & 0x01);
02050                         
02051                         // if the ccd is no longer enabled, use 0 for z reports
02052                         if (!cfg.d.plungerEnabled)
02053                             z = 0;
02054                         
02055                         // save the configuration
02056                         cfg.save(iap, flash_addr);
02057                     }
02058 #ifdef ENABLE_JOYSTICK
02059                     else if (data[1] == 2)
02060                     {
02061                         // 2 = Calibrate plunger
02062                         // (No parameters)
02063                         
02064                         // enter calibration mode
02065                         calBtnState = 3;
02066                         calBtnTimer.reset();
02067                         cfg.resetPlunger();
02068                     }
02069                     else if (data[1] == 3)
02070                     {
02071                         // 3 = pixel dump
02072                         // (No parameters)
02073                         reportPix = true;
02074                         
02075                         // show purple until we finish sending the report
02076                         ledR = 0;
02077                         ledB = 0;
02078                         ledG = 1;
02079                     }
02080                     else if (data[1] == 4)
02081                     {
02082                         // 4 = hardware configuration query
02083                         // (No parameters)
02084                         wait_ms(1);
02085                         js.reportConfig(numOutputs, cfg.d.ledWizUnitNo);
02086                     }
02087                     else if (data[1] == 5)
02088                     {
02089                         // 5 = all outputs off, reset to LedWiz defaults
02090                         allOutputsOff();
02091                     }
02092 #endif // ENABLE_JOYSTICK
02093                 }
02094                 else if (data[0] >= 200 && data[0] < 220)
02095                 {
02096                     // Extended protocol - banked brightness update.  
02097                     // data[0]-200 gives us the bank of 7 outputs we're setting:
02098                     // 200 is outputs 0-6, 201 is outputs 7-13, 202 is 14-20, etc.
02099                     // The remaining bytes are brightness levels, 0-255, for the
02100                     // seven outputs in the selected bank.  The LedWiz flashing 
02101                     // modes aren't accessible in this message type; we can only 
02102                     // set a fixed brightness, but in exchange we get 8-bit 
02103                     // resolution rather than the paltry 0-48 scale that the real
02104                     // LedWiz uses.  There's no separate on/off status for outputs
02105                     // adjusted with this message type, either, as there would be
02106                     // for a PBA message - setting a non-zero value immediately
02107                     // turns the output, overriding the last SBA setting.
02108                     //
02109                     // For outputs 0-31, this overrides any previous PBA/SBA
02110                     // settings for the port.  Any subsequent PBA/SBA message will
02111                     // in turn override the setting made here.  It's simple - the
02112                     // most recent message of either type takes precedence.  For
02113                     // outputs above the LedWiz range, PBA/SBA messages can't
02114                     // address those ports anyway.
02115                     int i0 = (data[0] - 200)*7;
02116                     int i1 = i0 + 7 < numOutputs ? i0 + 7 : numOutputs; 
02117                     for (int i = i0 ; i < i1 ; ++i)
02118                     {
02119                         // set the brightness level for the output
02120                         float b = data[i-i0+1]/255.0;
02121                         outLevel[i] = b;
02122                         
02123                         // if it's in the basic LedWiz output set, set the LedWiz
02124                         // profile value to 255, which means "use outLevel"
02125                         if (i < 32) 
02126                             wizVal[i] = 255;
02127                             
02128                         // set the output
02129                         lwPin[i]->set(b);
02130                     }
02131                     
02132                     // update 74HC595 outputs, if attached
02133                     hc595.update();
02134                 }
02135                 else 
02136                 {
02137                     // Everything else is LWZ-PBA.  This is a full "profile"
02138                     // dump from the host for one bank of 8 outputs.  Each
02139                     // byte sets one output in the current bank.  The current
02140                     // bank is implied; the bank starts at 0 and is reset to 0
02141                     // by any LWZ-SBA message, and is incremented to the next
02142                     // bank by each LWZ-PBA message.  Our variable pbaIdx keeps
02143                     // track of our notion of the current bank.  There's no direct
02144                     // way for the host to select the bank; it just has to count
02145                     // on us staying in sync.  In practice, the host will always
02146                     // send a full set of 4 PBA messages in a row to set all 32
02147                     // outputs.
02148                     //
02149                     // Note that a PBA implicitly overrides our extended profile
02150                     // messages (message prefix 200-219), because this sets the
02151                     // wizVal[] entry for each output, and that takes precedence
02152                     // over the extended protocol settings.
02153                     //
02154                     //printf("LWZ-PBA[%d] %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
02155                     //       pbaIdx, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
02156     
02157                     // Update all output profile settings
02158                     for (int i = 0 ; i < 8 ; ++i)
02159                         wizVal[pbaIdx + i] = data[i];
02160     
02161                     // Update the physical LED state if this is the last bank.
02162                     // Note that hosts always send a full set of four PBA
02163                     // messages, so there's no need to do a physical update
02164                     // until we've received the last bank's PBA message.
02165                     if (pbaIdx == 24)
02166                     {
02167                         updateWizOuts();
02168                         hc595.update();
02169                         pbaIdx = 0;
02170                     }
02171                     else
02172                         pbaIdx += 8;
02173                 }
02174             }
02175         }
02176        
02177         // check for plunger calibration
02178         if (calBtn != 0 && !calBtn->read())
02179         {
02180             // check the state
02181             switch (calBtnState)
02182             {
02183             case 0: 
02184                 // button not yet pushed - start debouncing
02185                 calBtnTimer.reset();
02186                 calBtnState = 1;
02187                 break;
02188                 
02189             case 1:
02190                 // pushed, not yet debounced - if the debounce time has
02191                 // passed, start the hold period
02192                 if (calBtnTimer.read_ms() > 50)
02193                     calBtnState = 2;
02194                 break;
02195                 
02196             case 2:
02197                 // in the hold period - if the button has been held down
02198                 // for the entire hold period, move to calibration mode
02199                 if (calBtnTimer.read_ms() > 2050)
02200                 {
02201                     // enter calibration mode
02202                     calBtnState = 3;
02203                     calBtnTimer.reset();
02204                     cfg.resetPlunger();
02205                 }
02206                 break;
02207                 
02208             case 3:
02209                 // Already in calibration mode - pushing the button here
02210                 // doesn't change the current state, but we won't leave this
02211                 // state as long as it's held down.  So nothing changes here.
02212                 break;
02213             }
02214         }
02215         else
02216         {
02217             // Button released.  If we're in calibration mode, and
02218             // the calibration time has elapsed, end the calibration
02219             // and save the results to flash.
02220             //
02221             // Otherwise, return to the base state without saving anything.
02222             // If the button is released before we make it to calibration
02223             // mode, it simply cancels the attempt.
02224             if (calBtnState == 3 && calBtnTimer.read_ms() > 15000)
02225             {
02226                 // exit calibration mode
02227                 calBtnState = 0;
02228                 
02229                 // save the updated configuration
02230                 cfg.d.plungerCal = 1;
02231                 cfg.save(iap, flash_addr);
02232             }
02233             else if (calBtnState != 3)
02234             {
02235                 // didn't make it to calibration mode - cancel the operation
02236                 calBtnState = 0;
02237             }
02238         }       
02239         
02240         // light/flash the calibration button light, if applicable
02241         int newCalBtnLit = calBtnLit;
02242         switch (calBtnState)
02243         {
02244         case 2:
02245             // in the hold period - flash the light
02246             newCalBtnLit = ((calBtnTimer.read_ms()/250) & 1);
02247             break;
02248             
02249         case 3:
02250             // calibration mode - show steady on
02251             newCalBtnLit = true;
02252             break;
02253             
02254         default:
02255             // not calibrating/holding - show steady off
02256             newCalBtnLit = false;
02257             break;
02258         }
02259         
02260         // light or flash the external calibration button LED, and 
02261         // do the same with the on-board blue LED
02262         if (calBtnLit != newCalBtnLit)
02263         {
02264             calBtnLit = newCalBtnLit;
02265             if (calBtnLit) {
02266                 if (calBtnLed != 0)
02267                     calBtnLed->write(1);
02268                 ledR = 1;
02269                 ledG = 1;
02270                 ledB = 0;
02271             }
02272             else {
02273                 if (calBtnLed != 0)
02274                     calBtnLed->write(0);
02275                 ledR = 1;
02276                 ledG = 1;
02277                 ledB = 1;
02278             }
02279         }
02280         
02281         // If the plunger is enabled, and we're not already in a firing event,
02282         // and the last plunger reading had the plunger pulled back at least
02283         // a bit, watch for plunger release events until it's time for our next
02284         // USB report.
02285         if (!firing && cfg.d.plungerEnabled && z >= JOYMAX/6)
02286         {
02287             // monitor the plunger until it's time for our next report
02288             while (reportTimer.read_ms() < 15)
02289             {
02290                 // do a fast low-res scan; if it's at or past the zero point,
02291                 // start a firing event
02292                 if (plungerSensor.lowResScan() <= cfg.d.plungerZero)
02293                     firing = 1;
02294             }
02295         }
02296 
02297         // read the plunger sensor, if it's enabled
02298         if (cfg.d.plungerEnabled)
02299         {
02300             // start with the previous reading, in case we don't have a
02301             // clear result on this frame
02302             int znew = z;
02303             if (plungerSensor.highResScan(pos))
02304             {
02305                 // We got a new reading.  If we're in calibration mode, use it
02306                 // to figure the new calibration, otherwise adjust the new reading
02307                 // for the established calibration.
02308                 if (calBtnState == 3)
02309                 {
02310                     // Calibration mode.  If this reading is outside of the current
02311                     // calibration bounds, expand the bounds.
02312                     if (pos < cfg.d.plungerMin)
02313                         cfg.d.plungerMin = pos;
02314                     if (pos < cfg.d.plungerZero)
02315                         cfg.d.plungerZero = pos;
02316                     if (pos > cfg.d.plungerMax)
02317                         cfg.d.plungerMax = pos;
02318                         
02319                     // normalize to the full physical range while calibrating
02320                     znew = int(round(float(pos)/npix * JOYMAX));
02321                 }
02322                 else
02323                 {
02324                     // Not in calibration mode, so normalize the new reading to the 
02325                     // established calibration range.  
02326                     //
02327                     // Note that negative values are allowed.  Zero represents the
02328                     // "park" position, where the plunger sits when at rest.  A mechanical 
02329                     // plunger has a small amount of travel in the "push" direction,
02330                     // since the barrel spring can be compressed slightly.  Negative
02331                     // values represent travel in the push direction.
02332                     if (pos > cfg.d.plungerMax)
02333                         pos = cfg.d.plungerMax;
02334                     znew = int(round(float(pos - cfg.d.plungerZero)
02335                         / (cfg.d.plungerMax - cfg.d.plungerZero + 1) * JOYMAX));
02336                 }
02337             }
02338 
02339             // If we're not already in a firing event, check to see if the
02340             // new position is forward of the last report.  If it is, a firing
02341             // event might have started during the high-res scan.  This might
02342             // seem unlikely given that the scan only takes about 5ms, but that
02343             // 5ms represents about 25-30% of our total time between reports,
02344             // there's about a 1 in 4 chance that a release starts during a
02345             // scan.  
02346             if (!firing && z0 > 0 && znew < z0)
02347             {
02348                 // The plunger has moved forward since the previous report.
02349                 // Watch it for a few more ms to see if we can get a stable
02350                 // new position.
02351                 int pos0 = plungerSensor.lowResScan();
02352                 int pos1 = pos0;
02353                 Timer tw;
02354                 tw.start();
02355                 while (tw.read_ms() < 6)
02356                 {
02357                     // read the new position
02358                     int pos2 = plungerSensor.lowResScan();
02359                     
02360                     // If it's stable over consecutive readings, stop looping.
02361                     // (Count it as stable if the position is within about 1/8".
02362                     // pos1 and pos2 are reported in pixels, so they range from
02363                     // 0 to npix.  The overall travel of a standard plunger is
02364                     // about 3.2", so we have (npix/3.2) pixels per inch, hence
02365                     // 1/8" is (npix/3.2)*(1/8) pixels.)
02366                     if (abs(pos2 - pos1) < int(npix/(3.2*8)))
02367                         break;
02368 
02369                     // If we've crossed the rest position, and we've moved by
02370                     // a minimum distance from where we starting this loop, begin
02371                     // a firing event.  (We require a minimum distance to prevent
02372                     // spurious firing from random analog noise in the readings
02373                     // when the plunger is actually just sitting still at the 
02374                     // rest position.  If it's at rest, it's normal to see small
02375                     // random fluctuations in the analog reading +/- 1% or so
02376                     // from the 0 point, especially with a sensor like a
02377                     // potentionemeter that reports the position as a single 
02378                     // analog voltage.)  Note that we compare the latest reading
02379                     // to the first reading of the loop - we don't require the
02380                     // threshold motion over consecutive readings, but any time
02381                     // over the stability wait loop.
02382                     if (pos1 < cfg.d.plungerZero
02383                         && abs(pos2 - pos0) > int(npix/(3.2*8)))
02384                     {
02385                         firing = 1;
02386                         break;
02387                     }
02388                                             
02389                     // the new reading is now the prior reading
02390                     pos1 = pos2;
02391                 }
02392             }
02393             
02394             // Check for a simulated Launch Ball button press, if enabled
02395             if (ZBLaunchBallPort != 0)
02396             {
02397                 const int cockThreshold = JOYMAX/3;
02398                 const int pushThreshold = int(-JOYMAX/3 * LaunchBallPushDistance);
02399                 int newState = lbState;
02400                 switch (lbState)
02401                 {
02402                 case 0:
02403                     // Base state.  If the plunger is pulled back by an inch
02404                     // or more, go to "cocked" state.  If the plunger is pushed
02405                     // forward by 1/4" or more, go to "pressed" state.
02406                     if (znew >= cockThreshold)
02407                         newState = 1;
02408                     else if (znew <= pushThreshold)
02409                         newState = 5;
02410                     break;
02411                     
02412                 case 1:
02413                     // Cocked state.  If a firing event is now in progress,
02414                     // go to "launch" state.  Otherwise, if the plunger is less
02415                     // than 1" retracted, go to "uncocked" state - the player
02416                     // might be slowly returning the plunger to rest so as not
02417                     // to trigger a launch.
02418                     if (firing || znew <= 0)
02419                         newState = 3;
02420                     else if (znew < cockThreshold)
02421                         newState = 2;
02422                     break;
02423                     
02424                 case 2:
02425                     // Uncocked state.  If the plunger is more than an inch
02426                     // retracted, return to cocked state.  If we've been in
02427                     // the uncocked state for more than half a second, return
02428                     // to the base state.  This allows the user to return the
02429                     // plunger to rest without triggering a launch, by moving
02430                     // it at manual speed to the rest position rather than
02431                     // releasing it.
02432                     if (znew >= cockThreshold)
02433                         newState = 1;
02434                     else if (lbTimer.read_ms() > 500)
02435                         newState = 0;
02436                     break;
02437                     
02438                 case 3:
02439                     // Launch state.  If the plunger is no longer pushed
02440                     // forward, switch to launch rest state.
02441                     if (znew >= 0)
02442                         newState = 4;
02443                     break;    
02444                     
02445                 case 4:
02446                     // Launch rest state.  If the plunger is pushed forward
02447                     // again, switch back to launch state.  If not, and we've
02448                     // been in this state for at least 200ms, return to the
02449                     // default state.
02450                     if (znew <= pushThreshold)
02451                         newState = 3;
02452                     else if (lbTimer.read_ms() > 200)
02453                         newState = 0;                    
02454                     break;
02455                     
02456                 case 5:
02457                     // Press-and-Hold state.  If the plunger is no longer pushed
02458                     // forward, AND it's been at least 50ms since we generated
02459                     // the simulated Launch Ball button press, return to the base 
02460                     // state.  The minimum time is to ensure that VP has a chance
02461                     // to see the button press and to avoid transient key bounce
02462                     // effects when the plunger position is right on the threshold.
02463                     if (znew > pushThreshold && lbTimer.read_ms() > 50)
02464                         newState = 0;
02465                     break;
02466                 }
02467                 
02468                 // change states if desired
02469                 const uint32_t lbButtonBit = (1 << (LaunchBallButton - 1));
02470                 if (newState != lbState)
02471                 {
02472                     // If we're entering Launch state OR we're entering the
02473                     // Press-and-Hold state, AND the ZB Launch Ball LedWiz signal 
02474                     // is turned on, simulate a Launch Ball button press.
02475                     if (((newState == 3 && lbState != 4) || newState == 5)
02476                         && wizOn[ZBLaunchBallPort-1])
02477                     {
02478                         lbBtnTimer.reset();
02479                         lbBtnTimer.start();
02480                         simButtons |= lbButtonBit;
02481                     }
02482                     
02483                     // if we're switching to state 0, release the button
02484                     if (newState == 0)
02485                         simButtons &= ~(1 << (LaunchBallButton - 1));
02486                     
02487                     // switch to the new state
02488                     lbState = newState;
02489                     
02490                     // start timing in the new state
02491                     lbTimer.reset();
02492                 }
02493                 
02494                 // If the Launch Ball button press is in effect, but the
02495                 // ZB Launch Ball LedWiz signal is no longer turned on, turn
02496                 // off the button.
02497                 //
02498                 // If we're in one of the Launch states (state #3 or #4),
02499                 // and the button has been on for long enough, turn it off.
02500                 // The Launch mode is triggered by a pull-and-release gesture.
02501                 // From the user's perspective, this is just a single gesture
02502                 // that should trigger just one momentary press on the Launch
02503                 // Ball button.  Physically, though, the plunger usually
02504                 // bounces back and forth for 500ms or so before coming to
02505                 // rest after this gesture.  That's what the whole state
02506                 // #3-#4 business is all about - we stay in this pair of
02507                 // states until the plunger comes to rest.  As long as we're
02508                 // in these states, we won't send duplicate button presses.
02509                 // But we also don't want the one button press to continue 
02510                 // the whole time, so we'll time it out now.
02511                 //
02512                 // (This could be written as one big 'if' condition, but
02513                 // I'm breaking it out verbosely like this to make it easier
02514                 // for human readers such as myself to comprehend the logic.)
02515                 if ((simButtons & lbButtonBit) != 0)
02516                 {
02517                     int turnOff = false;
02518                     
02519                     // turn it off if the ZB Launch Ball signal is off
02520                     if (!wizOn[ZBLaunchBallPort-1])
02521                         turnOff = true;
02522                         
02523                     // also turn it off if we're in state 3 or 4 ("Launch"),
02524                     // and the button has been on long enough
02525                     if ((lbState == 3 || lbState == 4) && lbBtnTimer.read_ms() > 250)
02526                         turnOff = true;
02527                         
02528                     // if we decided to turn off the button, do so
02529                     if (turnOff)
02530                     {
02531                         lbBtnTimer.stop();
02532                         simButtons &= ~lbButtonBit;
02533                     }
02534                 }
02535             }
02536                 
02537             // If a firing event is in progress, generate synthetic reports to 
02538             // describe an idealized version of the plunger motion to VP rather 
02539             // than reporting the actual physical plunger position.
02540             //
02541             // We use the synthetic reports during a release event because the
02542             // physical plunger motion when released is too fast for VP to track.
02543             // VP only syncs its internal physics model with the outside world 
02544             // about every 10ms.  In that amount of time, the plunger moves
02545             // fast enough when released that it can shoot all the way forward,
02546             // bounce off of the barrel spring, and rebound part of the way
02547             // back.  The result is the classic analog-to-digital problem of
02548             // sample aliasing.  If we happen to time our sample during the
02549             // release motion so that we catch the plunger at the peak of a
02550             // bounce, the digital signal incorrectly looks like the plunger
02551             // is moving slowly forward - VP thinks we went from fully
02552             // retracted to half retracted in the sample interval, whereas
02553             // we actually traveled all the way forward and half way back,
02554             // so the speed VP infers is about 1/3 of the actual speed.
02555             //
02556             // To correct this, we take advantage of our ability to sample 
02557             // the CCD image several times in the course of a VP report.  If
02558             // we catch the plunger near the origin after we've seen it
02559             // retracted, we go into Release Event mode.  During this mode,
02560             // we stop reporting the true physical plunger position, and
02561             // instead report an idealized pattern: we report the plunger
02562             // immediately shooting forward to a position in front of the
02563             // park position that's in proportion to how far back the plunger
02564             // was just before the release, and we then report it stationary
02565             // at the park position.  We continue to report the stationary
02566             // park position until the actual physical plunger motion has
02567             // stabilized on a new position.  We then exit Release Event
02568             // mode and return to reporting the true physical position.
02569             if (firing)
02570             {
02571                 // Firing in progress.  Keep reporting the park position
02572                 // until the physical plunger position comes to rest.
02573                 const int restTol = JOYMAX/24;
02574                 if (firing == 1)
02575                 {
02576                     // For the first couple of frames, show the plunger shooting
02577                     // forward past the zero point, to simulate the momentum carrying
02578                     // it forward to bounce off of the barrel spring.  Show the 
02579                     // bounce as proportional to the distance it was retracted
02580                     // in the prior report.
02581                     z = zBounce = -z0/6;
02582                     ++firing;
02583                 }
02584                 else if (firing == 2)
02585                 {
02586                     // second frame - keep the bounce a little longer
02587                     z = zBounce;
02588                     ++firing;
02589                 }
02590                 else if (firing > 4
02591                     && abs(znew - z0) < restTol
02592                     && abs(znew - z1) < restTol 
02593                     && abs(znew - z2) < restTol)
02594                 {
02595                     // The physical plunger has come to rest.  Exit firing
02596                     // mode and resume reporting the actual position.
02597                     firing = false;
02598                     z = znew;
02599                 }
02600                 else
02601                 {
02602                     // until the physical plunger comes to rest, simply 
02603                     // report the park position
02604                     z = 0;
02605                     ++firing;
02606                 }
02607             }
02608             else
02609             {
02610                 // not in firing mode - report the true physical position
02611                 z = znew;
02612             }
02613 
02614             // shift the new reading into the recent history buffer
02615             z2 = z1;
02616             z1 = z0;
02617             z0 = znew;
02618         }
02619 
02620         // update the buttons
02621         uint32_t buttons = readButtons();
02622 
02623 #ifdef ENABLE_JOYSTICK
02624         // If it's been long enough since our last USB status report,
02625         // send the new report.  We throttle the report rate because
02626         // it can overwhelm the PC side if we report too frequently.
02627         // VP only wants to sync with the real world in 10ms intervals,
02628         // so reporting more frequently only creates i/o overhead
02629         // without doing anything to improve the simulation.
02630         if (reportTimer.read_ms() > 15)
02631         {
02632             // read the accelerometer
02633             int xa, ya;
02634             accel.get(xa, ya);
02635             
02636             // confine the results to our joystick axis range
02637             if (xa < -JOYMAX) xa = -JOYMAX;
02638             if (xa > JOYMAX) xa = JOYMAX;
02639             if (ya < -JOYMAX) ya = -JOYMAX;
02640             if (ya > JOYMAX) ya = JOYMAX;
02641             
02642             // store the updated accelerometer coordinates
02643             x = xa;
02644             y = ya;
02645             
02646             // Report the current plunger position UNLESS the ZB Launch Ball 
02647             // signal is on, in which case just report a constant 0 value.  
02648             // ZB Launch Ball turns off the plunger position because it
02649             // tells us that the table has a Launch Ball button instead of
02650             // a traditional plunger.
02651             int zrep = (ZBLaunchBallPort != 0 && wizOn[ZBLaunchBallPort-1] ? 0 : z);
02652             
02653             // Send the status report.  Note that we have to map the X and Y
02654             // axes from the accelerometer to match the Windows joystick axes.
02655             // The mapping is determined according to the mounting direction
02656             // set in config.h via the ORIENTATION_xxx macros.
02657             js.update(JOY_X(x,y), JOY_Y(x,y), zrep, buttons | simButtons, statusFlags);
02658             
02659             // we've just started a new report interval, so reset the timer
02660             reportTimer.reset();
02661         }
02662 
02663         // If we're in pixel dump mode, report all pixel exposure values
02664         if (reportPix)
02665         {
02666             // send the report            
02667             plungerSensor.sendExposureReport(js);
02668 
02669             // we have satisfied this request
02670             reportPix = false;
02671         }
02672         
02673 #else // ENABLE_JOYSTICK
02674         // We're a secondary controller, with no joystick reporting.  Send
02675         // a generic status report to the host periodically for the sake of
02676         // the Windows config tool.
02677         if (reportTimer.read_ms() > 200)
02678         {
02679             js.updateStatus(0);
02680         }
02681 
02682 #endif // ENABLE_JOYSTICK
02683         
02684 #ifdef DEBUG_PRINTF
02685         if (x != 0 || y != 0)
02686             printf("%d,%d\r\n", x, y);
02687 #endif
02688 
02689         // check for connection status changes
02690         int newConnected = js.isConnected() && !js.isSuspended();
02691         if (newConnected != connected)
02692         {
02693             // give it a few seconds to stabilize
02694             time_t tc = time(0);
02695             if (tc - connectChangeTime > 3)
02696             {
02697                 // note the new status
02698                 connected = newConnected;
02699                 connectChangeTime = tc;
02700                 
02701                 // if we're no longer connected, turn off all outputs
02702                 if (!connected)
02703                     allOutputsOff();
02704             }
02705         }
02706 
02707         // provide a visual status indication on the on-board LED
02708         if (calBtnState < 2 && hbTimer.read_ms() > 1000) 
02709         {
02710             if (!newConnected)
02711             {
02712                 // suspended - turn off the LED
02713                 ledR = 1;
02714                 ledG = 1;
02715                 ledB = 1;
02716 
02717                 // show a status flash every so often                
02718                 if (hbcnt % 3 == 0)
02719                 {
02720                     // disconnected = red/red flash; suspended = red
02721                     for (int n = js.isConnected() ? 1 : 2 ; n > 0 ; --n)
02722                     {
02723                         ledR = 0;
02724                         wait(0.05);
02725                         ledR = 1;
02726                         wait(0.25);
02727                     }
02728                 }
02729             }
02730             else if (needReset)
02731             {
02732                 // connected, need to reset due to changes in config parameters -
02733                 // flash red/green
02734                 hb = !hb;
02735                 ledR = (hb ? 0 : 1);
02736                 ledG = (hb ? 1 : 0);
02737                 ledB = 0;
02738             }
02739             else if (cfg.d.plungerEnabled && !cfg.d.plungerCal)
02740             {
02741                 // connected, plunger calibration needed - flash yellow/green
02742                 hb = !hb;
02743                 ledR = (hb ? 0 : 1);
02744                 ledG = 0;
02745                 ledB = 1;
02746             }
02747             else
02748             {
02749                 // connected - flash blue/green
02750                 hb = !hb;
02751                 ledR = 1;
02752                 ledG = (hb ? 0 : 1);
02753                 ledB = (hb ? 1 : 0);
02754             }
02755             
02756             // reset the heartbeat timer
02757             hbTimer.reset();
02758             ++hbcnt;
02759         }
02760     }
02761 }