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

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* Copyright 2014, 2016 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 // The Pinscape Controller
00021 // A comprehensive input/output controller for virtual pinball machines
00022 //
00023 // This project implements an I/O controller for virtual pinball cabinets.  The
00024 // controller's function is to connect Visual Pinball (and other Windows pinball 
00025 // emulators) with physical devices in the cabinet:  buttons, sensors, and 
00026 // feedback devices that create visual or mechanical effects during play.  
00027 //
00028 // The controller can perform several different functions, which can be used 
00029 // individually or in any combination:
00030 //
00031 //  - Nudge sensing.  This uses the KL25Z's on-board accelerometer to sense the
00032 //    motion of the cabinet when you nudge it.  Visual Pinball and other pinball 
00033 //    emulators on the PC have native handling for this type of input, so that 
00034 //    physical nudges on the cabinet turn into simulated effects on the virtual 
00035 //    ball.  The KL25Z measures accelerations as analog readings and is quite 
00036 //    sensitive, so the effect of a nudge on the simulation is proportional
00037 //    to the strength of the nudge.  Accelerations are reported to the PC via a 
00038 //    simulated joystick (using the X and Y axes); you just have to set some 
00039 //    preferences in your  pinball software to tell it that an accelerometer 
00040 //    is attached.
00041 //
00042 //  - Plunger position sensing, with multiple sensor options.  To use this feature,
00043 //    you need to choose a sensor and set it up, connect the sensor electrically to 
00044 //    the KL25Z, and configure the Pinscape software on the KL25Z to let it know how 
00045 //    the sensor is hooked up.  The Pinscape software monitors the sensor and sends
00046 //    readings to Visual Pinball via the joystick Z axis.  VP and other PC software
00047 //    have native support for this type of input; as with the nudge setup, you just 
00048 //    have to set some options in VP to activate the plunger.
00049 //
00050 //    We support several sensor types:
00051 //
00052 //    - AEDR-8300-1K2 optical encoders.  These are quadrature encoders with
00053 //      reflective optical sensing and built-in lighting and optics.  The sensor
00054 //      is attached to the plunger so that it moves with the plunger, and slides
00055 //      along a guide rail with a reflective pattern of regularly spaces bars 
00056 //      for the encoder to read.  We read the plunger position by counting the
00057 //      bars the sensor passes as it moves across the rail.  This is the newest
00058 //      option, and it's my current favorite because it's highly accurate,
00059 //      precise, and fast, plus it's relatively inexpensive.
00060 //
00061 //    - Slide potentiometers.  There are slide potentioneters available with a
00062 //      long enough travel distance (at least 85mm) to cover the plunger travel.
00063 //      Attach the plunger to the potentiometer knob so that the moving the
00064 //      plunger moves the pot knob.  We sense the position by simply reading
00065 //      the analog voltage on the pot brush.  A pot with a "linear taper" (that
00066 //      is, the resistance varies linearly with the position) is required.
00067 //      This option is cheap, easy to set up, and works well.
00068 //
00069 //    - VL6108X time-of-flight distance sensor.  This is an optical distance
00070 //      sensor that measures the distance to a nearby object (within about 10cm)
00071 //      by measuring the travel time for reflected pulses of light.  It's fairly
00072 //      cheap and easy to set up, but I don't recommend it because it has very
00073 //      low precision.
00074 //
00075 //    - TSL1410R/TSL1412R linear array optical sensors.  These are large optical
00076 //      sensors with the pixels arranged in a single row.  The pixel arrays are
00077 //      large enough on these to cover the travel distance of the plunger, so we
00078 //      can set up the sensor near the plunger in such a way that the plunger 
00079 //      casts a shadow on the sensor.  We detect the plunger position by finding
00080 //      the edge of the sahdow in the image.  The optics for this setup are very
00081 //      simple since we don't need any lenses.  This was the first sensor we
00082 //      supported, and works very well, but unfortunately the sensor is difficult
00083 //      to find now since it's been discontinued by the manufacturer.
00084 //
00085 //    The v2 Build Guide has details on how to build and configure all of the
00086 //    sensor options.
00087 //
00088 //    Visual Pinball has built-in support for plunger devices like this one, but 
00089 //    some older VP tables (particularly for VP 9) can't use it without some
00090 //    modifications to their scripting.  The Build Guide has advice on how to
00091 //    fix up VP tables to add plunger support when necessary.
00092 //
00093 //  - Button input wiring.  You can assign GPIO ports as inputs for physical
00094 //    pinball-style buttons, such as flipper buttons, a Start button, coin
00095 //    chute switches, tilt bobs, and service panel buttons.  You can configure
00096 //    each button input to report a keyboard key or joystick button press to
00097 //    the PC when the physical button is pushed.
00098 //
00099 //  - LedWiz emulation.  The KL25Z can pretend to be an LedWiz device.  This lets
00100 //    you connect feedback devices (lights, solenoids, motors) to GPIO ports on the 
00101 //    KL25Z, and lets PC software (such as Visual Pinball) control them during game 
00102 //    play to create a more immersive playing experience.  The Pinscape software
00103 //    presents itself to the host as an LedWiz device and accepts the full LedWiz
00104 //    command set, so software on the PC designed for real LedWiz'es can control
00105 //    attached devices without any modifications.
00106 //
00107 //    Even though the software provides a very thorough LedWiz emulation, the KL25Z
00108 //    GPIO hardware design imposes some serious limitations.  The big one is that
00109 //    the KL25Z only has 10 PWM channels, meaning that only 10 ports can have
00110 //    varying-intensity outputs (e.g., for controlling the brightness level of an
00111 //    LED or the speed or a motor).  You can control more than 10 output ports, but
00112 //    only 10 can have PWM control; the rest are simple "digital" ports that can only
00113 //    be switched fully on or fully off.  The second limitation is that the KL25Z
00114 //    just doesn't have that many GPIO ports overall.  There are enough to populate
00115 //    all 32 button inputs OR all 32 LedWiz outputs, but not both.  The default is
00116 //    to assign 24 buttons and 22 LedWiz ports; you can change this balance to trade
00117 //    off more outputs for fewer inputs, or vice versa.  The third limitation is that
00118 //    the KL25Z GPIO pins have *very* tiny amperage limits - just 4mA, which isn't
00119 //    even enough to control a small LED.  So in order to connect any kind of feedback
00120 //    device to an output, you *must* build some external circuitry to boost the
00121 //    current handing.  The Build Guide has a reference circuit design for this
00122 //    purpose that's simple and inexpensive to build.
00123 //
00124 //  - Enhanced LedWiz emulation with TLC5940 and/or TLC59116 PWM controller chips. 
00125 //    You can attach external PWM chips for controlling device outputs, instead of
00126 //    using (or in addition to) the on-board GPIO ports as described above.  The 
00127 //    software can control a set of daisy-chained TLC5940 or TLC59116 chips.  Each
00128 //    chip provides 16 PWM outputs, so you just need two of them to get the full 
00129 //    complement of 32 output ports of a real LedWiz.  You can hook up even more, 
00130 //    though.  Four chips gives you 64 ports, which should be plenty for nearly any 
00131 //    virtual pinball project.  
00132 //
00133 //    The Pinscape Expansion Board project (which appeared in early 2016) provides
00134 //    a reference hardware design, with EAGLE circuit board layouts, that takes full
00135 //    advantage of the TLC5940 capability.  It lets you create a customized set of
00136 //    outputs with full PWM control and power handling for high-current devices
00137 //    built in to the boards.
00138 //
00139 //    To accommodate the larger supply of ports possible with the external chips,
00140 //    the controller software provides a custom, extended version of the LedWiz 
00141 //    protocol that can handle up to 128 ports.  Legacy PC software designed only
00142 //    for the original LedWiz obviously can't use the extended protocol, and thus 
00143 //    can't take advantage of its extra capabilities, but the latest version of 
00144 //    DOF (DirectOutput Framework) *does*  know the new language and can take full
00145 //    advantage.  Older software will still work, though - the new extensions are 
00146 //    all backwards compatible, so old software that only knows about the original 
00147 //    LedWiz protocol will still work, with the limitation that it can only access 
00148 //    the first 32 ports.  In addition, we provide a replacement LEDWIZ.DLL that 
00149 //    creates virtual LedWiz units representing additional ports beyond the first
00150 //    32.  This allows legacy LedWiz client software to address all ports by
00151 //    making them think that you have several physical LedWiz units installed.
00152 //
00153 //  - Night Mode control for output devices.  You can connect a switch or button
00154 //    to the controller to activate "Night Mode", which disables feedback devices
00155 //    that you designate as noisy.  You can designate outputs individually as being 
00156 //    included in this set or not.  This is useful if you want to play a game on 
00157 //    your cabinet late at night without waking the kids and annoying the neighbors.
00158 //
00159 //  - TV ON switch.  The controller can pulse a relay to turn on your TVs after
00160 //    power to the cabinet comes on, with a configurable delay timer.  This feature
00161 //    is for TVs that don't turn themselves on automatically when first plugged in.
00162 //    To use this feature, you have to build some external circuitry to allow the
00163 //    software to sense the power supply status.  The Build Guide has details 
00164 //    on the necessary circuitry.  You can use this to switch your TV on via a
00165 //    hardwired connection to the TV's "on" button, which requires taking the
00166 //    TV apart to gain access to its internal wiring, or optionally via the IR
00167 //    remote control transmitter feature below.
00168 //
00169 //  - Infrared (IR) remote control receiver and transmitter.  You can attach an
00170 //    IR LED and/or an IR sensor (we recommend the TSOP384xx series) to make the
00171 //    KL25Z capable of sending and/or receiving IR remote control signals.  This
00172 //    can be used with the TV ON feature above to turn your TV(s) on when the
00173 //    system power comes on by sending the "on" command to them via IR, as though
00174 //    you pressed the "on" button on the remote control.  The sensor lets the
00175 //    Pinscape software learn the IR codes from your existing remotes, in the
00176 //    same manner as a handheld universal remote control, and the IR LED lets
00177 //    it transmit learned codes.  The sensor can also be used to receive codes
00178 //    during normal operation and turn them into PC keystrokes; this lets you
00179 //    access extra commands on the PC without adding more buttons to your
00180 //    cabinet.  The IR LED can also be used to transmit other codes when you
00181 //    press selected cabinet buttons, allowing you to assign cabinet buttons
00182 //    to send IR commands to your cabinet TV or other devices.
00183 //
00184 //
00185 //
00186 // STATUS LIGHTS:  The on-board LED on the KL25Z flashes to indicate the current 
00187 // device status.  The flash patterns are:
00188 //
00189 //    short yellow flash = waiting to connect
00190 //
00191 //    short red flash = the connection is suspended (the host is in sleep
00192 //        or suspend mode, the USB cable is unplugged after a connection
00193 //        has been established)
00194 //
00195 //    two short red flashes = connection lost (the device should immediately
00196 //        go back to short-yellow "waiting to reconnect" mode when a connection
00197 //        is lost, so this display shouldn't normally appear)
00198 //
00199 //    long red/yellow = USB connection problem.  The device still has a USB
00200 //        connection to the host (or so it appears to the device), but data 
00201 //        transmissions are failing.
00202 //
00203 //    medium blue flash = TV ON delay timer running.  This means that the
00204 //        power to the secondary PSU has just been turned on, and the TV ON
00205 //        timer is waiting for the configured delay time before pulsing the
00206 //        TV power button relay.  This is only shown if the TV ON feature is
00207 //        enabled.
00208 //
00209 //    long yellow/green = everything's working, but the plunger hasn't
00210 //        been calibrated.  Follow the calibration procedure described in
00211 //        the project documentation.  This flash mode won't appear if there's
00212 //        no plunger sensor configured.
00213 //
00214 //    alternating blue/green = everything's working normally, and plunger
00215 //        calibration has been completed (or there's no plunger attached)
00216 //
00217 //    fast red/purple = out of memory.  The controller halts and displays
00218 //        this diagnostic code until you manually reset it.  If this happens,
00219 //        it's probably because the configuration is too complex, in which
00220 //        case the same error will occur after the reset.  If it's stuck
00221 //        in this cycle, you'll have to restore the default configuration
00222 //        by re-installing the controller software (the Pinscape .bin file).
00223 //
00224 //
00225 // USB PROTOCOL:  Most of our USB messaging is through standard USB HID
00226 // classes (joystick, keyboard).  We also accept control messages on our
00227 // primary HID interface "OUT endpoint" using a custom protocol that's
00228 // not defined in any USB standards (we do have to provide a USB HID
00229 // Report Descriptor for it, but this just describes the protocol as
00230 // opaque vendor-defined bytes).  The control protocol incorporates the 
00231 // LedWiz protocol as a subset, and adds our own private extensions.
00232 // For full details, see USBProtocol.h.
00233 
00234 
00235 #include "mbed.h"
00236 #include "math.h"
00237 #include "diags.h"
00238 #include "pinscape.h"
00239 #include "NewMalloc.h"
00240 #include "USBJoystick.h"
00241 #include "MMA8451Q.h"
00242 #include "FreescaleIAP.h"
00243 #include "crc32.h"
00244 #include "TLC5940.h"
00245 #include "TLC59116.h"
00246 #include "74HC595.h"
00247 #include "nvm.h"
00248 #include "TinyDigitalIn.h"
00249 #include "IRReceiver.h"
00250 #include "IRTransmitter.h"
00251 #include "NewPwm.h"
00252 
00253 // plunger sensors
00254 #include "plunger.h"
00255 #include "edgeSensor.h"
00256 #include "potSensor.h"
00257 #include "quadSensor.h"
00258 #include "nullSensor.h"
00259 #include "barCodeSensor.h"
00260 #include "distanceSensor.h"
00261 #include "tsl14xxSensor.h"
00262 #include "rotarySensor.h"
00263 #include "tcd1103Sensor.h"
00264 
00265 
00266 #define DECL_EXTERNS
00267 #include "config.h"
00268 
00269 
00270 // --------------------------------------------------------------------------
00271 // 
00272 // OpenSDA module identifier.  This is for the benefit of the Windows
00273 // configuration tool.  When the config tool installs a .bin file onto
00274 // the KL25Z, it will first find the sentinel string within the .bin file,
00275 // and patch the "\0" bytes that follow the sentinel string with the 
00276 // OpenSDA module ID data.  This allows us to report the OpenSDA 
00277 // identifiers back to the host system via USB, which in turn allows the 
00278 // config tool to figure out which OpenSDA MSD (mass storage device - a 
00279 // virtual disk drive) correlates to which Pinscape controller USB 
00280 // interface.  
00281 // 
00282 // This is only important if multiple Pinscape devices are attached to 
00283 // the same host.  There doesn't seem to be any other way to figure out 
00284 // which OpenSDA MSD corresponds to which KL25Z USB interface; the OpenSDA 
00285 // MSD doesn't report the KL25Z CPU ID anywhere, and the KL25Z doesn't
00286 // have any way to learn about the OpenSDA module it's connected to.  The
00287 // only way to pass this information to the KL25Z side that I can come up 
00288 // with is to have the Windows host embed it in the .bin file before 
00289 // downloading it to the OpenSDA MSD.
00290 //
00291 // We initialize the const data buffer (the part after the sentinel string)
00292 // with all "\0" bytes, so that's what will be in the executable image that
00293 // comes out of the mbed compiler.  If you manually install the resulting
00294 // .bin file onto the KL25Z (via the Windows desktop, say), the "\0" bytes
00295 // will stay this way and read as all 0's at run-time.  Since a real TUID
00296 // would never be all 0's, that tells us that we were never patched and
00297 // thus don't have any information on the OpenSDA module.
00298 //
00299 const char *getOpenSDAID()
00300 {
00301     #define OPENSDA_PREFIX "///Pinscape.OpenSDA.TUID///"
00302     static const char OpenSDA[] = OPENSDA_PREFIX "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0///";
00303     const size_t OpenSDA_prefix_length = sizeof(OPENSDA_PREFIX) - 1;
00304     
00305     return OpenSDA + OpenSDA_prefix_length;
00306 }
00307 
00308 // --------------------------------------------------------------------------
00309 //
00310 // Build ID.  We use the date and time of compiling the program as a build
00311 // identifier.  It would be a little nicer to use a simple serial number
00312 // instead, but the mbed platform doesn't have a way to automate that.  The
00313 // timestamp is a pretty good proxy for a serial number in that it will
00314 // naturally increase on each new build, which is the primary property we
00315 // want from this.
00316 //
00317 // As with the embedded OpenSDA ID, we store the build timestamp with a
00318 // sentinel string prefix, to allow automated tools to find the static data
00319 // in the .bin file by searching for the sentinel string.  In contrast to 
00320 // the OpenSDA ID, the value we store here is for tools to extract rather 
00321 // than store, since we automatically populate it via the preprocessor 
00322 // macros.
00323 //
00324 const char *getBuildID()
00325 {
00326     #define BUILDID_PREFIX "///Pinscape.Build.ID///"
00327     static const char BuildID[] = BUILDID_PREFIX __DATE__ " " __TIME__ "///";
00328     const size_t BuildID_prefix_length = sizeof(BUILDID_PREFIX) - 1;
00329     
00330     return BuildID + BuildID_prefix_length;
00331 }
00332 
00333 // --------------------------------------------------------------------------
00334 // Main loop iteration timing statistics.  Collected only if 
00335 // ENABLE_DIAGNOSTICS is set in diags.h.
00336 #if ENABLE_DIAGNOSTICS
00337   uint64_t mainLoopIterTime, mainLoopIterCheckpt[15], mainLoopIterCount;
00338   uint64_t mainLoopMsgTime, mainLoopMsgCount;
00339   Timer mainLoopTimer;
00340 #endif
00341 
00342 
00343 // ---------------------------------------------------------------------------
00344 //
00345 // Forward declarations
00346 //
00347 void setNightMode(bool on);
00348 void toggleNightMode();
00349 
00350 // ---------------------------------------------------------------------------
00351 // utilities
00352 
00353 // int/float point square of a number
00354 inline int square(int x) { return x*x; }
00355 inline float square(float x) { return x*x; }
00356 
00357 // floating point rounding
00358 inline float round(float x) { return x > 0 ? floor(x + 0.5) : ceil(x - 0.5); }
00359 
00360 
00361 // --------------------------------------------------------------------------
00362 // 
00363 // Extended verison of Timer class.  This adds the ability to interrogate
00364 // the running state.
00365 //
00366 class ExtTimer: public Timer
00367 {
00368 public:
00369     ExtTimer() : running(false) { }
00370 
00371     void start() { running = true; Timer::start(); }
00372     void stop()  { running = false; Timer::stop(); }
00373     
00374     bool isRunning() const { return running; }
00375     
00376 private:
00377     bool running;
00378 };
00379 
00380 
00381 // --------------------------------------------------------------------------
00382 // 
00383 // USB product version number
00384 //
00385 const uint16_t USB_VERSION_NO = 0x000A;
00386 
00387 // --------------------------------------------------------------------------
00388 //
00389 // Joystick axis report range - we report from -JOYMAX to +JOYMAX
00390 //
00391 #define JOYMAX 4096
00392 
00393 
00394 // ---------------------------------------------------------------------------
00395 //
00396 // Wire protocol value translations.  These translate byte values to and
00397 // from the USB protocol to local native format.
00398 //
00399 
00400 // unsigned 16-bit integer 
00401 inline uint16_t wireUI16(const uint8_t *b)
00402 {
00403     return b[0] | ((uint16_t)b[1] << 8);
00404 }
00405 inline void ui16Wire(uint8_t *b, uint16_t val)
00406 {
00407     b[0] = (uint8_t)(val & 0xff);
00408     b[1] = (uint8_t)((val >> 8) & 0xff);
00409 }
00410 
00411 inline int16_t wireI16(const uint8_t *b)
00412 {
00413     return (int16_t)wireUI16(b);
00414 }
00415 inline void i16Wire(uint8_t *b, int16_t val)
00416 {
00417     ui16Wire(b, (uint16_t)val);
00418 }
00419 
00420 inline uint32_t wireUI32(const uint8_t *b)
00421 {
00422     return b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
00423 }
00424 inline void ui32Wire(uint8_t *b, uint32_t val)
00425 {
00426     b[0] = (uint8_t)(val & 0xff);
00427     b[1] = (uint8_t)((val >> 8) & 0xff);    
00428     b[2] = (uint8_t)((val >> 16) & 0xff);    
00429     b[3] = (uint8_t)((val >> 24) & 0xff);    
00430 }
00431 
00432 inline int32_t wireI32(const uint8_t *b)
00433 {
00434     return (int32_t)wireUI32(b);
00435 }
00436 
00437 // Convert "wire" (USB) pin codes to/from PinName values.
00438 // 
00439 // The internal mbed PinName format is 
00440 //
00441 //   ((port) << PORT_SHIFT) | (pin << 2)    // MBED FORMAT
00442 //
00443 // where 'port' is 0-4 for Port A to Port E, and 'pin' is
00444 // 0 to 31.  E.g., E31 is (4 << PORT_SHIFT) | (31<<2).
00445 //
00446 // We remap this to our more compact wire format where each
00447 // pin name fits in 8 bits:
00448 //
00449 //   ((port) << 5) | pin)                   // WIRE FORMAT
00450 //
00451 // E.g., E31 is (4 << 5) | 31.
00452 //
00453 // Wire code FF corresponds to PinName NC (not connected).
00454 //
00455 inline PinName wirePinName(uint8_t c)
00456 {
00457     if (c == 0xFF)
00458         return NC;                                  // 0xFF -> NC
00459     else 
00460         return PinName(
00461             (int(c & 0xE0) << (PORT_SHIFT - 5))      // top three bits are the port
00462             | (int(c & 0x1F) << 2));                // bottom five bits are pin
00463 }
00464 inline void pinNameWire(uint8_t *b, PinName n)
00465 {
00466     *b = PINNAME_TO_WIRE(n);
00467 }
00468 
00469 
00470 // ---------------------------------------------------------------------------
00471 //
00472 // On-board RGB LED elements - we use these for diagnostic displays.
00473 //
00474 // Note that LED3 (the blue segment) is hard-wired on the KL25Z to PTD1,
00475 // so PTD1 shouldn't be used for any other purpose (e.g., as a keyboard
00476 // input or a device output).  This is kind of unfortunate in that it's 
00477 // one of only two ports exposed on the jumper pins that can be muxed to 
00478 // SPI0 SCLK.  This effectively limits us to PTC5 if we want to use the 
00479 // SPI capability.
00480 //
00481 DigitalOut *ledR, *ledG, *ledB;
00482 
00483 // Power on timer state for diagnostics.  We flash the blue LED when
00484 // nothing else is going on.  State 0-1 = off, 2-3 = on blue.  Also
00485 // show red when transmitting an LED signal, indicated by state 4.
00486 uint8_t powerTimerDiagState = 0;
00487 
00488 // Show the indicated pattern on the diagnostic LEDs.  0 is off, 1 is
00489 // on, and -1 is no change (leaves the current setting intact).
00490 static uint8_t diagLEDState = 0;
00491 void diagLED(int r, int g, int b)
00492 {
00493     // remember the new state
00494     diagLEDState = r | (g << 1) | (b << 2);
00495     
00496     // if turning everything off, use the power timer state instead, 
00497     // applying it to the blue LED
00498     if (diagLEDState == 0)
00499     {
00500         b = (powerTimerDiagState == 2 || powerTimerDiagState == 3);
00501         r = (powerTimerDiagState == 4);
00502     }
00503         
00504     // set the new state
00505     if (ledR != 0 && r != -1) ledR->write(!r);
00506     if (ledG != 0 && g != -1) ledG->write(!g);
00507     if (ledB != 0 && b != -1) ledB->write(!b);
00508 }
00509 
00510 // update the LEDs with the current state
00511 void diagLED(void)
00512 {
00513     diagLED(
00514         diagLEDState & 0x01,
00515         (diagLEDState >> 1) & 0x01,
00516         (diagLEDState >> 2) & 0x01);
00517 }
00518 
00519 // check an output port or pin assignment to see if it conflicts with
00520 // an on-board LED segment
00521 struct LedSeg 
00522 { 
00523     bool r, g, b; 
00524     LedSeg() { r = g = b = false; } 
00525 
00526     // check an output port to see if it conflicts with one of the LED ports
00527     void check(LedWizPortCfg &pc)
00528     {
00529         // if it's a GPIO, check to see if it's assigned to one of
00530         // our on-board LED segments
00531         int t = pc.typ;
00532         if (t == PortTypeGPIOPWM || t == PortTypeGPIODig)
00533             check(pc.pin);
00534     }
00535 
00536     // check a pin to see if it conflicts with one of the diagnostic LED ports    
00537     void check(uint8_t pinId)
00538     {
00539         PinName pin = wirePinName(pinId);
00540         if (pin == LED1)
00541             r = true;
00542         else if (pin == LED2)
00543             g = true;
00544         else if (pin == LED3)
00545             b = true;
00546     }
00547 };
00548 
00549 // Initialize the diagnostic LEDs.  By default, we use the on-board
00550 // RGB LED to display the microcontroller status.  However, we allow
00551 // the user to commandeer the on-board LED as an LedWiz output device,
00552 // which can be useful for testing a new installation.  So we'll check
00553 // for LedWiz outputs assigned to the on-board LED segments, and turn
00554 // off the diagnostic use for any so assigned.
00555 void initDiagLEDs(Config &cfg)
00556 {
00557     // run through the configuration list and cross off any of the
00558     // LED segments assigned to LedWiz ports
00559     LedSeg l;
00560     for (int i = 0 ; i < MAX_OUT_PORTS && cfg.outPort[i].typ != PortTypeDisabled ; ++i)
00561         l.check(cfg.outPort[i]);
00562         
00563     // check the button inputs
00564     for (int i = 0 ; i < countof(cfg.button) ; ++i)
00565         l.check(cfg.button[i].pin);
00566         
00567     // check plunger inputs
00568     if (cfg.plunger.enabled && cfg.plunger.sensorType != PlungerType_None)
00569     {
00570         for (int i = 0 ; i < countof(cfg.plunger.sensorPin) ; ++i)
00571             l.check(cfg.plunger.sensorPin[i]);
00572             
00573         l.check(cfg.plunger.cal.btn);
00574         l.check(cfg.plunger.cal.led);
00575     }
00576     
00577     // check the TV ON pin assignments
00578     l.check(cfg.TVON.statusPin);
00579     l.check(cfg.TVON.latchPin);
00580     l.check(cfg.TVON.relayPin);
00581 
00582     // check  the TLC5940 pins
00583     if (cfg.tlc5940.nchips != 0)
00584     {
00585         l.check(cfg.tlc5940.sin);
00586         l.check(cfg.tlc5940.sclk);
00587         l.check(cfg.tlc5940.xlat);
00588         l.check(cfg.tlc5940.blank);
00589         l.check(cfg.tlc5940.gsclk);
00590     }
00591     
00592     // check 74HC595 pin assignments
00593     if (cfg.hc595.nchips != 0)
00594     {
00595         l.check(cfg.hc595.sin);
00596         l.check(cfg.hc595.sclk);
00597         l.check(cfg.hc595.latch);
00598         l.check(cfg.hc595.ena);
00599     }
00600     
00601     // check TLC59116 pin assignments
00602     if (cfg.tlc59116.chipMask != 0)
00603     {
00604         l.check(cfg.tlc59116.sda);
00605         l.check(cfg.tlc59116.scl);
00606         l.check(cfg.tlc59116.reset);
00607     }
00608     
00609     // check the IR remove control hardware
00610     l.check(cfg.IR.sensor);
00611     l.check(cfg.IR.emitter);
00612     
00613     // We now know which segments are taken for other uses and which
00614     // are free.  Create diagnostic ports for the ones not claimed for
00615     // other purposes.
00616     if (!l.r) ledR = new DigitalOut(LED1, 1);
00617     if (!l.g) ledG = new DigitalOut(LED2, 1);
00618     if (!l.b) ledB = new DigitalOut(LED3, 1);
00619 }
00620 
00621 
00622 // ---------------------------------------------------------------------------
00623 //
00624 // LedWiz emulation
00625 //
00626 
00627 // LedWiz output states.
00628 //
00629 // The LedWiz protocol has two separate control axes for each output.
00630 // One axis is its on/off state; the other is its "profile" state, which
00631 // is either a fixed brightness or a blinking pattern for the light.
00632 // The two axes are independent.
00633 //
00634 // Even though the original LedWiz protocol can only access 32 ports, we
00635 // maintain LedWiz state for every port, even if we have more than 32.  Our
00636 // extended protocol allows the client to send LedWiz-style messages that
00637 // control any set of ports.  A replacement LEDWIZ.DLL can make a single
00638 // Pinscape unit look like multiple virtual LedWiz units to legacy clients,
00639 // allowing them to control all of our ports.  The clients will still be
00640 // using LedWiz-style states to control the ports, so we need to support
00641 // the LedWiz scheme with separate on/off and brightness control per port.
00642 
00643 // On/off state for each LedWiz output
00644 static uint8_t *wizOn;
00645 
00646 // LedWiz "Profile State" (the LedWiz brightness level or blink mode)
00647 // for each LedWiz output.  If the output was last updated through an 
00648 // LedWiz protocol message, it will have one of these values:
00649 //
00650 //   0-48 = fixed brightness 0% to 100%
00651 //   49  = fixed brightness 100% (equivalent to 48)
00652 //   129 = ramp up / ramp down
00653 //   130 = flash on / off
00654 //   131 = on / ramp down
00655 //   132 = ramp up / on
00656 //
00657 // (Note that value 49 isn't documented in the LedWiz spec, but real
00658 // LedWiz units treat it as equivalent to 48, and some PC software uses
00659 // it, so we need to accept it for compatibility.)
00660 static uint8_t *wizVal;
00661 
00662 // Current actual brightness for each output.  This is a simple linear
00663 // value on a 0..255 scale.  This is EITHER the linear brightness computed 
00664 // from the LedWiz setting for the port, OR the 0..255 value set explicitly 
00665 // by the extended protocol:
00666 //
00667 // - If the last command that updated the port was an extended protocol 
00668 //   SET BRIGHTNESS command, this is the value set by that command.  In
00669 //   addition, wizOn[port] is set to 0 if the brightness is 0, 1 otherwise;
00670 //   and wizVal[port] is set to the brightness rescaled to the 0..48 range
00671 //   if the brightness is non-zero.
00672 //
00673 // - If the last command that updated the port was an LedWiz command
00674 //   (SBA/PBA/SBX/PBX), this contains the brightness value computed from
00675 //   the combination of wizOn[port] and wizVal[port].  If wizOn[port] is 
00676 //   zero, this is simply 0, otherwise it's wizVal[port] rescaled to the
00677 //   0..255 range.
00678 //
00679 // - For a port set to wizOn[port]=1 and wizVal[port] in 129..132, this is
00680 //   also updated continuously to reflect the current flashing brightness
00681 //   level.
00682 //
00683 static uint8_t *outLevel;
00684 
00685 
00686 // LedWiz flash speed.  This is a value from 1 to 7 giving the pulse
00687 // rate for lights in blinking states.  The LedWiz API doesn't document
00688 // what the numbers mean in real time units, but by observation, the
00689 // "speed" setting represents the period of the flash cycle in 0.25s
00690 // units, so speed 1 = 0.25 period = 4Hz, speed 7 = 1.75s period = 0.57Hz.
00691 // The period is the full cycle time of the flash waveform.
00692 //
00693 // Each bank of 32 lights has its independent own pulse rate, so we need 
00694 // one entry per bank.  Each bank has 32 outputs, so we need a total of
00695 // ceil(number_of_physical_outputs/32) entries.  Note that we could allocate 
00696 // this dynamically once we know the number of actual outputs, but the 
00697 // upper limit is low enough that it's more efficient to use a fixed array
00698 // at the maximum size.
00699 static const int MAX_LW_BANKS = (MAX_OUT_PORTS+31)/32;
00700 static uint8_t wizSpeed[MAX_LW_BANKS];
00701 
00702 // Current starting output index for "PBA" messages from the PC (using
00703 // the LedWiz USB protocol).  Each PBA message implicitly uses the
00704 // current index as the starting point for the ports referenced in
00705 // the message, and increases it (by 8) for the next call.
00706 static int pbaIdx = 0;
00707 
00708 
00709 // ---------------------------------------------------------------------------
00710 //
00711 // Output Ports
00712 //
00713 // There are two way to connect outputs.  First, you can use the on-board
00714 // GPIO ports to implement device outputs: each LedWiz software port is
00715 // connected to a physical GPIO pin on the KL25Z.  This has some pretty
00716 // strict limits, though.  The KL25Z only has 10 PWM channels, so only 10
00717 // GPIO LedWiz ports can be made dimmable; the rest are strictly on/off.  
00718 // The KL25Z also simply doesn't have enough exposed GPIO ports overall to 
00719 // support all of the features the software supports.  The software allows 
00720 // for up to 128 outputs, 48 button inputs, plunger input (requiring 1-5 
00721 // GPIO pins), and various other external devices.  The KL25Z only exposes
00722 // about 50 GPIO pins.  So if you want to do everything with GPIO ports,
00723 // you have to ration pins among features.
00724 //
00725 // To overcome some of these limitations, we also support several external
00726 // peripheral controllers that allow adding many more outputs, using only
00727 // a small number of GPIO pins to interface with the peripherals:
00728 //
00729 // - TLC5940 PWM controller chips.  Each TLC5940 provides 16 ports with
00730 //   12-bit PWM, and multiple TLC5940 chips can be daisy-chained.  The
00731 //   chips connect via 5 GPIO pins, and since they're daisy-chainable,
00732 //   one set of 5 pins can control any number of the chips.  So this chip
00733 //   effectively converts 5 GPIO pins into almost any number of PWM outputs.
00734 //
00735 // - TLC59116 PWM controller chips.  These are similar to the TLC5940 but
00736 //   a newer generation with an improved design.  These use an I2C bus,
00737 //   allowing up to 14 chips to be connected via 3 GPIO pins.
00738 //
00739 // - 74HC595 shift register chips.  These provide 8 digital (on/off only)
00740 //   outputs per chip.  These need 4 GPIO pins, and like the other can be
00741 //   daisy chained to add more outputs without using more GPIO pins.  These
00742 //   are advantageous for outputs that don't require PWM, since the data
00743 //   transfer sizes are so much smaller.  The expansion boards use these
00744 //   for the chime board outputs.
00745 //
00746 // Direct GPIO output ports and peripheral controllers can be mixed and
00747 // matched in one system.  The assignment of pins to ports and the 
00748 // configuration of peripheral controllers is all handled in the software
00749 // setup, so a physical system can be expanded and updated at any time.
00750 //
00751 // To handle the diversity of output port types, we start with an abstract
00752 // base class for outputs.  Each type of physical output interface has a
00753 // concrete subclass.  During initialization, we create the appropriate
00754 // subclass for each software port, mapping it to the assigned GPIO pin 
00755 // or peripheral port.   Most of the rest of the software only cares about
00756 // the abstract interface, so once the subclassed port objects are set up,
00757 // the rest of the system can control the ports without knowing which types
00758 // of physical devices they're connected to.
00759 
00760 
00761 // Generic LedWiz output port interface.  We create a cover class to 
00762 // virtualize digital vs PWM outputs, and on-board KL25Z GPIO vs external 
00763 // TLC5940 outputs, and give them all a common interface.  
00764 class LwOut
00765 {
00766 public:
00767     // Set the output intensity.  'val' is 0 for fully off, 255 for
00768     // fully on, with values in between signifying lower intensity.
00769     virtual void set(uint8_t val) = 0;
00770 };
00771 
00772 // LwOut class for virtual ports.  This type of port is visible to
00773 // the host software, but isn't connected to any physical output.
00774 // This can be used for special software-only ports like the ZB
00775 // Launch Ball output, or simply for placeholders in the LedWiz port
00776 // numbering.
00777 class LwVirtualOut: public LwOut
00778 {
00779 public:
00780     LwVirtualOut() { }
00781     virtual void set(uint8_t ) { }
00782 };
00783 
00784 // Active Low out.  For any output marked as active low, we layer this
00785 // on top of the physical pin interface.  This simply inverts the value of
00786 // the output value, so that 255 means fully off and 0 means fully on.
00787 class LwInvertedOut: public LwOut
00788 {
00789 public:
00790     LwInvertedOut(LwOut *o) : out(o) { }
00791     virtual void set(uint8_t val) { out->set(255 - val); }
00792     
00793 private:
00794     // underlying physical output
00795     LwOut *out;
00796 };
00797 
00798 // Global ZB Launch Ball state
00799 bool zbLaunchOn = false;
00800 
00801 // ZB Launch Ball output.  This is layered on a port (physical or virtual)
00802 // to track the ZB Launch Ball signal.
00803 class LwZbLaunchOut: public LwOut
00804 {
00805 public:
00806     LwZbLaunchOut(LwOut *o) : out(o) { }
00807     virtual void set(uint8_t val)
00808     {
00809         // update the global ZB Launch Ball state
00810         zbLaunchOn = (val != 0);
00811         
00812         // pass it along to the underlying port, in case it's a physical output
00813         out->set(val);
00814     }
00815         
00816 private:
00817     // underlying physical or virtual output
00818     LwOut *out;
00819 };
00820 
00821 
00822 // Gamma correction table for 8-bit input values
00823 static const uint8_t dof_to_gamma_8bit[] = {
00824       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
00825       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   1,   1,   1, 
00826       1,   1,   1,   1,   1,   1,   1,   1,   1,   2,   2,   2,   2,   2,   2,   2, 
00827       2,   3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,   5,   5,   5, 
00828       5,   6,   6,   6,   6,   7,   7,   7,   7,   8,   8,   8,   9,   9,   9,  10, 
00829      10,  10,  11,  11,  11,  12,  12,  13,  13,  13,  14,  14,  15,  15,  16,  16, 
00830      17,  17,  18,  18,  19,  19,  20,  20,  21,  21,  22,  22,  23,  24,  24,  25, 
00831      25,  26,  27,  27,  28,  29,  29,  30,  31,  32,  32,  33,  34,  35,  35,  36, 
00832      37,  38,  39,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  50, 
00833      51,  52,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  64,  66,  67,  68, 
00834      69,  70,  72,  73,  74,  75,  77,  78,  79,  81,  82,  83,  85,  86,  87,  89, 
00835      90,  92,  93,  95,  96,  98,  99, 101, 102, 104, 105, 107, 109, 110, 112, 114, 
00836     115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142, 
00837     144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175, 
00838     177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 
00839     215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255
00840 };
00841 
00842 // Gamma-corrected out.  This is a filter object that we layer on top
00843 // of a physical pin interface.  This applies gamma correction to the
00844 // input value and then passes it along to the underlying pin object.
00845 class LwGammaOut: public LwOut
00846 {
00847 public:
00848     LwGammaOut(LwOut *o) : out(o) { }
00849     virtual void set(uint8_t val) { out->set(dof_to_gamma_8bit[val]); }
00850     
00851 private:
00852     LwOut *out;
00853 };
00854 
00855 // Global night mode flag.  To minimize overhead when reporting
00856 // the status, we set this to the status report flag bit for
00857 // night mode, 0x02, when engaged.
00858 static uint8_t nightMode = 0x00;
00859 
00860 // Noisy output.  This is a filter object that we layer on top of
00861 // a physical pin output.  This filter disables the port when night
00862 // mode is engaged.
00863 class LwNoisyOut: public LwOut
00864 {
00865 public:
00866     LwNoisyOut(LwOut *o) : out(o) { }
00867     virtual void set(uint8_t val) { out->set(nightMode ? 0 : val); }
00868     
00869 private:
00870     LwOut *out;
00871 };
00872 
00873 // Night Mode indicator output.  This is a filter object that we
00874 // layer on top of a physical pin output.  This filter ignores the
00875 // host value and simply shows the night mode status.
00876 class LwNightModeIndicatorOut: public LwOut
00877 {
00878 public:
00879     LwNightModeIndicatorOut(LwOut *o) : out(o) { }
00880     virtual void set(uint8_t)
00881     {
00882         // ignore the host value and simply show the current 
00883         // night mode setting
00884         out->set(nightMode ? 255 : 0);
00885     }
00886 
00887 private:
00888     LwOut *out;
00889 };
00890 
00891 
00892 // Flipper Logic output.  This is a filter object that we layer on
00893 // top of a physical pin output.
00894 //
00895 // A Flipper Logic output is effectively a digital output from the
00896 // client's perspective, in that it ignores the intensity level and
00897 // only pays attention to the ON/OFF state.  0 is OFF and any other
00898 // level is ON.
00899 //
00900 // In terms of the physical output, though, we do use varying power.
00901 // It's just that the varying power isn't under the client's control;
00902 // we control it according to our flipperLogic settings:
00903 //
00904 // - When the software port transitions from OFF (0 brightness) to ON
00905 //   (any non-zero brightness level), we set the physical port to 100%
00906 //   power and start a timer.
00907 //
00908 // - When the full power time in our flipperLogic settings elapses,
00909 //   if the software port is still ON, we reduce the physical port to
00910 //   the PWM level in our flipperLogic setting.
00911 //
00912 class LwFlipperLogicOut: public LwOut
00913 {
00914 public:
00915     // Set up the output.  'params' is the flipperLogic value from
00916     // the configuration.
00917     LwFlipperLogicOut(LwOut *o, uint8_t params)
00918         : out(o), params(params)
00919     {
00920         // initially OFF
00921         state = 0;
00922     }
00923     
00924     virtual void set(uint8_t level)
00925     {
00926         // remember the new nominal level set by the client
00927         val = level;
00928         
00929         // update the physical output according to our current timing state
00930         switch (state)
00931         {
00932         case 0:
00933             // We're currently off.  If the new level is non-zero, switch
00934             // to state 1 (initial full-power interval) and set the requested
00935             // level.  If the new level is zero, we're switching from off to
00936             // off, so there's no change.
00937             if (level != 0)
00938             {
00939                 // switch to state 1 (initial full-power interval)
00940                 state = 1;
00941                 
00942                 // set the requested output level - there's no limit during
00943                 // the initial full-power interval, so set the exact level
00944                 // requested
00945                 out->set(level);
00946 
00947                 // add myself to the pending timer list
00948                 pending[nPending++] = this;
00949                 
00950                 // note the starting time
00951                 t0 = timer.read_us();
00952             }
00953             break;
00954             
00955         case 1:
00956             // Initial full-power interval.  If the new level is non-zero,
00957             // simply apply the new level as requested, since there's no
00958             // limit during this period.  If the new level is zero, shut
00959             // off the output and cancel the pending timer.
00960             out->set(level);
00961             if (level == 0)
00962             {
00963                 // We're switching off.  In state 1, we have a pending timer,
00964                 // so we need to remove it from the list.
00965                 for (int i = 0 ; i < nPending ; ++i)
00966                 {
00967                     // is this us?
00968                     if (pending[i] == this)
00969                     {
00970                         // remove myself by replacing the slot with the
00971                         // last list entry
00972                         pending[i] = pending[--nPending];
00973                         
00974                         // no need to look any further
00975                         break;
00976                     }
00977                 }
00978                 
00979                 // switch to state 0 (off)
00980                 state = 0;
00981             }
00982             break;
00983             
00984         case 2: 
00985             // Hold interval.  If the new level is zero, switch to state
00986             // 0 (off).  If the new level is non-zero, stay in the hold
00987             // state, and set the new level, applying the hold power setting
00988             // as the upper bound.
00989             if (level == 0)
00990             {
00991                 // switching off - turn off the physical output
00992                 out->set(0);
00993                 
00994                 // go to state 0 (off)
00995                 state = 0;
00996             }
00997             else
00998             {
00999                 // staying on - set the new physical output power to the
01000                 // lower of the requested power and the hold power
01001                 uint8_t hold = holdPower();
01002                 out->set(level < hold ? level : hold);
01003             }
01004             break;
01005         }
01006     }
01007     
01008     // Class initialization
01009     static void classInit(Config &cfg)
01010     {
01011         // Count the Flipper Logic outputs in the configuration.  We
01012         // need to allocate enough pending timer list space to accommodate
01013         // all of these outputs.
01014         int n = 0;
01015         for (int i = 0 ; i < MAX_OUT_PORTS ; ++i)
01016         {
01017             // if this port is active and marked as Flipper Logic, count it
01018             if (cfg.outPort[i].typ != PortTypeDisabled
01019                 && (cfg.outPort[i].flags & PortFlagFlipperLogic) != 0)
01020                 ++n;
01021         }
01022         
01023         // allocate space for the pending timer list
01024         pending = new LwFlipperLogicOut*[n];
01025         
01026         // there's nothing in the pending list yet
01027         nPending = 0;
01028         
01029         // Start our shared timer.  The epoch is arbitrary, since we only
01030         // use it to figure elapsed times.
01031         timer.start();
01032     }
01033 
01034     // Check for ports with pending timers.  The main routine should
01035     // call this on each iteration to process our state transitions.
01036     static void poll()
01037     {
01038         // note the current time
01039         uint32_t t = timer.read_us();
01040         
01041         // go through the timer list
01042         for (int i = 0 ; i < nPending ; )
01043         {
01044             // get the port
01045             LwFlipperLogicOut *port = pending[i];
01046             
01047             // assume we'll keep it
01048             bool remove = false;
01049             
01050             // check if the port is still on
01051             if (port->state != 0)
01052             {
01053                 // it's still on - check if the initial full power time has elapsed
01054                 if (uint32_t(t - port->t0) > port->fullPowerTime_us())
01055                 {
01056                     // done with the full power interval - switch to hold state
01057                     port->state = 2;
01058 
01059                     // set the physical port to the hold power setting or the
01060                     // client brightness setting, whichever is lower
01061                     uint8_t hold = port->holdPower();
01062                     uint8_t val = port->val;                    
01063                     port->out->set(val < hold ? val : hold);
01064                     
01065                     // we're done with the timer
01066                     remove = true;
01067                 }
01068             }
01069             else
01070             {
01071                 // the port was turned off before the timer expired - remove
01072                 // it from the timer list
01073                 remove = true;
01074             }
01075             
01076             // if desired, remove the port from the timer list
01077             if (remove)
01078             {
01079                 // Remove the list entry by overwriting the slot with
01080                 // the last entry in the list.
01081                 pending[i] = pending[--nPending];
01082                 
01083                 // Note that we don't increment the loop counter, since
01084                 // we now need to revisit this same slot.
01085             }
01086             else
01087             {
01088                 // we're keeping this item; move on to the next one
01089                 ++i;
01090             }
01091         }
01092     }
01093 
01094 protected:
01095     // underlying physical output
01096     LwOut *out;
01097     
01098     // Timestamp on 'timer' of start of full-power interval.  We set this
01099     // to the current 'timer' timestamp when entering state 1.
01100     uint32_t t0;
01101 
01102     // Nominal output level (brightness) last set by the client.  During
01103     // the initial full-power interval, we replicate the requested level
01104     // exactly on the physical output.  During the hold interval, we limit
01105     // the physical output to the hold power, but use the caller's value
01106     // if it's lower.
01107     uint8_t val;
01108     
01109     // Current port state:
01110     //
01111     //  0 = off
01112     //  1 = on at initial full power
01113     //  2 = on at hold power
01114     uint8_t state;
01115     
01116     // Configuration parameters.  The high 4 bits encode the initial full-
01117     // power time in 50ms units, starting at 0=50ms.  The low 4 bits encode
01118     // the hold power (applied after the initial time expires if the output
01119     // is still on) in units of 6.66%.  The resulting percentage is used
01120     // for the PWM duty cycle of the physical output.
01121     uint8_t params;
01122     
01123     // Figure the initial full-power time in microseconds: 50ms * (1+N),
01124     // where N is the high 4 bits of the parameter byte.
01125     inline uint32_t fullPowerTime_us() const { return 50000*(1 + ((params >> 4) & 0x0F)); }
01126     
01127     // Figure the hold power PWM level (0-255) 
01128     inline uint8_t holdPower() const { return (params & 0x0F) * 17; }
01129     
01130     // Timer.  This is a shared timer for all of the FL ports.  When we
01131     // transition from OFF to ON, we note the current time on this timer
01132     // (which runs continuously).  
01133     static Timer timer;
01134     
01135     // Flipper logic pending timer list.  Whenever a flipper logic output
01136     // transitions from OFF to ON, we add it to this list.  We scan the
01137     // list in our polling routine to find ports that have reached the
01138     // expiration of their initial full-power intervals.
01139     static LwFlipperLogicOut **pending;
01140     static uint8_t nPending;
01141 };
01142 
01143 // Flipper Logic statics
01144 Timer LwFlipperLogicOut::timer;
01145 LwFlipperLogicOut **LwFlipperLogicOut::pending;
01146 uint8_t LwFlipperLogicOut::nPending;
01147 
01148 // Chime Logic.  This is a filter output that we layer on a physical
01149 // output to set a minimum and maximum ON time for the output. 
01150 class LwChimeLogicOut: public LwOut
01151 {
01152 public:
01153     // Set up the output.  'params' encodes the minimum and maximum time.
01154     LwChimeLogicOut(LwOut *o, uint8_t params)
01155         : out(o), params(params)
01156     {
01157         // initially OFF
01158         state = 0;
01159     }
01160     
01161     virtual void set(uint8_t level)
01162     {
01163         // update the physical output according to our current timing state
01164         switch (state)
01165         {
01166         case 0:
01167             // We're currently off.  If the new level is non-zero, switch
01168             // to state 1 (initial minimum interval) and set the requested
01169             // level.  If the new level is zero, we're switching from off to
01170             // off, so there's no change.
01171             if (level != 0)
01172             {
01173                 // switch to state 1 (initial minimum interval, port is
01174                 // logically on)
01175                 state = 1;
01176                 
01177                 // set the requested output level
01178                 out->set(level);
01179 
01180                 // add myself to the pending timer list
01181                 pending[nPending++] = this;
01182                 
01183                 // note the starting time
01184                 t0 = timer.read_us();
01185             }
01186             break;
01187             
01188         case 1:   // min ON interval, port on
01189         case 2:   // min ON interval, port off
01190             // We're in the initial minimum ON interval.  If the new power
01191             // level is non-zero, pass it through to the physical port, since
01192             // the client is allowed to change the power level during the
01193             // initial ON interval - they just can't turn it off entirely.
01194             // Set the state to 1 to indicate that the logical port is on.
01195             //
01196             // If the new level is zero, leave the underlying port at its 
01197             // current power level, since we're not allowed to turn it off
01198             // during this period.  Set the state to 2 to indicate that the
01199             // logical port is off even though the physical port has to stay
01200             // on for the remainder of the interval.
01201             if (level != 0)
01202             {
01203                 // client is leaving the port on - pass through the new 
01204                 // power level and set state 1 (logically on)
01205                 out->set(level);
01206                 state = 1;
01207             }
01208             else
01209             {
01210                 // Client is turning off the port - leave the underlying port 
01211                 // on at its current level and set state 2 (logically off).
01212                 // When the minimum ON time expires, the polling routine will
01213                 // see that we're logically off and will pass that through to
01214                 // the underlying physical port.  Until then, though, we have
01215                 // to leave the physical port on to satisfy the minimum ON
01216                 // time requirement.
01217                 state = 2;
01218             }
01219             break;
01220             
01221         case 3: 
01222             // We're after the minimum ON interval and before the maximum
01223             // ON time limit.  We can set any new level, including fully off.  
01224             // Pass the new power level through to the port.
01225             out->set(level);
01226             
01227             // if the port is now off, return to state 0 (OFF)
01228             if (level == 0)
01229             {
01230                 // return to the OFF state
01231                 state = 0;
01232                 
01233                 // If we have a timer pending, remove it.  A timer will be
01234                 // pending if we have a non-infinite maximum on time for the
01235                 // port.
01236                 for (int i = 0 ; i < nPending ; ++i)
01237                 {
01238                     // is this us?
01239                     if (pending[i] == this)
01240                     {
01241                         // remove myself by replacing the slot with the
01242                         // last list entry
01243                         pending[i] = pending[--nPending];
01244                         
01245                         // no need to look any further
01246                         break;
01247                     }
01248                 }
01249             }
01250             break;
01251             
01252         case 4:
01253             // We're after the maximum ON time.  The physical port stays off
01254             // during this interval, so we don't pass any changes through to
01255             // the physical port.  When the client sets the level to 0, we
01256             // turn off the logical port and reset to state 0.
01257             if (level == 0)
01258                 state = 0;
01259             break;
01260         }
01261     }
01262     
01263     // Class initialization
01264     static void classInit(Config &cfg)
01265     {
01266         // Count the Minimum On Time outputs in the configuration.  We
01267         // need to allocate enough pending timer list space to accommodate
01268         // all of these outputs.
01269         int n = 0;
01270         for (int i = 0 ; i < MAX_OUT_PORTS ; ++i)
01271         {
01272             // if this port is active and marked as Flipper Logic, count it
01273             if (cfg.outPort[i].typ != PortTypeDisabled
01274                 && (cfg.outPort[i].flags & PortFlagChimeLogic) != 0)
01275                 ++n;
01276         }
01277         
01278         // allocate space for the pending timer list
01279         pending = new LwChimeLogicOut*[n];
01280         
01281         // there's nothing in the pending list yet
01282         nPending = 0;
01283         
01284         // Start our shared timer.  The epoch is arbitrary, since we only
01285         // use it to figure elapsed times.
01286         timer.start();
01287     }
01288 
01289     // Check for ports with pending timers.  The main routine should
01290     // call this on each iteration to process our state transitions.
01291     static void poll()
01292     {
01293         // note the current time
01294         uint32_t t = timer.read_us();
01295         
01296         // go through the timer list
01297         for (int i = 0 ; i < nPending ; )
01298         {
01299             // get the port
01300             LwChimeLogicOut *port = pending[i];
01301             
01302             // assume we'll keep it
01303             bool remove = false;
01304             
01305             // check our state
01306             switch (port->state)
01307             {
01308             case 1:  // initial minimum ON time, port logically on
01309             case 2:  // initial minimum ON time, port logically off
01310                 // check if the minimum ON time has elapsed
01311                 if (uint32_t(t - port->t0) > port->minOnTime_us())
01312                 {
01313                     // This port has completed its initial ON interval, so
01314                     // it advances to the next state. 
01315                     if (port->state == 1)
01316                     {
01317                         // The port is logically on, so advance to state 3.
01318                         // The underlying port is already at its proper level, 
01319                         // since we pass through non-zero power settings to the 
01320                         // underlying port throughout the initial minimum time.
01321                         // The timer stays active into state 3.
01322                         port->state = 3;
01323                         
01324                         // Special case: maximum on time 0 means "infinite".
01325                         // There's no need for a timer in this case; we'll
01326                         // just stay in state 3 until the client turns the
01327                         // port off.
01328                         if (port->maxOnTime_us() == 0)
01329                             remove = true;
01330                     }
01331                     else
01332                     {
01333                         // The port was switched off by the client during the
01334                         // minimum ON period.  We haven't passed the OFF state
01335                         // to the underlying port yet, because the port has to
01336                         // stay on throughout the minimum ON period.  So turn
01337                         // the port off now.
01338                         port->out->set(0);
01339                         
01340                         // return to state 0 (OFF)
01341                         port->state = 0;
01342 
01343                         // we're done with the timer
01344                         remove = true;
01345                     }
01346                 }
01347                 break;
01348                 
01349             case 3:  // between minimum ON time and maximum ON time
01350                 // check if the maximum ON time has expired
01351                 if (uint32_t(t - port->t0) > port->maxOnTime_us())
01352                 {
01353                     // The maximum ON time has expired.  Turn off the physical
01354                     // port.
01355                     port->out->set(0);
01356                     
01357                     // Switch to state 4 (logically ON past maximum time)
01358                     port->state = 4;
01359                     
01360                     // Remove the timer on this port.  This port simply stays
01361                     // in state 4 until the client turns off the port.
01362                     remove = true;
01363                 }
01364                 break;                
01365             }
01366             
01367             // if desired, remove the port from the timer list
01368             if (remove)
01369             {
01370                 // Remove the list entry by overwriting the slot with
01371                 // the last entry in the list.
01372                 pending[i] = pending[--nPending];
01373                 
01374                 // Note that we don't increment the loop counter, since
01375                 // we now need to revisit this same slot.
01376             }
01377             else
01378             {
01379                 // we're keeping this item; move on to the next one
01380                 ++i;
01381             }
01382         }
01383     }
01384 
01385 protected:
01386     // underlying physical output
01387     LwOut *out;
01388     
01389     // Timestamp on 'timer' of start of full-power interval.  We set this
01390     // to the current 'timer' timestamp when entering state 1.
01391     uint32_t t0;
01392 
01393     // Current port state:
01394     //
01395     //  0 = off
01396     //  1 = in initial minimum ON interval, logical port is on
01397     //  2 = in initial minimum ON interval, logical port is off
01398     //  3 = in interval between minimum and maximum ON times
01399     //  4 = after the maximum ON interval
01400     //
01401     // The "logical" on/off state of the port is the state set by the 
01402     // client.  The "physical" state is the state of the underlying port.
01403     // The relationships between logical and physical port state, and the 
01404     // effects of updates by the client, are as follows:
01405     //
01406     //    State | Logical | Physical | Client set on | Client set off
01407     //    -----------------------------------------------------------
01408     //      0   |   Off   |   Off    | phys on, -> 1 |   no effect
01409     //      1   |   On    |   On     |   no effect   |     -> 2
01410     //      2   |   Off   |   On     |     -> 1      |   no effect
01411     //      3   |   On    |   On     |   no effect   | phys off, -> 0
01412     //      4   |   On    |   On     |   no effect   | phys off, -> 0
01413     //      
01414     // The polling routine makes the following transitions when the current
01415     // time limit expires:
01416     //
01417     //   1: at end of minimum ON, -> 3 (or 4 if max == infinity)
01418     //   2: at end of minimum ON, port off, -> 0
01419     //   3: at end of maximum ON, port off, -> 4
01420     //
01421     uint8_t state;
01422     
01423     // Configuration parameters byte.  This encodes the minimum and maximum
01424     // ON times.
01425     uint8_t params;
01426     
01427     // Timer.  This is a shared timer for all of the minimum ON time ports.
01428     // When we transition from OFF to ON, we note the current time on this 
01429     // timer to establish the start of our minimum ON period.
01430     static Timer timer;
01431 
01432     // translaton table from timing parameter in config to minimum ON time
01433     static const uint32_t paramToTime_us[];
01434     
01435     // Figure the minimum ON time.  The minimum ON time is given by the
01436     // low-order 4 bits of the parameters byte, which serves as an index
01437     // into our time table.
01438     inline uint32_t minOnTime_us() const { return paramToTime_us[params & 0x0F]; }
01439     
01440     // Figure the maximum ON time.  The maximum time is the high 4 bits
01441     // of the parameters byte.  This is an index into our time table, but
01442     // 0 has the special meaning "infinite".
01443     inline uint32_t maxOnTime_us() const { return paramToTime_us[((params >> 4) & 0x0F)]; }
01444 
01445     // Pending timer list.  Whenever one of our ports transitions from OFF
01446     // to ON, we add it to this list.  We scan this list in our polling
01447     // routine to find ports that have reached the ends of their initial
01448     // ON intervals.
01449     static LwChimeLogicOut **pending;
01450     static uint8_t nPending;
01451 };
01452 
01453 // Min Time Out statics
01454 Timer LwChimeLogicOut::timer;
01455 LwChimeLogicOut **LwChimeLogicOut::pending;
01456 uint8_t LwChimeLogicOut::nPending;
01457 const uint32_t LwChimeLogicOut::paramToTime_us[] = {
01458     0,          // for the max time, this means "infinite"
01459     1000, 
01460     2000,
01461     5000, 
01462     10000, 
01463     20000, 
01464     40000, 
01465     80000, 
01466     100000, 
01467     200000, 
01468     300000, 
01469     400000, 
01470     500000, 
01471     600000, 
01472     700000, 
01473     800000
01474 };
01475 
01476 //
01477 // The TLC5940 interface object.  We'll set this up with the port 
01478 // assignments set in config.h.
01479 //
01480 TLC5940 *tlc5940 = 0;
01481 void init_tlc5940(Config &cfg)
01482 {
01483     if (cfg.tlc5940.nchips != 0)
01484     {
01485         tlc5940 = new TLC5940(
01486             wirePinName(cfg.tlc5940.sclk), 
01487             wirePinName(cfg.tlc5940.sin),
01488             wirePinName(cfg.tlc5940.gsclk),
01489             wirePinName(cfg.tlc5940.blank), 
01490             wirePinName(cfg.tlc5940.xlat), 
01491             cfg.tlc5940.nchips);
01492     }
01493 }
01494 
01495 // Conversion table for 8-bit DOF level to 12-bit TLC5940 level
01496 static const uint16_t dof_to_tlc[] = {
01497        0,   16,   32,   48,   64,   80,   96,  112,  128,  145,  161,  177,  193,  209,  225,  241, 
01498      257,  273,  289,  305,  321,  337,  353,  369,  385,  401,  418,  434,  450,  466,  482,  498, 
01499      514,  530,  546,  562,  578,  594,  610,  626,  642,  658,  674,  691,  707,  723,  739,  755, 
01500      771,  787,  803,  819,  835,  851,  867,  883,  899,  915,  931,  947,  964,  980,  996, 1012, 
01501     1028, 1044, 1060, 1076, 1092, 1108, 1124, 1140, 1156, 1172, 1188, 1204, 1220, 1237, 1253, 1269, 
01502     1285, 1301, 1317, 1333, 1349, 1365, 1381, 1397, 1413, 1429, 1445, 1461, 1477, 1493, 1510, 1526, 
01503     1542, 1558, 1574, 1590, 1606, 1622, 1638, 1654, 1670, 1686, 1702, 1718, 1734, 1750, 1766, 1783, 
01504     1799, 1815, 1831, 1847, 1863, 1879, 1895, 1911, 1927, 1943, 1959, 1975, 1991, 2007, 2023, 2039, 
01505     2056, 2072, 2088, 2104, 2120, 2136, 2152, 2168, 2184, 2200, 2216, 2232, 2248, 2264, 2280, 2296, 
01506     2312, 2329, 2345, 2361, 2377, 2393, 2409, 2425, 2441, 2457, 2473, 2489, 2505, 2521, 2537, 2553, 
01507     2569, 2585, 2602, 2618, 2634, 2650, 2666, 2682, 2698, 2714, 2730, 2746, 2762, 2778, 2794, 2810, 
01508     2826, 2842, 2858, 2875, 2891, 2907, 2923, 2939, 2955, 2971, 2987, 3003, 3019, 3035, 3051, 3067, 
01509     3083, 3099, 3115, 3131, 3148, 3164, 3180, 3196, 3212, 3228, 3244, 3260, 3276, 3292, 3308, 3324, 
01510     3340, 3356, 3372, 3388, 3404, 3421, 3437, 3453, 3469, 3485, 3501, 3517, 3533, 3549, 3565, 3581, 
01511     3597, 3613, 3629, 3645, 3661, 3677, 3694, 3710, 3726, 3742, 3758, 3774, 3790, 3806, 3822, 3838, 
01512     3854, 3870, 3886, 3902, 3918, 3934, 3950, 3967, 3983, 3999, 4015, 4031, 4047, 4063, 4079, 4095
01513 };
01514 
01515 // Conversion table for 8-bit DOF level to 12-bit TLC5940 level, with 
01516 // gamma correction.  Note that the output layering scheme can handle
01517 // this without a separate table, by first applying gamma to the DOF
01518 // level to produce an 8-bit gamma-corrected value, then convert that
01519 // to the 12-bit TLC5940 value.  But we get better precision by doing
01520 // the gamma correction in the 12-bit TLC5940 domain.  We can only
01521 // get the 12-bit domain by combining both steps into one layering
01522 // object, though, since the intermediate values in the layering system
01523 // are always 8 bits.
01524 static const uint16_t dof_to_gamma_tlc[] = {
01525       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   1,   1,   1,   1,   1, 
01526       2,   2,   2,   3,   3,   4,   4,   5,   5,   6,   7,   8,   8,   9,  10,  11, 
01527      12,  13,  15,  16,  17,  18,  20,  21,  23,  25,  26,  28,  30,  32,  34,  36, 
01528      38,  40,  43,  45,  48,  50,  53,  56,  59,  62,  65,  68,  71,  75,  78,  82, 
01529      85,  89,  93,  97, 101, 105, 110, 114, 119, 123, 128, 133, 138, 143, 149, 154, 
01530     159, 165, 171, 177, 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258, 
01531     266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360, 369, 379, 389, 399, 
01532     409, 419, 430, 440, 451, 462, 473, 485, 496, 508, 520, 532, 544, 556, 569, 582, 
01533     594, 608, 621, 634, 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811, 
01534     827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998, 1016, 1034, 1053, 1072, 1091, 
01535     1110, 1130, 1150, 1170, 1190, 1210, 1231, 1252, 1273, 1294, 1316, 1338, 1360, 1382, 1404, 1427, 
01536     1450, 1473, 1497, 1520, 1544, 1568, 1593, 1617, 1642, 1667, 1693, 1718, 1744, 1770, 1797, 1823, 
01537     1850, 1877, 1905, 1932, 1960, 1988, 2017, 2045, 2074, 2103, 2133, 2162, 2192, 2223, 2253, 2284, 
01538     2315, 2346, 2378, 2410, 2442, 2474, 2507, 2540, 2573, 2606, 2640, 2674, 2708, 2743, 2778, 2813, 
01539     2849, 2884, 2920, 2957, 2993, 3030, 3067, 3105, 3143, 3181, 3219, 3258, 3297, 3336, 3376, 3416, 
01540     3456, 3496, 3537, 3578, 3619, 3661, 3703, 3745, 3788, 3831, 3874, 3918, 3962, 4006, 4050, 4095
01541 };
01542 
01543 // LwOut class for TLC5940 outputs.  These are fully PWM capable.
01544 // The 'idx' value in the constructor is the output index in the
01545 // daisy-chained TLC5940 array.  0 is output #0 on the first chip,
01546 // 1 is #1 on the first chip, 15 is #15 on the first chip, 16 is
01547 // #0 on the second chip, 32 is #0 on the third chip, etc.
01548 class Lw5940Out: public LwOut
01549 {
01550 public:
01551     Lw5940Out(uint8_t idx) : idx(idx) { prv = 0; }
01552     virtual void set(uint8_t val)
01553     {
01554         if (val != prv)
01555            tlc5940->set(idx, dof_to_tlc[prv = val]);
01556     }
01557     uint8_t idx;
01558     uint8_t prv;
01559 };
01560 
01561 // LwOut class for TLC5940 gamma-corrected outputs.
01562 class Lw5940GammaOut: public LwOut
01563 {
01564 public:
01565     Lw5940GammaOut(uint8_t idx) : idx(idx) { prv = 0; }
01566     virtual void set(uint8_t val)
01567     {
01568         if (val != prv)
01569            tlc5940->set(idx, dof_to_gamma_tlc[prv = val]);
01570     }
01571     uint8_t idx;
01572     uint8_t prv;
01573 };
01574 
01575 //
01576 // TLC59116 interface object
01577 //
01578 TLC59116 *tlc59116 = 0;
01579 void init_tlc59116(Config &cfg)
01580 {
01581     // Create the interface if any chips are enabled
01582     if (cfg.tlc59116.chipMask != 0)
01583     {
01584         // set up the interface
01585         tlc59116 = new TLC59116(
01586             wirePinName(cfg.tlc59116.sda),
01587             wirePinName(cfg.tlc59116.scl),
01588             wirePinName(cfg.tlc59116.reset));
01589             
01590         // initialize the chips
01591         tlc59116->init();
01592     }
01593 }
01594 
01595 // LwOut class for TLC59116 outputs.  The 'addr' value in the constructor
01596 // is low 4 bits of the chip's I2C address; this is the part of the address
01597 // that's configurable per chip.  'port' is the output number on the chip
01598 // (0-15).
01599 //
01600 // Note that we don't need a separate gamma-corrected subclass for this
01601 // output type, since there's no loss of precision with the standard layered
01602 // gamma (it emits 8-bit values, and we take 8-bit inputs).
01603 class Lw59116Out: public LwOut
01604 {
01605 public:
01606     Lw59116Out(uint8_t addr, uint8_t port) : addr(addr), port(port) { prv = 0; }
01607     virtual void set(uint8_t val)
01608     {
01609         if (val != prv)
01610             tlc59116->set(addr, port, prv = val);
01611     }
01612     
01613 protected:
01614     uint8_t addr;
01615     uint8_t port;
01616     uint8_t prv;
01617 };
01618 
01619 
01620 //
01621 // 74HC595 interface object.  Set this up with the port assignments in
01622 // config.h.
01623 //
01624 HC595 *hc595 = 0;
01625 
01626 // initialize the 74HC595 interface
01627 void init_hc595(Config &cfg)
01628 {
01629     if (cfg.hc595.nchips != 0)
01630     {
01631         hc595 = new HC595(
01632             wirePinName(cfg.hc595.nchips), 
01633             wirePinName(cfg.hc595.sin), 
01634             wirePinName(cfg.hc595.sclk), 
01635             wirePinName(cfg.hc595.latch), 
01636             wirePinName(cfg.hc595.ena));
01637         hc595->init();
01638         hc595->update();
01639     }
01640 }
01641 
01642 // LwOut class for 74HC595 outputs.  These are simple digial outs.
01643 // The 'idx' value in the constructor is the output index in the
01644 // daisy-chained 74HC595 array.  0 is output #0 on the first chip,
01645 // 1 is #1 on the first chip, 7 is #7 on the first chip, 8 is
01646 // #0 on the second chip, etc.
01647 class Lw595Out: public LwOut
01648 {
01649 public:
01650     Lw595Out(uint8_t idx) : idx(idx) { prv = 0; }
01651     virtual void set(uint8_t val)
01652     {
01653         if (val != prv)
01654            hc595->set(idx, (prv = val) == 0 ? 0 : 1);
01655     }
01656     uint8_t idx;
01657     uint8_t prv;
01658 };
01659 
01660 
01661 
01662 // Conversion table - 8-bit DOF output level to PWM duty cycle,
01663 // normalized to 0.0 to 1.0 scale.
01664 static const float dof_to_pwm[] = {
01665     0.000000f, 0.003922f, 0.007843f, 0.011765f, 0.015686f, 0.019608f, 0.023529f, 0.027451f, 
01666     0.031373f, 0.035294f, 0.039216f, 0.043137f, 0.047059f, 0.050980f, 0.054902f, 0.058824f, 
01667     0.062745f, 0.066667f, 0.070588f, 0.074510f, 0.078431f, 0.082353f, 0.086275f, 0.090196f, 
01668     0.094118f, 0.098039f, 0.101961f, 0.105882f, 0.109804f, 0.113725f, 0.117647f, 0.121569f, 
01669     0.125490f, 0.129412f, 0.133333f, 0.137255f, 0.141176f, 0.145098f, 0.149020f, 0.152941f, 
01670     0.156863f, 0.160784f, 0.164706f, 0.168627f, 0.172549f, 0.176471f, 0.180392f, 0.184314f, 
01671     0.188235f, 0.192157f, 0.196078f, 0.200000f, 0.203922f, 0.207843f, 0.211765f, 0.215686f, 
01672     0.219608f, 0.223529f, 0.227451f, 0.231373f, 0.235294f, 0.239216f, 0.243137f, 0.247059f, 
01673     0.250980f, 0.254902f, 0.258824f, 0.262745f, 0.266667f, 0.270588f, 0.274510f, 0.278431f, 
01674     0.282353f, 0.286275f, 0.290196f, 0.294118f, 0.298039f, 0.301961f, 0.305882f, 0.309804f, 
01675     0.313725f, 0.317647f, 0.321569f, 0.325490f, 0.329412f, 0.333333f, 0.337255f, 0.341176f, 
01676     0.345098f, 0.349020f, 0.352941f, 0.356863f, 0.360784f, 0.364706f, 0.368627f, 0.372549f, 
01677     0.376471f, 0.380392f, 0.384314f, 0.388235f, 0.392157f, 0.396078f, 0.400000f, 0.403922f, 
01678     0.407843f, 0.411765f, 0.415686f, 0.419608f, 0.423529f, 0.427451f, 0.431373f, 0.435294f, 
01679     0.439216f, 0.443137f, 0.447059f, 0.450980f, 0.454902f, 0.458824f, 0.462745f, 0.466667f, 
01680     0.470588f, 0.474510f, 0.478431f, 0.482353f, 0.486275f, 0.490196f, 0.494118f, 0.498039f, 
01681     0.501961f, 0.505882f, 0.509804f, 0.513725f, 0.517647f, 0.521569f, 0.525490f, 0.529412f, 
01682     0.533333f, 0.537255f, 0.541176f, 0.545098f, 0.549020f, 0.552941f, 0.556863f, 0.560784f, 
01683     0.564706f, 0.568627f, 0.572549f, 0.576471f, 0.580392f, 0.584314f, 0.588235f, 0.592157f, 
01684     0.596078f, 0.600000f, 0.603922f, 0.607843f, 0.611765f, 0.615686f, 0.619608f, 0.623529f, 
01685     0.627451f, 0.631373f, 0.635294f, 0.639216f, 0.643137f, 0.647059f, 0.650980f, 0.654902f, 
01686     0.658824f, 0.662745f, 0.666667f, 0.670588f, 0.674510f, 0.678431f, 0.682353f, 0.686275f, 
01687     0.690196f, 0.694118f, 0.698039f, 0.701961f, 0.705882f, 0.709804f, 0.713725f, 0.717647f, 
01688     0.721569f, 0.725490f, 0.729412f, 0.733333f, 0.737255f, 0.741176f, 0.745098f, 0.749020f, 
01689     0.752941f, 0.756863f, 0.760784f, 0.764706f, 0.768627f, 0.772549f, 0.776471f, 0.780392f, 
01690     0.784314f, 0.788235f, 0.792157f, 0.796078f, 0.800000f, 0.803922f, 0.807843f, 0.811765f, 
01691     0.815686f, 0.819608f, 0.823529f, 0.827451f, 0.831373f, 0.835294f, 0.839216f, 0.843137f, 
01692     0.847059f, 0.850980f, 0.854902f, 0.858824f, 0.862745f, 0.866667f, 0.870588f, 0.874510f, 
01693     0.878431f, 0.882353f, 0.886275f, 0.890196f, 0.894118f, 0.898039f, 0.901961f, 0.905882f, 
01694     0.909804f, 0.913725f, 0.917647f, 0.921569f, 0.925490f, 0.929412f, 0.933333f, 0.937255f, 
01695     0.941176f, 0.945098f, 0.949020f, 0.952941f, 0.956863f, 0.960784f, 0.964706f, 0.968627f, 
01696     0.972549f, 0.976471f, 0.980392f, 0.984314f, 0.988235f, 0.992157f, 0.996078f, 1.000000f
01697 };
01698 
01699 
01700 // Conversion table for 8-bit DOF level to pulse width, with gamma correction
01701 // pre-calculated.  The values are normalized duty cycles from 0.0 to 1.0.
01702 // Note that we could use the layered gamma output on top of the regular 
01703 // LwPwmOut class for this instead of a separate table, but we get much better 
01704 // precision with a dedicated table, because we apply gamma correction to the
01705 // actual duty cycle values (as 'float') rather than the 8-bit DOF values.
01706 static const float dof_to_gamma_pwm[] = {
01707     0.000000f, 0.000000f, 0.000001f, 0.000004f, 0.000009f, 0.000017f, 0.000028f, 0.000042f,
01708     0.000062f, 0.000086f, 0.000115f, 0.000151f, 0.000192f, 0.000240f, 0.000296f, 0.000359f,
01709     0.000430f, 0.000509f, 0.000598f, 0.000695f, 0.000803f, 0.000920f, 0.001048f, 0.001187f,
01710     0.001337f, 0.001499f, 0.001673f, 0.001860f, 0.002059f, 0.002272f, 0.002498f, 0.002738f,
01711     0.002993f, 0.003262f, 0.003547f, 0.003847f, 0.004162f, 0.004494f, 0.004843f, 0.005208f,
01712     0.005591f, 0.005991f, 0.006409f, 0.006845f, 0.007301f, 0.007775f, 0.008268f, 0.008781f,
01713     0.009315f, 0.009868f, 0.010442f, 0.011038f, 0.011655f, 0.012293f, 0.012954f, 0.013637f,
01714     0.014342f, 0.015071f, 0.015823f, 0.016599f, 0.017398f, 0.018223f, 0.019071f, 0.019945f,
01715     0.020844f, 0.021769f, 0.022720f, 0.023697f, 0.024701f, 0.025731f, 0.026789f, 0.027875f,
01716     0.028988f, 0.030129f, 0.031299f, 0.032498f, 0.033726f, 0.034983f, 0.036270f, 0.037587f,
01717     0.038935f, 0.040313f, 0.041722f, 0.043162f, 0.044634f, 0.046138f, 0.047674f, 0.049243f,
01718     0.050844f, 0.052478f, 0.054146f, 0.055847f, 0.057583f, 0.059353f, 0.061157f, 0.062996f,
01719     0.064870f, 0.066780f, 0.068726f, 0.070708f, 0.072726f, 0.074780f, 0.076872f, 0.079001f,
01720     0.081167f, 0.083371f, 0.085614f, 0.087895f, 0.090214f, 0.092572f, 0.094970f, 0.097407f,
01721     0.099884f, 0.102402f, 0.104959f, 0.107558f, 0.110197f, 0.112878f, 0.115600f, 0.118364f,
01722     0.121170f, 0.124019f, 0.126910f, 0.129844f, 0.132821f, 0.135842f, 0.138907f, 0.142016f,
01723     0.145170f, 0.148367f, 0.151610f, 0.154898f, 0.158232f, 0.161611f, 0.165037f, 0.168509f,
01724     0.172027f, 0.175592f, 0.179205f, 0.182864f, 0.186572f, 0.190327f, 0.194131f, 0.197983f,
01725     0.201884f, 0.205834f, 0.209834f, 0.213883f, 0.217982f, 0.222131f, 0.226330f, 0.230581f,
01726     0.234882f, 0.239234f, 0.243638f, 0.248094f, 0.252602f, 0.257162f, 0.261774f, 0.266440f,
01727     0.271159f, 0.275931f, 0.280756f, 0.285636f, 0.290570f, 0.295558f, 0.300601f, 0.305699f,
01728     0.310852f, 0.316061f, 0.321325f, 0.326645f, 0.332022f, 0.337456f, 0.342946f, 0.348493f,
01729     0.354098f, 0.359760f, 0.365480f, 0.371258f, 0.377095f, 0.382990f, 0.388944f, 0.394958f,
01730     0.401030f, 0.407163f, 0.413356f, 0.419608f, 0.425921f, 0.432295f, 0.438730f, 0.445226f,
01731     0.451784f, 0.458404f, 0.465085f, 0.471829f, 0.478635f, 0.485504f, 0.492436f, 0.499432f,
01732     0.506491f, 0.513614f, 0.520800f, 0.528052f, 0.535367f, 0.542748f, 0.550194f, 0.557705f,
01733     0.565282f, 0.572924f, 0.580633f, 0.588408f, 0.596249f, 0.604158f, 0.612133f, 0.620176f,
01734     0.628287f, 0.636465f, 0.644712f, 0.653027f, 0.661410f, 0.669863f, 0.678384f, 0.686975f,
01735     0.695636f, 0.704366f, 0.713167f, 0.722038f, 0.730979f, 0.739992f, 0.749075f, 0.758230f,
01736     0.767457f, 0.776755f, 0.786126f, 0.795568f, 0.805084f, 0.814672f, 0.824334f, 0.834068f,
01737     0.843877f, 0.853759f, 0.863715f, 0.873746f, 0.883851f, 0.894031f, 0.904286f, 0.914616f,
01738     0.925022f, 0.935504f, 0.946062f, 0.956696f, 0.967407f, 0.978194f, 0.989058f, 1.000000f
01739 };
01740 
01741 // Polled-update PWM output list
01742 //
01743 // This is a workaround for a KL25Z hardware bug/limitation.  The bug (more
01744 // about this below) is that we can't write to a PWM output "value" register
01745 // more than once per PWM cycle; if we do, outputs after the first are lost.
01746 // The value register controls the duty cycle, so it's what you have to write
01747 // if you want to update the brightness of an output.
01748 //
01749 // The symptom of the problem, if it's not worked around somehow, is that 
01750 // an output will get "stuck" due to a missed write.  This is especially
01751 // noticeable during a series of updates such as a fade.  If the last
01752 // couple of updates in a fade are lost, the output will get stuck at some
01753 // value above or below the desired final value.  The stuck setting will
01754 // persist until the output is deliberately changed again later.
01755 //
01756 // Our solution:  Simply repeat all PWM updates periodically.  This way, any
01757 // lost write will *eventually* take hold on one of the repeats.  Repeats of
01758 // the same value won't change anything and thus won't be noticeable.  We do
01759 // these periodic updates during the main loop, which makes them very low 
01760 // overhead (there's no interrupt overhead; we just do them when convenient 
01761 // in the main loop), and also makes them very frequent.  The frequency 
01762 // is crucial because it ensures that updates will never be lost for long 
01763 // enough to become noticeable.
01764 //
01765 // The mbed library has its own, different solution to this bug, but the
01766 // mbed solution isn't really a solution at all because it creates a separate 
01767 // problem of its own.  The mbed approach is to reset the TPM "count" register
01768 // on every value register write.   The count reset truncates the current
01769 // PWM cycle, which bypasses the hardware problem.  Remember, the hardware
01770 // problem is that you can only write once per cycle; the mbed "solution" gets
01771 // around that by making sure the cycle ends immediately after the write.
01772 // The problem with this approach is that the truncated cycle causes visible 
01773 // flicker if the output is connected to an LED.  This is particularly 
01774 // noticeable during fades, when we're updating the value register repeatedly 
01775 // and rapidly: an attempt to fade from fully on to fully off causes rapid 
01776 // fluttering and flashing rather than a smooth brightness fade.  That's why
01777 // I had to come up with something different - the mbed solution just trades
01778 // one annoying bug for another that's just as bad.
01779 //
01780 // The hardware bug, by the way, is a case of good intentions gone bad.  
01781 // The whole point of the staging register is to make things easier for
01782 // us software writers.  In most PWM hardware, software has to coordinate
01783 // with the PWM duty cycle when updating registers to avoid a glitch that
01784 // you'd get by scribbling to the duty cycle register mid-cycle.  The
01785 // staging register solves this by letting the software write an update at
01786 // any time, knowing that the hardware will apply the update at exactly the
01787 // end of the cycle, ensuring glitch-free updates.  It's a great design,
01788 // except that it doesn't quite work.  The problem is that they implemented
01789 // this clever staging register as a one-element FIFO that refuses any more
01790 // writes when full.  That is, writing a value to the FIFO fills it; once
01791 // full, it ignores writes until it gets emptied out.  How's it emptied out?
01792 // By the hardware moving the staged value to the real register.  Sadly, they
01793 // didn't provide any way for the software to clear the register, and no way
01794 // to even tell that it's full.  So we don't have glitches on write, but we're
01795 // back to the original problem that the software has to be aware of the PWM
01796 // cycle timing, because the only way for the software to know that a write
01797 // actually worked is to know that it's been at least one PWM cycle since the
01798 // last write.  That largely defeats the whole purpose of the staging register,
01799 // since the whole point was to free software writers of these timing
01800 // considerations.  It's still an improvement over no staging register at
01801 // all, since we at least don't have to worry about glitches, but it leaves
01802 // us with this somewhat similar hassle.
01803 //
01804 // So here we have our list of PWM outputs that need to be polled for updates.
01805 // The KL25Z hardware only has 10 PWM channels, so we only need a fixed set
01806 // of polled items.
01807 static int numPolledPwm;
01808 static class LwPwmOut *polledPwm[10];
01809 
01810 // LwOut class for a PWM-capable GPIO port.
01811 class LwPwmOut: public LwOut
01812 {
01813 public:
01814     LwPwmOut(PinName pin, uint8_t initVal) : p(pin)
01815     {
01816         // add myself to the list of polled outputs for periodic updates
01817         if (numPolledPwm < countof(polledPwm))
01818             polledPwm[numPolledPwm++] = this;
01819             
01820         // IMPORTANT:  Do not set the PWM period (frequency) here explicitly.  
01821         // We instead want to accept the current setting for the TPM unit
01822         // we're assigned to.  The KL25Z hardware can only set the period at
01823         // the TPM unit level, not per channel, so if we changed the frequency
01824         // here, we'd change it for everything attached to our TPM unit.  LW
01825         // outputs don't care about frequency other than that it's fast enough
01826         // that attached LEDs won't flicker.  Some other PWM users (IR remote,
01827         // TLC5940) DO care about exact frequencies, because they use the PWM
01828         // as a signal generator rather than merely for brightness control.
01829         // If we changed the frequency here, we could clobber one of those
01830         // carefully chosen frequencies and break the other subsystem.  So
01831         // we need to be the "free variable" here and accept whatever setting
01832         // is currently on our assigned unit.  To minimize flicker, the main()
01833         // entrypoint sets a default PWM rate of 1kHz on all channels.  All
01834         // of the other subsystems that might set specific frequencies will
01835         // set much high frequencies, so that should only be good for us.
01836             
01837         // set the initial brightness value
01838         set(initVal);
01839     }
01840 
01841     virtual void set(uint8_t val) 
01842     {
01843         // save the new value
01844         this->val = val;
01845         
01846         // commit it to the hardware
01847         commit();
01848     }
01849 
01850     // handle periodic update polling
01851     void poll()
01852     {
01853         commit();
01854     }
01855 
01856 protected:
01857     virtual void commit()
01858     {
01859         // write the current value to the PWM controller if it's changed
01860         p.glitchFreeWrite(dof_to_pwm[val]);
01861     }
01862     
01863     NewPwmOut p;
01864     uint8_t val;
01865 };
01866 
01867 // Gamma corrected PWM GPIO output.  This works exactly like the regular
01868 // PWM output, but translates DOF values through the gamma-corrected
01869 // table instead of the regular linear table.
01870 class LwPwmGammaOut: public LwPwmOut
01871 {
01872 public:
01873     LwPwmGammaOut(PinName pin, uint8_t initVal)
01874         : LwPwmOut(pin, initVal)
01875     {
01876     }
01877     
01878 protected:
01879     virtual void commit()
01880     {
01881         // write the current value to the PWM controller if it's changed
01882         p.glitchFreeWrite(dof_to_gamma_pwm[val]);
01883     }
01884 };
01885 
01886 // poll the PWM outputs
01887 Timer polledPwmTimer;
01888 uint64_t polledPwmTotalTime, polledPwmRunCount;
01889 void pollPwmUpdates()
01890 {
01891     // If it's been long enough since the last update, do another update.
01892     // Note that the time limit is fairly arbitrary: it has to be at least
01893     // 1.5X the PWM period, so that we can be sure that at least one PWM
01894     // period has elapsed since the last update, but there's no hard upper
01895     // bound.  Instead, it only has to be short enough that fades don't
01896     // become noticeably chunky.  The competing interest is that we don't 
01897     // want to do this more often than necessary to provide incremental
01898     // benefit, because the polling adds overhead to the main loop and
01899     // takes time away from other tasks we could be performing.  The
01900     // shortest time with practical benefit is probably around 50-60Hz,
01901     // since that gives us "video rate" granularity in fades.  Anything
01902     // faster wouldn't probably make fades look any smoother to a human 
01903     // viewer.
01904     if (polledPwmTimer.read_us() >= 15000)
01905     {
01906         // time the run for statistics collection
01907         IF_DIAG(
01908           Timer t; 
01909           t.start();
01910         )
01911         
01912         // poll each output
01913         for (int i = numPolledPwm ; i > 0 ; )
01914             polledPwm[--i]->poll();
01915         
01916         // reset the timer for the next cycle
01917         polledPwmTimer.reset();
01918         
01919         // collect statistics
01920         IF_DIAG(
01921           polledPwmTotalTime += t.read_us();
01922           polledPwmRunCount += 1;
01923         )
01924     }
01925 }
01926 
01927 // LwOut class for a Digital-Only (Non-PWM) GPIO port
01928 class LwDigOut: public LwOut
01929 {
01930 public:
01931     LwDigOut(PinName pin, uint8_t initVal) : p(pin, initVal ? 1 : 0) { prv = initVal; }
01932     virtual void set(uint8_t val) 
01933     {
01934          if (val != prv)
01935             p.write((prv = val) == 0 ? 0 : 1); 
01936     }
01937     DigitalOut p;
01938     uint8_t prv;
01939 };
01940 
01941 // Array of output physical pin assignments.  This array is indexed
01942 // by LedWiz logical port number - lwPin[n] is the maping for LedWiz
01943 // port n (0-based).  
01944 //
01945 // Each pin is handled by an interface object for the physical output 
01946 // type for the port, as set in the configuration.  The interface 
01947 // objects handle the specifics of addressing the different hardware
01948 // types (GPIO PWM ports, GPIO digital ports, TLC5940 ports, and
01949 // 74HC595 ports).
01950 static int numOutputs;
01951 static LwOut **lwPin;
01952 
01953 // create a single output pin
01954 LwOut *createLwPin(int portno, LedWizPortCfg &pc, Config &cfg)
01955 {
01956     // get this item's values
01957     int typ = pc.typ;
01958     int pin = pc.pin;
01959     int flags = pc.flags;
01960     int noisy = flags & PortFlagNoisemaker;
01961     int activeLow = flags & PortFlagActiveLow;
01962     int gamma = flags & PortFlagGamma;
01963     int flipperLogic = flags & PortFlagFlipperLogic;
01964     int chimeLogic = flags & PortFlagChimeLogic;
01965     
01966     // cancel gamma on flipper logic ports
01967     if (flipperLogic)
01968         gamma = false;
01969 
01970     // create the pin interface object according to the port type        
01971     LwOut *lwp;
01972     switch (typ)
01973     {
01974     case PortTypeGPIOPWM:
01975         // PWM GPIO port - assign if we have a valid pin
01976         if (pin != 0)
01977         {
01978             // If gamma correction is to be used, and we're not inverting the output,
01979             // use the combined Pwmout + Gamma output class; otherwise use the plain
01980             // PwmOut class.  We can't use the combined class for inverted outputs
01981             // because we have to apply gamma correction before the inversion.
01982             if (gamma && !activeLow)
01983             {
01984                 // use the gamma-corrected PwmOut type
01985                 lwp = new LwPwmGammaOut(wirePinName(pin), 0);
01986                 
01987                 // don't apply further gamma correction to this output
01988                 gamma = false;
01989             }
01990             else
01991             {
01992                 // no gamma correction - use the standard PwmOut class
01993                 lwp = new LwPwmOut(wirePinName(pin), activeLow ? 255 : 0);
01994             }
01995         }
01996         else
01997             lwp = new LwVirtualOut();
01998         break;
01999     
02000     case PortTypeGPIODig:
02001         // Digital GPIO port
02002         if (pin != 0)
02003             lwp = new LwDigOut(wirePinName(pin), activeLow ? 255 : 0);
02004         else
02005             lwp = new LwVirtualOut();
02006         break;
02007     
02008     case PortTypeTLC5940:
02009         // TLC5940 port (if we don't have a TLC controller object, or it's not a valid
02010         // output port number on the chips we have, create a virtual port)
02011         if (tlc5940 != 0 && pin < cfg.tlc5940.nchips*16)
02012         {
02013             // If gamma correction is to be used, and we're not inverting the output,
02014             // use the combined TLC4950 + Gamma output class.  Otherwise use the plain 
02015             // TLC5940 output.  We skip the combined class if the output is inverted
02016             // because we need to apply gamma BEFORE the inversion to get the right
02017             // results, but the combined class would apply it after because of the
02018             // layering scheme - the combined class is a physical device output class,
02019             // and a physical device output class is necessarily at the bottom of 
02020             // the stack.  We don't have a combined inverted+gamma+TLC class, because
02021             // inversion isn't recommended for TLC5940 chips in the first place, so
02022             // it's not worth the extra memory footprint to have a dedicated table
02023             // for this unlikely case.
02024             if (gamma && !activeLow)
02025             {
02026                 // use the gamma-corrected 5940 output mapper
02027                 lwp = new Lw5940GammaOut(pin);
02028                 
02029                 // DON'T apply further gamma correction to this output
02030                 gamma = false;
02031             }
02032             else
02033             {
02034                 // no gamma - use the plain (linear) 5940 output class
02035                 lwp = new Lw5940Out(pin);
02036             }
02037         }
02038         else
02039         {
02040             // no TLC5940 chips, or invalid port number - use a virtual out
02041             lwp = new LwVirtualOut();
02042         }
02043         break;
02044     
02045     case PortType74HC595:
02046         // 74HC595 port (if we don't have an HC595 controller object, or it's not 
02047         // a valid output number, create a virtual port)
02048         if (hc595 != 0 && pin < cfg.hc595.nchips*8)
02049             lwp = new Lw595Out(pin);
02050         else
02051             lwp = new LwVirtualOut();
02052         break;
02053         
02054     case PortTypeTLC59116:
02055         // TLC59116 port.  The pin number in the config encodes the chip address
02056         // in the high 4 bits and the output number on the chip in the low 4 bits.
02057         // There's no gamma-corrected version of this output handler, so we don't
02058         // need to worry about that here; just use the layered gamma as needed.
02059         if (tlc59116 != 0)
02060             lwp = new Lw59116Out((pin >> 4) & 0x0F, pin & 0x0F);
02061         break;
02062 
02063     case PortTypeVirtual:
02064     case PortTypeDisabled:
02065     default:
02066         // virtual or unknown
02067         lwp = new LwVirtualOut();
02068         break;
02069     }
02070     
02071     // If it's Active Low, layer on an inverter.  Note that an inverter
02072     // needs to be the bottom-most layer, since all of the other filters
02073     // assume that they're working with normal (non-inverted) values.
02074     if (activeLow)
02075         lwp = new LwInvertedOut(lwp);
02076         
02077     // Layer on Flipper Logic if desired
02078     if (flipperLogic)
02079         lwp = new LwFlipperLogicOut(lwp, pc.flipperLogic);
02080         
02081     // Layer on Chime Logic if desired.  Note that Chime Logic and
02082     // Flipper Logic are mutually exclusive, and Flipper Logic takes
02083     // precedence, so ignore the Chime Logic bit if both are set.
02084     if (chimeLogic && !flipperLogic)
02085         lwp = new LwChimeLogicOut(lwp, pc.flipperLogic);
02086         
02087     // If it's a noisemaker, layer on a night mode switch
02088     if (noisy)
02089         lwp = new LwNoisyOut(lwp);
02090         
02091     // If it's gamma-corrected, layer on a gamma corrector
02092     if (gamma)
02093         lwp = new LwGammaOut(lwp);
02094         
02095     // If this is the ZB Launch Ball port, layer a monitor object.  Note
02096     // that the nominal port numbering in the config starts at 1, but we're
02097     // using an array index, so test against portno+1.
02098     if (portno + 1 == cfg.plunger.zbLaunchBall.port)
02099         lwp = new LwZbLaunchOut(lwp);
02100         
02101     // If this is the Night Mode indicator port, layer a night mode object.
02102     if (portno + 1 == cfg.nightMode.port)
02103         lwp = new LwNightModeIndicatorOut(lwp);
02104 
02105     // turn it off initially      
02106     lwp->set(0);
02107     
02108     // return the pin
02109     return lwp;
02110 }
02111 
02112 // initialize the output pin array
02113 void initLwOut(Config &cfg)
02114 {
02115     // Initialize the Flipper Logic and Chime Logic outputs
02116     LwFlipperLogicOut::classInit(cfg);
02117     LwChimeLogicOut::classInit(cfg);
02118 
02119     // Count the outputs.  The first disabled output determines the
02120     // total number of ports.
02121     numOutputs = MAX_OUT_PORTS;
02122     int i;
02123     for (i = 0 ; i < MAX_OUT_PORTS ; ++i)
02124     {
02125         if (cfg.outPort[i].typ == PortTypeDisabled)
02126         {
02127             numOutputs = i;
02128             break;
02129         }
02130     }
02131     
02132     // allocate the pin array
02133     lwPin = new LwOut*[numOutputs];
02134     
02135     // Allocate the current brightness array
02136     outLevel = new uint8_t[numOutputs];
02137     
02138     // allocate the LedWiz output state arrays
02139     wizOn = new uint8_t[numOutputs];
02140     wizVal = new uint8_t[numOutputs];
02141     
02142     // initialize all LedWiz outputs to off and brightness 48
02143     memset(wizOn, 0, numOutputs);
02144     memset(wizVal, 48, numOutputs);
02145     
02146     // set all LedWiz virtual unit flash speeds to 2
02147     for (i = 0 ; i < countof(wizSpeed) ; ++i)
02148         wizSpeed[i] = 2;
02149     
02150     // create the pin interface object for each port
02151     for (i = 0 ; i < numOutputs ; ++i)
02152         lwPin[i] = createLwPin(i, cfg.outPort[i], cfg);
02153 }
02154 
02155 // Translate an LedWiz brightness level (0..49) to a DOF brightness
02156 // level (0..255).  Note that brightness level 49 isn't actually valid,
02157 // according to the LedWiz API documentation, but many clients use it
02158 // anyway, and the real LedWiz accepts it and seems to treat it as 
02159 // equivalent to 48.
02160 static const uint8_t lw_to_dof[] = {
02161        0,    5,   11,   16,   21,   27,   32,   37, 
02162       43,   48,   53,   58,   64,   69,   74,   80, 
02163       85,   90,   96,  101,  106,  112,  117,  122, 
02164      128,  133,  138,  143,  149,  154,  159,  165, 
02165      170,  175,  181,  186,  191,  197,  202,  207, 
02166      213,  218,  223,  228,  234,  239,  244,  250, 
02167      255,  255
02168 };
02169 
02170 // Translate a DOF brightness level (0..255) to an LedWiz brightness
02171 // level (1..48)
02172 static const uint8_t dof_to_lw[] = {
02173      1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  3,  3,
02174      3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,  5,  5,  5,  6,  6,
02175      6,  6,  6,  7,  7,  7,  7,  7,  8,  8,  8,  8,  8,  8,  9,  9,
02176      9,  9,  9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12,
02177     12, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 15, 15,
02178     15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18,
02179     18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21,
02180     21, 21, 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24,
02181     24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27,
02182     27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 30, 30, 30,
02183     30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 33, 33,
02184     33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36,
02185     36, 36, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39,
02186     39, 39, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 42, 42,
02187     42, 42, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45,
02188     45, 45, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48
02189 };
02190 
02191 // LedWiz flash cycle tables.  For efficiency, we use a lookup table
02192 // rather than calculating these on the fly.  The flash cycles are
02193 // generated by the following formulas, where 'c' is the current
02194 // cycle counter, from 0 to 255:
02195 //
02196 //  mode 129 = sawtooth = (c < 128 ? c*2 + 1 : (255-c)*2)
02197 //  mode 130 = flash on/off = (c < 128 ? 255 : 0)
02198 //  mode 131 = on/ramp down = (c < 128 ? 255 : (255-c)*2)
02199 //  mode 132 = ramp up/on = (c < 128 ? c*2 : 255)
02200 //
02201 // To look up the current output value for a given mode and a given
02202 // cycle counter 'c', index the table with ((mode-129)*256)+c.
02203 static const uint8_t wizFlashLookup[] = {
02204     // mode 129 = sawtooth = (c < 128 ? c*2 + 1 : (255-c)*2)
02205     0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f,
02206     0x21, 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x39, 0x3b, 0x3d, 0x3f,
02207     0x41, 0x43, 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53, 0x55, 0x57, 0x59, 0x5b, 0x5d, 0x5f,
02208     0x61, 0x63, 0x65, 0x67, 0x69, 0x6b, 0x6d, 0x6f, 0x71, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d, 0x7f,
02209     0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9f,
02210     0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb1, 0xb3, 0xb5, 0xb7, 0xb9, 0xbb, 0xbd, 0xbf,
02211     0xc1, 0xc3, 0xc5, 0xc7, 0xc9, 0xcb, 0xcd, 0xcf, 0xd1, 0xd3, 0xd5, 0xd7, 0xd9, 0xdb, 0xdd, 0xdf,
02212     0xe1, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb, 0xed, 0xef, 0xf1, 0xf3, 0xf5, 0xf7, 0xf9, 0xfb, 0xfd, 0xff,
02213     0xfe, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0,
02214     0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0,
02215     0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0,
02216     0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92, 0x90, 0x8e, 0x8c, 0x8a, 0x88, 0x86, 0x84, 0x82, 0x80,
02217     0x7e, 0x7c, 0x7a, 0x78, 0x76, 0x74, 0x72, 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62, 0x60,
02218     0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40,
02219     0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20,
02220     0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00,
02221 
02222     // mode 130 = flash on/off = (c < 128 ? 255 : 0)
02223     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02224     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02225     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02226     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02227     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02228     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02229     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02230     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02231     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02232     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02233     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02234     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02235     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02236     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02237     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02238     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
02239 
02240     // mode 131 = on/ramp down = c < 128 ? 255 : (255 - c)*2
02241     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02242     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02243     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02244     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02245     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02246     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02247     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02248     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02249     0xfe, 0xfc, 0xfa, 0xf8, 0xf6, 0xf4, 0xf2, 0xf0, 0xee, 0xec, 0xea, 0xe8, 0xe6, 0xe4, 0xe2, 0xe0,
02250     0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xd0, 0xce, 0xcc, 0xca, 0xc8, 0xc6, 0xc4, 0xc2, 0xc0,
02251     0xbe, 0xbc, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xb0, 0xae, 0xac, 0xaa, 0xa8, 0xa6, 0xa4, 0xa2, 0xa0,
02252     0x9e, 0x9c, 0x9a, 0x98, 0x96, 0x94, 0x92, 0x90, 0x8e, 0x8c, 0x8a, 0x88, 0x86, 0x84, 0x82, 0x80,
02253     0x7e, 0x7c, 0x7a, 0x78, 0x76, 0x74, 0x72, 0x70, 0x6e, 0x6c, 0x6a, 0x68, 0x66, 0x64, 0x62, 0x60,
02254     0x5e, 0x5c, 0x5a, 0x58, 0x56, 0x54, 0x52, 0x50, 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x42, 0x40,
02255     0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20,
02256     0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x14, 0x12, 0x10, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00,
02257 
02258     // mode 132 = ramp up/on = c < 128 ? c*2 : 255
02259     0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
02260     0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
02261     0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
02262     0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
02263     0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
02264     0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
02265     0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
02266     0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
02267     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02268     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02269     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02270     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02271     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02272     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02273     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
02274     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
02275 };
02276 
02277 // LedWiz flash cycle timer.  This runs continuously.  On each update,
02278 // we use this to figure out where we are on the cycle for each bank.
02279 Timer wizCycleTimer;
02280 
02281 // timing statistics for wizPulse()
02282 uint64_t wizPulseTotalTime, wizPulseRunCount;
02283 
02284 // LedWiz flash timer pulse.  The main loop calls this on each cycle
02285 // to update outputs using LedWiz flash modes.  We do one bank of 32
02286 // outputs on each cycle.
02287 static void wizPulse()
02288 {
02289     // current bank
02290     static int wizPulseBank = 0;
02291 
02292     // start a timer for statistics collection
02293     IF_DIAG(
02294       Timer t;
02295       t.start();
02296     )
02297 
02298     // Update the current bank's cycle counter: figure the current
02299     // phase of the LedWiz pulse cycle for this bank.
02300     //
02301     // The LedWiz speed setting gives the flash period in 0.25s units
02302     // (speed 1 is a flash period of .25s, speed 7 is a period of 1.75s).
02303     //
02304     // What we're after here is the "phase", which is to say the point
02305     // in the current cycle.  If we assume that the cycle has been running
02306     // continuously since some arbitrary time zero in the past, we can
02307     // figure where we are in the current cycle by dividing the time since
02308     // that zero by the cycle period and taking the remainder.  E.g., if
02309     // the cycle time is 5 seconds, and the time since t-zero is 17 seconds,
02310     // we divide 17 by 5 to get a remainder of 2.  That says we're 2 seconds
02311     // into the current 5-second cycle, or 2/5 of the way through the
02312     // current cycle.
02313     //
02314     // We do this calculation on every iteration of the main loop, so we 
02315     // want it to be very fast.  To streamline it, we'll use some tricky
02316     // integer arithmetic.  The result will be the same as the straightforward
02317     // remainder and fraction calculation we just explained, but we'll get
02318     // there by less-than-obvious means.
02319     //
02320     // Rather than finding the phase as a continuous quantity or floating
02321     // point number, we'll quantize it.  We'll divide each cycle into 256 
02322     // time units, or quanta.  Each quantum is 1/256 of the cycle length,
02323     // so for a 1-second cycle (LedWiz speed 4), each quantum is 1/256 of 
02324     // a second, or about 3.9ms.  If we express the time since t-zero in
02325     // these units, the time period of one cycle is exactly 256 units, so
02326     // we can calculate our point in the cycle by taking the remainder of
02327     // the time (in our funny units) divided by 256.  The special thing
02328     // about making the cycle time equal to 256 units is that "x % 256" 
02329     // is exactly the same as "x & 255", which is a much faster operation
02330     // than division on ARM M0+: this CPU has no hardware DIVIDE operation,
02331     // so an integer division takes about 5us.  The bit mask operation, in 
02332     // contrast, takes only about 60ns - about 100x faster.  5us doesn't
02333     // sound like much, but we do this on every main loop, so every little
02334     // bit counts.  
02335     //
02336     // The snag is that our system timer gives us the elapsed time in
02337     // microseconds.  We still need to convert this to our special quanta
02338     // of 256 units per cycle.  The straightforward way to do that is by
02339     // dividing by (microseconds per quantum).  E.g., for LedWiz speed 4,
02340     // we decided that our quantum was 1/256 of a second, or 3906us, so
02341     // dividing the current system time in microseconds by 3906 will give
02342     // us the time in our quantum units.  But now we've just substituted
02343     // one division for another!
02344     //
02345     // This is where our really tricky integer math comes in.  Dividing
02346     // by X is the same as multiplying by 1/X.  In integer math, 1/3906
02347     // is zero, so that won't work.  But we can get around that by doing
02348     // the integer math as "fixed point" arithmetic instead.  It's still
02349     // actually carried out as integer operations, but we'll scale our
02350     // integers by a scaling factor, then take out the scaling factor
02351     // later to get the final result.  The scaling factor we'll use is
02352     // 2^24.  So we're going to calculate (time * 2^24/3906), then divide
02353     // the result by 2^24 to get the final answer.  I know it seems like 
02354     // we're substituting one division for another yet again, but this 
02355     // time's the charm, because dividing by 2^24 is a bit shift operation,
02356     // which is another single-cycle operation on M0+.  You might also
02357     // wonder how all these tricks don't cause overflows or underflows
02358     // or what not.  Well, the multiply by 2^24/3906 will cause an
02359     // overflow, but we don't care, because the overflow will all be in
02360     // the high-order bits that we're going to discard in the final 
02361     // remainder calculation anyway.
02362     //
02363     // Each entry in the array below represents 2^24/N for the corresponding
02364     // LedWiz speed, where N is the number of time quanta per cycle at that
02365     // speed.  The time quanta are chosen such that 256 quanta add up to 
02366     // approximately (LedWiz speed setting * 0.25s).
02367     // 
02368     // Note that the calculation has an implicit bit mask (result & 0xFF)
02369     // to get the final result mod 256.  But we don't have to actually
02370     // do that work because we're using 32-bit ints and a 2^24 fixed
02371     // point base (X in the narrative above).  The final shift right by
02372     // 24 bits to divide out the base will leave us with only 8 bits in
02373     // the result, since we started with 32.
02374     static const uint32_t inv_us_per_quantum[] = { // indexed by LedWiz speed
02375         0, 17172, 8590, 5726, 4295, 3436, 2863, 2454
02376     };
02377     int counter = ((wizCycleTimer.read_us() * inv_us_per_quantum[wizSpeed[wizPulseBank]]) >> 24);
02378         
02379     // get the range of 32 output sin this bank
02380     int fromPort = wizPulseBank*32;
02381     int toPort = fromPort+32;
02382     if (toPort > numOutputs)
02383         toPort = numOutputs;
02384         
02385     // update all outputs set to flashing values
02386     for (int i = fromPort ; i < toPort ; ++i)
02387     {
02388         // Update the port only if the LedWiz SBA switch for the port is on
02389         // (wizOn[i]) AND the port is a PBA flash mode in the range 129..132.
02390         // These modes and only these modes have the high bit (0x80) set, so
02391         // we can test for them simply by testing the high bit.
02392         if (wizOn[i])
02393         {
02394             uint8_t val = wizVal[i];
02395             if ((val & 0x80) != 0)
02396             {
02397                 // ook up the value for the mode at the cycle time
02398                 lwPin[i]->set(outLevel[i] = wizFlashLookup[((val-129) << 8) + counter]);
02399             }
02400         }
02401     }
02402         
02403     // flush changes to 74HC595 chips, if attached
02404     if (hc595 != 0)
02405         hc595->update();
02406         
02407     // switch to the next bank
02408     if (++wizPulseBank >= MAX_LW_BANKS)
02409         wizPulseBank = 0;
02410 
02411     // collect timing statistics
02412     IF_DIAG(
02413       wizPulseTotalTime += t.read_us();
02414       wizPulseRunCount += 1;
02415     )
02416 }
02417 
02418 // Update a port to reflect its new LedWiz SBA+PBA setting.
02419 static void updateLwPort(int port)
02420 {
02421     // check if the SBA switch is on or off
02422     if (wizOn[port])
02423     {
02424         // It's on.  If the port is a valid static brightness level,
02425         // set the output port to match.  Otherwise leave it as is:
02426         // if it's a flashing mode, the flash mode pulse will update
02427         // it on the next cycle.
02428         int val = wizVal[port];
02429         if (val <= 49)
02430             lwPin[port]->set(outLevel[port] = lw_to_dof[val]);
02431     }
02432     else
02433     {
02434         // the port is off - set absolute brightness zero
02435         lwPin[port]->set(outLevel[port] = 0);
02436     }
02437 }
02438 
02439 // Turn off all outputs and restore everything to the default LedWiz
02440 // state.  This sets all outputs to LedWiz profile value 48 (full
02441 // brightness) and switch state Off, and sets the LedWiz flash rate 
02442 // to 2.  This effectively restores the power-on conditions.
02443 //
02444 void allOutputsOff()
02445 {
02446     // reset all outputs to OFF/48
02447     for (int i = 0 ; i < numOutputs ; ++i)
02448     {
02449         outLevel[i] = 0;
02450         wizOn[i] = 0;
02451         wizVal[i] = 48;
02452         lwPin[i]->set(0);
02453     }
02454     
02455     // restore default LedWiz flash rate
02456     for (int i = 0 ; i < countof(wizSpeed) ; ++i)
02457         wizSpeed[i] = 2;
02458         
02459     // flush changes to hc595, if applicable
02460     if (hc595 != 0)
02461         hc595->update();
02462 }
02463 
02464 // Cary out an SBA or SBX message.  portGroup is 0 for ports 1-32,
02465 // 1 for ports 33-64, etc.  Original protocol SBA messages always
02466 // address port group 0; our private SBX extension messages can 
02467 // address any port group.
02468 void sba_sbx(int portGroup, const uint8_t *data)
02469 {
02470     // update all on/off states in the group
02471     for (int i = 0, bit = 1, imsg = 1, port = portGroup*32 ; 
02472          i < 32 && port < numOutputs ; 
02473          ++i, bit <<= 1, ++port)
02474     {
02475         // figure the on/off state bit for this output
02476         if (bit == 0x100) {
02477             bit = 1;
02478             ++imsg;
02479         }
02480         
02481         // set the on/off state
02482         bool on = wizOn[port] = ((data[imsg] & bit) != 0);
02483         
02484         // set the output port brightness to match the new setting
02485         updateLwPort(port);
02486     }
02487     
02488     // set the flash speed for the port group
02489     if (portGroup < countof(wizSpeed))
02490         wizSpeed[portGroup] = (data[5] < 1 ? 1 : data[5] > 7 ? 7 : data[5]);
02491 
02492     // update 74HC959 outputs
02493     if (hc595 != 0)
02494         hc595->update();
02495 }
02496 
02497 // Carry out a PBA or PBX message.
02498 void pba_pbx(int basePort, const uint8_t *data)
02499 {
02500     // update each wizVal entry from the brightness data
02501     for (int i = 0, port = basePort ; i < 8 && port < numOutputs ; ++i, ++port)
02502     {
02503         // get the value
02504         uint8_t v = data[i];
02505         
02506         // Validate it.  The legal values are 0..49 for brightness
02507         // levels, and 128..132 for flash modes.  Set anything invalid
02508         // to full brightness (48) instead.  Note that 49 isn't actually
02509         // a valid documented value, but in practice some clients send
02510         // this to mean 100% brightness, and the real LedWiz treats it
02511         // as such.
02512         if ((v > 49 && v < 129) || v > 132)
02513             v = 48;
02514         
02515         // store it
02516         wizVal[port] = v;
02517         
02518         // update the port
02519         updateLwPort(port);
02520     }
02521 
02522     // update 74HC595 outputs
02523     if (hc595 != 0)
02524         hc595->update();
02525 }
02526 
02527 // ---------------------------------------------------------------------------
02528 //
02529 // IR Remote Control transmitter & receiver
02530 //
02531 
02532 // receiver
02533 IRReceiver *ir_rx;
02534 
02535 // transmitter
02536 IRTransmitter *ir_tx;
02537 
02538 // Mapping from IR commands slots in the configuration to "virtual button"
02539 // numbers on the IRTransmitter's "virtual remote".  To minimize RAM usage, 
02540 // we only create virtual buttons on the transmitter object for code slots 
02541 // that are configured for transmission, which includes slots used for TV
02542 // ON commands and slots that can be triggered by button presses.  This
02543 // means that virtual button numbers won't necessarily match the config
02544 // slot numbers.  This table provides the mapping:
02545 // IRConfigSlotToVirtualButton[n] = ir_tx virtual button number for 
02546 // configuration slot n
02547 uint8_t IRConfigSlotToVirtualButton[MAX_IR_CODES];
02548 
02549 // IR transmitter virtual button number for ad hoc IR command.  We allocate 
02550 // one virtual button for sending ad hoc IR codes, such as through the USB
02551 // protocol.
02552 uint8_t IRAdHocBtn;
02553 
02554 // Staging area for ad hoc IR commands.  It takes multiple messages
02555 // to fill out an IR command, so we store the partial command here
02556 // while waiting for the rest.
02557 static struct
02558 {
02559     uint8_t protocol;       // protocol ID
02560     uint64_t code;          // code
02561     uint8_t dittos : 1;     // using dittos?
02562     uint8_t ready : 1;      // do we have a code ready to transmit?    
02563 } IRAdHocCmd;
02564 
02565 
02566 // IR mode timer.  In normal mode, this is the time since the last
02567 // command received; we use this to handle commands with timed effects,
02568 // such as sending a key to the PC.  In learning mode, this is the time
02569 // since we activated learning mode, which we use to automatically end
02570 // learning mode if a decodable command isn't received within a reasonable
02571 // amount of time.
02572 Timer IRTimer;
02573 
02574 // IR Learning Mode.  The PC enters learning mode via special function 65 12.
02575 // The states are:
02576 //
02577 //   0 -> normal operation (not in learning mode)
02578 //   1 -> learning mode; reading raw codes, no command read yet
02579 //   2 -> learning mode; command received, awaiting auto-repeat
02580 //   3 -> learning mode; done, command and repeat mode decoded
02581 //
02582 // When we enter learning mode, we reset IRTimer to keep track of how long
02583 // we've been in the mode.  This allows the mode to time out if no code is
02584 // received within a reasonable time.
02585 uint8_t IRLearningMode = 0;
02586 
02587 // Learning mode command received.  This stores the first decoded command
02588 // when in learning mode.  For some protocols, we can't just report the
02589 // first command we receive, because we need to wait for an auto-repeat to
02590 // determine what format the remote uses for repeats.  This stores the first
02591 // command while we await a repeat.  This is necessary for protocols that 
02592 // have "dittos", since some remotes for such protocols use the dittos and 
02593 // some don't; the only way to find out is to read a repeat code and see if 
02594 // it's a ditto or just a repeat of the full code.
02595 IRCommand learnedIRCode;
02596 
02597 // IR command received, as a config slot index, 1..MAX_IR_CODES.
02598 // When we receive a command that matches one of our programmed commands, 
02599 // we note the slot here.  We also reset the IR timer so that we know how 
02600 // long it's been since the command came in.  This lets us handle commands 
02601 // with timed effects, such as PC key input.  Note that this is a 1-based 
02602 // index; 0 represents no command.
02603 uint8_t IRCommandIn = 0;
02604 
02605 // "Toggle bit" of last command.  Some IR protocols have a toggle bit
02606 // that distinguishes an auto-repeating key from a key being pressed
02607 // several times in a row.  This records the toggle bit of the last
02608 // command we received.
02609 uint8_t lastIRToggle = 0;
02610 
02611 // Are we in a gap between successive key presses?  When we detect that a 
02612 // key is being pressed multiple times rather than auto-repeated (which we 
02613 // can detect via a toggle bit in some protocols), we'll briefly stop sending 
02614 // the associated key to the PC, so that the PC likewise recognizes the 
02615 // distinct key press.  
02616 uint8_t IRKeyGap = false;
02617 
02618 
02619 // initialize
02620 void init_IR(Config &cfg, bool &kbKeys)
02621 {
02622     PinName pin;
02623     
02624     // start the IR timer
02625     IRTimer.start();
02626     
02627     // if there's a transmitter, set it up
02628     if ((pin = wirePinName(cfg.IR.emitter)) != NC)
02629     {
02630         // no virtual buttons yet
02631         int nVirtualButtons = 0;
02632         memset(IRConfigSlotToVirtualButton, 0xFF, sizeof(IRConfigSlotToVirtualButton));
02633         
02634         // assign virtual buttons slots for TV ON codes
02635         for (int i = 0 ; i < MAX_IR_CODES ; ++i)
02636         {
02637             if ((cfg.IRCommand[i].flags & IRFlagTVON) != 0)
02638                 IRConfigSlotToVirtualButton[i] = nVirtualButtons++;
02639         }
02640             
02641         // assign virtual buttons for codes that can be triggered by 
02642         // real button inputs
02643         for (int i = 0 ; i < MAX_BUTTONS ; ++i)
02644         {
02645             // get the button
02646             ButtonCfg &b = cfg.button[i];
02647             
02648             // check the unshifted button
02649             int c = b.IRCommand - 1;
02650             if (c >= 0 && c < MAX_IR_CODES 
02651                 && IRConfigSlotToVirtualButton[c] == 0xFF)
02652                 IRConfigSlotToVirtualButton[c] = nVirtualButtons++;
02653                 
02654             // check the shifted button
02655             c = b.IRCommand2 - 1;
02656             if (c >= 0 && c < MAX_IR_CODES 
02657                 && IRConfigSlotToVirtualButton[c] == 0xFF)
02658                 IRConfigSlotToVirtualButton[c] = nVirtualButtons++;
02659         }
02660         
02661         // allocate an additional virtual button for transmitting ad hoc
02662         // codes, such as for the "send code" USB API function
02663         IRAdHocBtn = nVirtualButtons++;
02664             
02665         // create the transmitter
02666         ir_tx = new IRTransmitter(pin, nVirtualButtons);
02667         
02668         // program the commands into the virtual button slots
02669         for (int i = 0 ; i < MAX_IR_CODES ; ++i)
02670         {
02671             // if this slot is assigned to a virtual button, program it
02672             int vb = IRConfigSlotToVirtualButton[i];
02673             if (vb != 0xFF)
02674             {
02675                 IRCommandCfg &cb = cfg.IRCommand[i];
02676                 uint64_t code = cb.code.lo | (uint64_t(cb.code.hi) << 32);
02677                 bool dittos = (cb.flags & IRFlagDittos) != 0;
02678                 ir_tx->programButton(vb, cb.protocol, dittos, code);
02679             }
02680         }
02681     }
02682 
02683     // if there's a receiver, set it up
02684     if ((pin = wirePinName(cfg.IR.sensor)) != NC)
02685     {
02686         // create the receiver
02687         ir_rx = new IRReceiver(pin, 32);
02688         
02689         // connect the transmitter (if any) to the receiver, so that
02690         // the receiver can suppress reception of our own transmissions
02691         ir_rx->setTransmitter(ir_tx);
02692         
02693         // enable it
02694         ir_rx->enable();
02695         
02696         // Check the IR command slots to see if any slots are configured
02697         // to send a keyboard key on receiving an IR command.  If any are,
02698         // tell the caller that we need a USB keyboard interface.
02699         for (int i = 0 ; i < MAX_IR_CODES ; ++i)
02700         {
02701             IRCommandCfg &cb = cfg.IRCommand[i];
02702             if (cb.protocol != 0
02703                 && (cb.keytype == BtnTypeKey || cb.keytype == BtnTypeMedia))
02704             {
02705                 kbKeys = true;
02706                 break;
02707             }
02708         }
02709     }
02710 }
02711 
02712 // Press or release a button with an assigned IR function.  'cmd'
02713 // is the command slot number (1..MAX_IR_CODES) assigned to the button.
02714 void IR_buttonChange(uint8_t cmd, bool pressed)
02715 {
02716     // only proceed if there's an IR transmitter attached
02717     if (ir_tx != 0)
02718     {
02719         // adjust the command slot to a zero-based index
02720         int slot = cmd - 1;
02721         
02722         // press or release the virtual button
02723         ir_tx->pushButton(IRConfigSlotToVirtualButton[slot], pressed);
02724     }
02725 }
02726 
02727 // Process IR input and output
02728 void process_IR(Config &cfg, USBJoystick &js)
02729 {
02730     // check for transmitter tasks, if there's a transmitter
02731     if (ir_tx != 0)
02732     {
02733         // If we're not currently sending, and an ad hoc IR command
02734         // is ready to send, send it.
02735         if (!ir_tx->isSending() && IRAdHocCmd.ready)
02736         {
02737             // program the command into the transmitter virtual button
02738             // that we reserved for ad hoc commands
02739             ir_tx->programButton(IRAdHocBtn, IRAdHocCmd.protocol,
02740                 IRAdHocCmd.dittos, IRAdHocCmd.code);
02741                 
02742             // send the command - just pulse the button to send it once
02743             ir_tx->pushButton(IRAdHocBtn, true);
02744             ir_tx->pushButton(IRAdHocBtn, false);
02745             
02746             // we've sent the command, so clear the 'ready' flag
02747             IRAdHocCmd.ready = false;
02748         }
02749     }
02750     
02751     // check for receiver tasks, if there's a receiver
02752     if (ir_rx != 0)
02753     {
02754         // Time out any received command
02755         if (IRCommandIn != 0)
02756         {
02757             // Time out commands after 200ms without a repeat signal.
02758             // Time out the inter-key gap after 50ms.
02759             uint32_t t = IRTimer.read_us();
02760             if (t > 200000)
02761                 IRCommandIn = 0;
02762             else if (t > 50000)
02763                 IRKeyGap = false;
02764         }
02765     
02766         // Check if we're in learning mode
02767         if (IRLearningMode != 0)
02768         {
02769             // Learning mode.  Read raw inputs from the IR sensor and 
02770             // forward them to the PC via USB reports, up to the report
02771             // limit.
02772             const int nmax = USBJoystick::maxRawIR;
02773             uint16_t raw[nmax];
02774             int n;
02775             for (n = 0 ; n < nmax && ir_rx->processOne(raw[n]) ; ++n) ;
02776             
02777             // if we read any raw samples, report them
02778             if (n != 0)
02779                 js.reportRawIR(n, raw);
02780                 
02781             // check for a command
02782             IRCommand c;
02783             if (ir_rx->readCommand(c))
02784             {
02785                 // check the current learning state
02786                 switch (IRLearningMode)
02787                 {
02788                 case 1:
02789                     // Initial state, waiting for the first decoded command.
02790                     // This is it.
02791                     learnedIRCode = c;
02792                     
02793                     // Check if we need additional information.  If the
02794                     // protocol supports dittos, we have to wait for a repeat
02795                     // to see if the remote actually uses the dittos, since
02796                     // some implementations of such protocols use the dittos
02797                     // while others just send repeated full codes.  Otherwise,
02798                     // all we need is the initial code, so we're done.
02799                     IRLearningMode = (c.hasDittos ? 2 : 3);
02800                     break;
02801                     
02802                 case 2:
02803                     // Code received, awaiting auto-repeat information.  If
02804                     // the protocol has dittos, check to see if we got a ditto:
02805                     //
02806                     // - If we received a ditto in the same protocol as the
02807                     //   prior command, the remote uses dittos.
02808                     //
02809                     // - If we received a repeat of the prior command (not a
02810                     //   ditto, but a repeat of the full code), the remote
02811                     //   doesn't use dittos even though the protocol supports
02812                     //   them.
02813                     //
02814                     // - Otherwise, it's not an auto-repeat at all, so we
02815                     //   can't decide one way or the other on dittos: start
02816                     //   over.
02817                     if (c.proId == learnedIRCode.proId
02818                         && c.hasDittos
02819                         && c.ditto)
02820                     {
02821                         // success - the remote uses dittos
02822                         IRLearningMode = 3;
02823                     }
02824                     else if (c.proId == learnedIRCode.proId
02825                         && c.hasDittos
02826                         && !c.ditto
02827                         && c.code == learnedIRCode.code)
02828                     {
02829                         // success - it's a repeat of the last code, so
02830                         // the remote doesn't use dittos even though the
02831                         // protocol supports them
02832                         learnedIRCode.hasDittos = false;
02833                         IRLearningMode = 3;
02834                     }
02835                     else
02836                     {
02837                         // It's not a ditto and not a full repeat of the
02838                         // last code, so it's either a new key, or some kind
02839                         // of multi-code key encoding that we don't recognize.
02840                         // We can't use this code, so start over.
02841                         IRLearningMode = 1;
02842                     }
02843                     break;
02844                 }
02845                 
02846                 // If we ended in state 3, we've successfully decoded
02847                 // the transmission.  Report the decoded data and terminate
02848                 // learning mode.
02849                 if (IRLearningMode == 3)
02850                 {
02851                     // figure the flags: 
02852                     //   0x02 -> dittos
02853                     uint8_t flags = 0;
02854                     if (learnedIRCode.hasDittos)
02855                         flags |= 0x02;
02856                         
02857                     // report the code
02858                     js.reportIRCode(learnedIRCode.proId, flags, learnedIRCode.code);
02859                         
02860                     // exit learning mode
02861                     IRLearningMode = 0;
02862                 }
02863             }
02864             
02865             // time out of IR learning mode if it's been too long
02866             if (IRLearningMode != 0 && IRTimer.read_us() > 10000000L)
02867             {
02868                 // report the termination by sending a raw IR report with
02869                 // zero data elements
02870                 js.reportRawIR(0, 0);
02871                 
02872                 
02873                 // cancel learning mode
02874                 IRLearningMode = 0;
02875             }
02876         }
02877         else
02878         {
02879             // Not in learning mode.  We don't care about the raw signals;
02880             // just run them through the protocol decoders.
02881             ir_rx->process();
02882             
02883             // Check for decoded commands.  Keep going until all commands
02884             // have been read.
02885             IRCommand c;
02886             while (ir_rx->readCommand(c))
02887             {
02888                 // We received a decoded command.  Determine if it's a repeat,
02889                 // and if so, try to determine whether it's an auto-repeat (due
02890                 // to the remote key being held down) or a distinct new press 
02891                 // on the same key as last time.  The distinction is significant
02892                 // because it affects the auto-repeat behavior of the PC key
02893                 // input.  An auto-repeat represents a key being held down on
02894                 // the remote, which we want to translate to a (virtual) key 
02895                 // being held down on the PC keyboard; a distinct key press on
02896                 // the remote translates to a distinct key press on the PC.
02897                 //
02898                 // It can only be a repeat if there's a prior command that
02899                 // hasn't timed out yet, so start by checking for a previous
02900                 // command.
02901                 bool repeat = false, autoRepeat = false;
02902                 if (IRCommandIn != 0)
02903                 {
02904                     // We have a command in progress.  Check to see if the
02905                     // new command is a repeat of the previous command.  Check
02906                     // first to see if it's a "ditto", which explicitly represents
02907                     // an auto-repeat of the last command.
02908                     IRCommandCfg &cmdcfg = cfg.IRCommand[IRCommandIn - 1];
02909                     if (c.ditto)
02910                     {
02911                         // We received a ditto.  Dittos are always auto-
02912                         // repeats, so it's an auto-repeat as long as the
02913                         // ditto is in the same protocol as the last command.
02914                         // If the ditto is in a new protocol, the ditto can't
02915                         // be for the last command we saw, because a ditto
02916                         // never changes protocols from its antecedent.  In
02917                         // such a case, we must have missed the antecedent
02918                         // command and thus don't know what's being repeated.
02919                         repeat = autoRepeat = (c.proId == cmdcfg.protocol);
02920                     }
02921                     else
02922                     {
02923                         // It's not a ditto.  The new command is a repeat if
02924                         // it matches the protocol and command code of the 
02925                         // prior command.
02926                         repeat = (c.proId == cmdcfg.protocol 
02927                                   && uint32_t(c.code) == cmdcfg.code.lo
02928                                   && uint32_t(c.code >> 32) == cmdcfg.code.hi);
02929                                   
02930                         // If the command is a repeat, try to determine whether
02931                         // it's an auto-repeat or a new press on the same key.
02932                         // If the protocol uses dittos, it's definitely a new
02933                         // key press, because an auto-repeat would have used a
02934                         // ditto.  For a protocol that doesn't use dittos, both
02935                         // an auto-repeat and a new key press just send the key
02936                         // code again, so we can't tell the difference based on
02937                         // that alone.  But if the protocol has a toggle bit, we
02938                         // can tell by the toggle bit value: a new key press has
02939                         // the opposite toggle value as the last key press, while 
02940                         // an auto-repeat has the same toggle.  Note that if the
02941                         // protocol doesn't use toggle bits, the toggle value
02942                         // will always be the same, so we'll simply always treat
02943                         // any repeat as an auto-repeat.  Many protocols simply
02944                         // provide no way to distinguish the two, so in such
02945                         // cases it's consistent with the native implementations
02946                         // to treat any repeat as an auto-repeat.
02947                         autoRepeat = 
02948                             repeat 
02949                             && !(cmdcfg.flags & IRFlagDittos)
02950                             && c.toggle == lastIRToggle;
02951                     }
02952                 }
02953                 
02954                 // Check to see if it's a repeat of any kind
02955                 if (repeat)
02956                 {
02957                     // It's a repeat.  If it's not an auto-repeat, it's a
02958                     // new distinct key press, so we need to send the PC a
02959                     // momentary gap where we're not sending the same key,
02960                     // so that the PC also recognizes this as a distinct
02961                     // key press event.
02962                     if (!autoRepeat)
02963                         IRKeyGap = true;
02964                         
02965                     // restart the key-up timer
02966                     IRTimer.reset();
02967                 }
02968                 else if (c.ditto)
02969                 {
02970                     // It's a ditto, but not a repeat of the last command.
02971                     // But a ditto doesn't contain any information of its own
02972                     // on the command being repeated, so given that it's not
02973                     // our last command, we can't infer what command the ditto
02974                     // is for and thus can't make sense of it.  We have to
02975                     // simply ignore it and wait for the sender to start with
02976                     // a full command for a new key press.
02977                     IRCommandIn = 0;
02978                 }
02979                 else
02980                 {
02981                     // It's not a repeat, so the last command is no longer
02982                     // in effect (regardless of whether we find a match for
02983                     // the new command).
02984                     IRCommandIn = 0;
02985                     
02986                     // Check to see if we recognize the new command, by
02987                     // searching for a match in our learned code list.
02988                     for (int i = 0 ; i < MAX_IR_CODES ; ++i)
02989                     {
02990                         // if the protocol and command code from the code
02991                         // list both match the input, it's a match
02992                         IRCommandCfg &cmdcfg = cfg.IRCommand[i];
02993                         if (cmdcfg.protocol == c.proId 
02994                             && cmdcfg.code.lo == uint32_t(c.code)
02995                             && cmdcfg.code.hi == uint32_t(c.code >> 32))
02996                         {
02997                             // Found it!  Make this the last command, and 
02998                             // remember the starting time.
02999                             IRCommandIn = i + 1;
03000                             lastIRToggle = c.toggle;
03001                             IRTimer.reset();
03002                             
03003                             // no need to keep searching
03004                             break;
03005                         }
03006                     }
03007                 }
03008             }
03009         }
03010     }
03011 }
03012 
03013 
03014 // ---------------------------------------------------------------------------
03015 //
03016 // Button input
03017 //
03018 
03019 // button state
03020 struct ButtonState
03021 {
03022     ButtonState()
03023     {
03024         physState = logState = prevLogState = 0;
03025         virtState = 0;
03026         dbState = 0;
03027         pulseState = 0;
03028         pulseTime = 0;
03029     }
03030     
03031     // "Virtually" press or un-press the button.  This can be used to
03032     // control the button state via a software (virtual) source, such as
03033     // the ZB Launch Ball feature.
03034     //
03035     // To allow sharing of one button by multiple virtual sources, each
03036     // virtual source must keep track of its own state internally, and 
03037     // only call this routine to CHANGE the state.  This is because calls
03038     // to this routine are additive: turning the button ON twice will
03039     // require turning it OFF twice before it actually turns off.
03040     void virtPress(bool on)
03041     {
03042         // Increment or decrement the current state
03043         virtState += on ? 1 : -1;
03044     }
03045     
03046     // DigitalIn for the button, if connected to a physical input
03047     TinyDigitalIn di;
03048     
03049     // Time of last pulse state transition.
03050     //
03051     // Each state change sticks for a minimum period; when the timer expires,
03052     // if the underlying physical switch is in a different state, we switch
03053     // to the next state and restart the timer.  pulseTime is the time remaining
03054     // remaining before we can make another state transition, in microseconds.
03055     // The state transitions require a complete cycle, 1 -> 2 -> 3 -> 4 -> 1...; 
03056     // this guarantees that the parity of the pulse count always matches the 
03057     // current physical switch state when the latter is stable, which makes
03058     // it impossible to "trick" the host by rapidly toggling the switch state.
03059     // (On my original Pinscape cabinet, I had a hardware pulse generator
03060     // for coin door, and that *was* possible to trick by rapid toggling.
03061     // This software system can't be fooled that way.)
03062     uint32_t pulseTime;
03063     
03064     // Config key index.  This points to the ButtonCfg structure in the
03065     // configuration that contains the PC key mapping for the button.
03066     uint8_t cfgIndex;
03067     
03068     // Virtual press state.  This is used to simulate pressing the button via
03069     // software inputs rather than physical inputs.  To allow one button to be
03070     // controlled by mulitple software sources, each source should keep track
03071     // of its own virtual state for the button independently, and then INCREMENT
03072     // this variable when the source's state transitions from off to on, and
03073     // DECREMENT it when the source's state transitions from on to off.  That
03074     // will make the button's pressed state the logical OR of all of the virtual
03075     // and physical source states.
03076     uint8_t virtState;
03077     
03078     // Debounce history.  On each scan, we shift in a 1 bit to the lsb if
03079     // the physical key is reporting ON, and shift in a 0 bit if the physical
03080     // key is reporting OFF.  We consider the key to have a new stable state
03081     // if we have N consecutive 0's or 1's in the low N bits (where N is
03082     // a parameter that determines how long we wait for transients to settle).
03083     uint8_t dbState;
03084     
03085     // current PHYSICAL on/off state, after debouncing
03086     uint8_t physState : 1;
03087     
03088     // current LOGICAL on/off state as reported to the host.
03089     uint8_t logState : 1;
03090 
03091     // Previous logical on/off state, when keys were last processed for USB 
03092     // reports and local effects.  This lets us detect edges (transitions)
03093     // in the logical state, for effects that are triggered when the state
03094     // changes rather than merely by the button being on or off.
03095     uint8_t prevLogState : 1;
03096     
03097     // Pulse state
03098     // 
03099     // A button in pulse mode (selected via the config flags for the button) 
03100     // transmits a brief logical button press and release each time the attached 
03101     // physical switch changes state.  This is useful for cases where the host 
03102     // expects a key press for each change in the state of the physical switch.  
03103     // The canonical example is the Coin Door switch in VPinMAME, which requires 
03104     // pressing the END key to toggle the open/closed state.  This software design 
03105     // isn't easily implemented in a physical coin door, though; the simplest
03106     // physical sensor for the coin door state is a switch that's on when the 
03107     // door is open and off when the door is closed (or vice versa, but in either 
03108     // case, the switch state corresponds to the current state of the door at any
03109     // given time, rather than pulsing on state changes).  The "pulse mode"
03110     // option bridges this gap by generating a toggle key event each time
03111     // there's a change to the physical switch's state.
03112     //
03113     // Pulse state:
03114     //   0 -> not a pulse switch - logical key state equals physical switch state
03115     //   1 -> off
03116     //   2 -> transitioning off-on
03117     //   3 -> on
03118     //   4 -> transitioning on-off
03119     uint8_t pulseState : 3;         // 5 states -> we need 3 bits
03120 
03121 } __attribute__((packed));
03122 
03123 ButtonState *buttonState;       // live button slots, allocated on startup
03124 int8_t nButtons;                // number of live button slots allocated
03125 int8_t zblButtonIndex = -1;     // index of ZB Launch button slot; -1 if unused
03126 
03127 // Shift button state
03128 struct
03129 {
03130     int8_t index;               // buttonState[] index of shift button; -1 if none
03131     uint8_t state;              // current state, for "Key OR Shift" mode:
03132                                 //   0 = not shifted
03133                                 //   1 = shift button down, no key pressed yet
03134                                 //   2 = shift button down, key pressed
03135                                 //   3 = released, sending pulsed keystroke
03136     uint32_t pulseTime;         // time remaining in pulsed keystroke (state 3)
03137 }
03138 __attribute__((packed)) shiftButton;
03139 
03140 // Button data
03141 uint32_t jsButtons = 0;
03142 
03143 // Keyboard report state.  This tracks the USB keyboard state.  We can
03144 // report at most 6 simultaneous non-modifier keys here, plus the 8
03145 // modifier keys.
03146 struct
03147 {
03148     bool changed;       // flag: changed since last report sent
03149     uint8_t nkeys;      // number of active keys in the list
03150     uint8_t data[8];    // key state, in USB report format: byte 0 is the modifier key mask,
03151                         // byte 1 is reserved, and bytes 2-7 are the currently pressed key codes
03152 } kbState = { false, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
03153 
03154 // Media key state
03155 struct
03156 {
03157     bool changed;       // flag: changed since last report sent
03158     uint8_t data;       // key state byte for USB reports
03159 } mediaState = { false, 0 };
03160 
03161 // button scan interrupt timer
03162 Timeout scanButtonsTimeout;
03163 
03164 // Button scan interrupt handler.  We call this periodically via
03165 // a timer interrupt to scan the physical button states.  
03166 void scanButtons()
03167 {
03168     // schedule the next interrupt
03169     scanButtonsTimeout.attach_us(&scanButtons, 1000);
03170     
03171     // scan all button input pins
03172     ButtonState *bs = buttonState, *last = bs + nButtons;
03173     for ( ; bs < last ; ++bs)
03174     {
03175         // Shift the new state into the debounce history
03176         uint8_t db = (bs->dbState << 1) | bs->di.read();
03177         bs->dbState = db;
03178         
03179         // If we have all 0's or 1's in the history for the required
03180         // debounce period, the key state is stable, so apply the new
03181         // physical state.  Note that the pins are active low, so the
03182         // new button on/off state is the inverse of the GPIO state.
03183         const uint8_t stable = 0x1F;   // 00011111b -> low 5 bits = last 5 readings
03184         db &= stable;
03185         if (db == 0 || db == stable)
03186             bs->physState = !db;
03187     }
03188 }
03189 
03190 // Button state transition timer.  This is used for pulse buttons, to
03191 // control the timing of the logical key presses generated by transitions
03192 // in the physical button state.
03193 Timer buttonTimer;
03194 
03195 // Count a button during the initial setup scan
03196 void countButton(uint8_t typ, uint8_t shiftTyp, bool &kbKeys)
03197 {
03198     // count it
03199     ++nButtons;
03200     
03201     // if it's a keyboard key or media key, note that we need a USB 
03202     // keyboard interface
03203     if (typ == BtnTypeKey || typ == BtnTypeMedia
03204         || shiftTyp == BtnTypeKey || shiftTyp == BtnTypeMedia)
03205         kbKeys = true;
03206 }
03207 
03208 // initialize the button inputs
03209 void initButtons(Config &cfg, bool &kbKeys)
03210 {
03211     // presume no shift key
03212     shiftButton.index = -1;
03213     shiftButton.state = 0;
03214     
03215     // Count up how many button slots we'll need to allocate.  Start
03216     // with assigned buttons from the configuration, noting that we
03217     // only need to create slots for buttons that are actually wired.
03218     nButtons = 0;
03219     for (int i = 0 ; i < MAX_BUTTONS ; ++i)
03220     {
03221         // it's valid if it's wired to a real input pin
03222         if (wirePinName(cfg.button[i].pin) != NC)
03223             countButton(cfg.button[i].typ, cfg.button[i].typ2, kbKeys);
03224     }
03225     
03226     // Count virtual buttons
03227 
03228     // ZB Launch
03229     if (cfg.plunger.zbLaunchBall.port != 0)
03230     {
03231         // valid - remember the live button index
03232         zblButtonIndex = nButtons;
03233         
03234         // count it
03235         countButton(cfg.plunger.zbLaunchBall.keytype, BtnTypeNone, kbKeys);
03236     }
03237 
03238     // Allocate the live button slots
03239     ButtonState *bs = buttonState = new ButtonState[nButtons];
03240     
03241     // Configure the physical inputs
03242     for (int i = 0 ; i < MAX_BUTTONS ; ++i)
03243     {
03244         PinName pin = wirePinName(cfg.button[i].pin);
03245         if (pin != NC)
03246         {
03247             // point back to the config slot for the keyboard data
03248             bs->cfgIndex = i;
03249 
03250             // set up the GPIO input pin for this button
03251             bs->di.assignPin(pin);
03252             
03253             // if it's a pulse mode button, set the initial pulse state to Off
03254             if (cfg.button[i].flags & BtnFlagPulse)
03255                 bs->pulseState = 1;
03256                 
03257             // If this is the shift button, note its buttonState[] index.
03258             // We have to figure the buttonState[] index separately from
03259             // the config index, because the indices can differ if some
03260             // config slots are left unused.
03261             if (cfg.shiftButton.idx == i+1)
03262                 shiftButton.index = bs - buttonState;
03263                 
03264             // advance to the next button
03265             ++bs;
03266         }
03267     }
03268     
03269     // Configure the virtual buttons.  These are buttons controlled via
03270     // software triggers rather than physical GPIO inputs.  The virtual
03271     // buttons have the same control structures as regular buttons, but
03272     // they get their configuration data from other config variables.
03273     
03274     // ZB Launch Ball button
03275     if (cfg.plunger.zbLaunchBall.port != 0)
03276     {
03277         // Point back to the config slot for the keyboard data.
03278         // We use a special extra slot for virtual buttons, 
03279         // so we also need to set up the slot data by copying
03280         // the ZBL config data to our virtual button slot.
03281         bs->cfgIndex = ZBL_BUTTON_CFG;
03282         cfg.button[ZBL_BUTTON_CFG].pin = PINNAME_TO_WIRE(NC);
03283         cfg.button[ZBL_BUTTON_CFG].typ = cfg.plunger.zbLaunchBall.keytype;
03284         cfg.button[ZBL_BUTTON_CFG].val = cfg.plunger.zbLaunchBall.keycode;
03285         
03286         // advance to the next button
03287         ++bs;
03288     }
03289     
03290     // start the button scan thread
03291     scanButtonsTimeout.attach_us(scanButtons, 1000);
03292 
03293     // start the button state transition timer
03294     buttonTimer.start();
03295 }
03296 
03297 // Media key mapping.  This maps from an 8-bit USB media key
03298 // code to the corresponding bit in our USB report descriptor.
03299 // The USB key code is the index, and the value at the index
03300 // is the report descriptor bit.  See joystick.cpp for the
03301 // media descriptor details.  Our currently mapped keys are:
03302 //
03303 //    0xE2 -> Mute -> 0x01
03304 //    0xE9 -> Volume Up -> 0x02
03305 //    0xEA -> Volume Down -> 0x04
03306 //    0xB5 -> Next Track -> 0x08
03307 //    0xB6 -> Previous Track -> 0x10
03308 //    0xB7 -> Stop -> 0x20
03309 //    0xCD -> Play / Pause -> 0x40
03310 //
03311 static const uint8_t mediaKeyMap[] = {
03312      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 00-0F
03313      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 10-1F
03314      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 20-2F
03315      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 30-3F
03316      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 40-4F
03317      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 50-5F
03318      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 60-6F
03319      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 70-7F
03320      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 80-8F
03321      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 90-9F
03322      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // A0-AF
03323      0,  0,  0,  0,  0,  8, 16, 32,  0,  0,  0,  0,  0,  0,  0,  0, // B0-BF
03324      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 64,  0,  0, // C0-CF
03325      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // D0-DF
03326      0,  0,  1,  0,  0,  0,  0,  0,  0,  2,  4,  0,  0,  0,  0,  0, // E0-EF
03327      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0  // F0-FF
03328 };
03329  
03330 // Keyboard key/joystick button state.  processButtons() uses this to 
03331 // build the set of key presses to report to the PC based on the logical
03332 // states of the button iputs.
03333 struct KeyState
03334 {
03335     KeyState()
03336     {
03337         // zero all members
03338         memset(this, 0, sizeof(*this));
03339     }
03340     
03341     // Keyboard media keys currently pressed.  This is a bit vector in
03342     // the format used in our USB keyboard reports (see USBJoystick.cpp).
03343     uint8_t mediakeys;
03344          
03345     // Keyboard modifier (shift) keys currently pressed.  This is a bit 
03346     // vector in the format used in our USB keyboard reports (see
03347     // USBJoystick.cpp).
03348     uint8_t modkeys;
03349      
03350     // Regular keyboard keys currently pressed.  Each element is a USB
03351     // key code, or 0 for empty slots.  Note that the USB report format
03352     // theoretically allows a flexible size limit, but the Windows KB
03353     // drivers have a fixed limit of 6 simultaneous keys (and won't
03354     // accept reports with more), so there's no point in making this
03355     // flexible; we'll just use the fixed size dictated by Windows.
03356     uint8_t keys[7];
03357      
03358     // number of valid entries in keys[] array
03359     int nkeys;
03360      
03361     // Joystick buttons pressed, as a bit vector.  Bit n (1 << n)
03362     // represents joystick button n, n in 0..31, with 0 meaning 
03363     // unpressed and 1 meaning pressed.
03364     uint32_t js;
03365     
03366     
03367     // Add a key press.  'typ' is the button type code (ButtonTypeXxx),
03368     // and 'val' is the value (the meaning of which varies by type code).
03369     void addKey(uint8_t typ, uint8_t val)
03370     {
03371         // add the key according to the type
03372         switch (typ)
03373         {
03374         case BtnTypeJoystick:
03375             // joystick button
03376             js |= (1 << (val - 1));
03377             break;
03378             
03379         case BtnTypeKey:
03380             // Keyboard key.  The USB keyboard report encodes regular
03381             // keys and modifier keys separately, so we need to check
03382             // which type we have.  Note that past versions mapped the 
03383             // Keyboard Volume Up, Keyboard Volume Down, and Keyboard 
03384             // Mute keys to the corresponding Media keys.  We no longer
03385             // do this; instead, we have the separate BtnTypeMedia for
03386             // explicitly using media keys if desired.
03387             if (val >= 0xE0 && val <= 0xE7)
03388             {
03389                 // It's a modifier key.  These are represented in the USB 
03390                 // reports with a bit mask.  We arrange the mask bits in
03391                 // the same order as the scan codes, so we can figure the
03392                 // appropriate bit with a simple shift.
03393                 modkeys |= (1 << (val - 0xE0));
03394             }
03395             else
03396             {
03397                 // It's a regular key.  Make sure it's not already in the 
03398                 // list, and that the list isn't full.  If neither of these 
03399                 // apply, add the key to the key array.
03400                 if (nkeys < 7)
03401                 {
03402                     bool found = false;
03403                     for (int i = 0 ; i < nkeys ; ++i)
03404                     {
03405                         if (keys[i] == val)
03406                         {
03407                             found = true;
03408                             break;
03409                         }
03410                     }
03411                     if (!found)
03412                         keys[nkeys++] = val;
03413                 }
03414             }
03415             break;
03416 
03417         case BtnTypeMedia:
03418             // Media control key.  The media keys are mapped in the USB
03419             // report to bits, whereas the key codes are specified in the
03420             // config with their USB usage numbers.  E.g., the config val
03421             // for Media Next Track is 0xB5, but we encode this in the USB
03422             // report as bit 0x08.  The mediaKeyMap[] table translates
03423             // from the USB usage number to the mask bit.  If the key isn't
03424             // among the subset we support, the mapped bit will be zero, so
03425             // the "|=" will have no effect and the key will be ignored.
03426             mediakeys |= mediaKeyMap[val];
03427             break;                                
03428         }
03429     }
03430 };
03431 
03432 
03433 // Process the button state.  This sets up the joystick, keyboard, and
03434 // media control descriptors with the current state of keys mapped to
03435 // those HID interfaces, and executes the local effects for any keys 
03436 // mapped to special device functions (e.g., Night Mode).
03437 void processButtons(Config &cfg)
03438 {
03439     // key state
03440     KeyState ks;
03441     
03442     // calculate the time since the last run
03443     uint32_t dt = buttonTimer.read_us();
03444     buttonTimer.reset();
03445     
03446     // check the shift button state
03447     if (shiftButton.index != -1)
03448     {
03449         // get the shift button's physical state object
03450         ButtonState *sbs = &buttonState[shiftButton.index];
03451         
03452         // figure what to do based on the shift button mode in the config
03453         switch (cfg.shiftButton.mode)
03454         {
03455         case 0:
03456         default:
03457             // "Shift OR Key" mode.  The shift button doesn't send its key
03458             // immediately when pressed.  Instead, we wait to see what 
03459             // happens while it's down.  Check the current cycle state.
03460             switch (shiftButton.state)
03461             {
03462             case 0:
03463                 // Not shifted.  Check if the button is now down: if so,
03464                 // switch to state 1 (shift button down, no key pressed yet).
03465                 if (sbs->physState)
03466                     shiftButton.state = 1;
03467                 break;
03468                 
03469             case 1:
03470                 // Shift button down, no key pressed yet.  If the button is
03471                 // now up, it counts as an ordinary button press instead of
03472                 // a shift button press, since the shift function was never
03473                 // used.  Return to unshifted state and start a timed key 
03474                 // pulse event.
03475                 if (!sbs->physState)
03476                 {
03477                     shiftButton.state = 3;
03478                     shiftButton.pulseTime = 50000+dt;  // 50 ms left on the key pulse
03479                 }
03480                 break;
03481                 
03482             case 2:
03483                 // Shift button down, other key was pressed.  If the button is
03484                 // now up, simply clear the shift state without sending a key
03485                 // press for the shift button itself to the PC.  The shift
03486                 // function was used, so its ordinary key press function is
03487                 // suppressed.
03488                 if (!sbs->physState)
03489                     shiftButton.state = 0;
03490                 break;
03491                 
03492             case 3:
03493                 // Sending pulsed keystroke.  Deduct the current time interval
03494                 // from the remaining pulse timer.  End the pulse if the time
03495                 // has expired.
03496                 if (shiftButton.pulseTime > dt)
03497                     shiftButton.pulseTime -= dt;
03498                 else
03499                     shiftButton.state = 0;
03500                 break;
03501             }
03502             break;
03503             
03504         case 1:
03505             // "Shift AND Key" mode.  In this mode, the shift button acts
03506             // like any other button and sends its mapped key immediately.
03507             // The state cycle in this case simply matches the physical
03508             // state: ON -> cycle state 1, OFF -> cycle state 0.
03509             shiftButton.state = (sbs->physState ? 1 : 0);
03510             break;
03511         }
03512     }
03513 
03514     // scan the button list
03515     ButtonState *bs = buttonState;
03516     for (int i = 0 ; i < nButtons ; ++i, ++bs)
03517     {
03518         // get the config entry for the button
03519         ButtonCfg *bc = &cfg.button[bs->cfgIndex];
03520 
03521         // Check the button type:
03522         //   - shift button
03523         //   - pulsed button
03524         //   - regular button
03525         if (shiftButton.index == i)
03526         {
03527             // This is the shift button.  The logical state handling
03528             // depends on the mode.
03529             switch (cfg.shiftButton.mode)
03530             {
03531             case 0:
03532             default:
03533                 // "Shift OR Key" mode.  The logical state is ON only
03534                 // during the timed pulse when the key is released, which
03535                 // is signified by shift button state 3.
03536                 bs->logState = (shiftButton.state == 3);
03537                 break;
03538                 
03539             case 1:
03540                 // "Shif AND Key" mode.  The shift button acts like any
03541                 // other button, so it's logically on when physically on.
03542                 bs->logState = bs->physState;
03543                 break;
03544             }
03545         }        
03546         else if (bs->pulseState != 0)
03547         {
03548             // if the timer has expired, check for state changes
03549             if (bs->pulseTime > dt)
03550             {
03551                 // not expired yet - deduct the last interval
03552                 bs->pulseTime -= dt;
03553             }
03554             else
03555             {
03556                 // pulse time expired - check for a state change
03557                 const uint32_t pulseLength = 200000UL;  // 200 milliseconds
03558                 switch (bs->pulseState)
03559                 {
03560                 case 1:
03561                     // off - if the physical switch is now on, start a button pulse
03562                     if (bs->physState) 
03563                     {
03564                         bs->pulseTime = pulseLength;
03565                         bs->pulseState = 2;
03566                         bs->logState = 1;
03567                     }
03568                     break;
03569                     
03570                 case 2:
03571                     // transitioning off to on - end the pulse, and start a gap
03572                     // equal to the pulse time so that the host can observe the
03573                     // change in state in the logical button
03574                     bs->pulseState = 3;
03575                     bs->pulseTime = pulseLength;
03576                     bs->logState = 0;
03577                     break;
03578                     
03579                 case 3:
03580                     // on - if the physical switch is now off, start a button pulse
03581                     if (!bs->physState) 
03582                     {
03583                         bs->pulseTime = pulseLength;
03584                         bs->pulseState = 4;
03585                         bs->logState = 1;
03586                     }
03587                     break;
03588                     
03589                 case 4:
03590                     // transitioning on to off - end the pulse, and start a gap
03591                     bs->pulseState = 1;
03592                     bs->pulseTime = pulseLength;
03593                     bs->logState = 0;
03594                     break;
03595                 }
03596             }
03597         }
03598         else
03599         {
03600             // not a pulse switch - the logical state is the same as the physical state
03601             bs->logState = bs->physState;
03602         }
03603         
03604         // Determine if we're going to use the shifted version of the
03605         // button.  We're using the shifted version if...
03606         // 
03607         //  - the shift button is down, AND
03608         //  - this button isn't itself the shift button, AND
03609         //  - this button has some kind of shifted meaning
03610         //
03611         // A "shifted meaning" means that we have any of the following 
03612         // assigned to the shifted version of the button: a key assignment, 
03613         // (in typ2,key2), an IR command (in IRCommand2), or Night mode.
03614         //
03615         // The test for Night Mode is a bit tricky.  The shifted version of 
03616         // the button is the Night Mode toggle if the button matches the 
03617         // Night Mode button index, AND its flags are set with "toggle mode
03618         // ON" (bit 0x02 is on) and "switch mode OFF" (bit 0x01 is off).
03619         // So (button flags) & 0x03 must equal 0x02.
03620         bool useShift = 
03621             (shiftButton.state != 0
03622              && shiftButton.index != i
03623              && (bc->typ2 != BtnTypeNone
03624                  || bc->IRCommand2 != 0
03625                  || (cfg.nightMode.btn == i+1 && (cfg.nightMode.flags & 0x03) == 0x02)));
03626                  
03627         // If we're using the shift function, and no other button has used
03628         // the shift function yet (shift state 1: "shift button is down but
03629         // no one has used the shift function yet"), then we've "consumed"
03630         // the shift button press (so go to shift state 2: "shift button has
03631         // been used by some other button press that has a shifted meaning").
03632         if (useShift && shiftButton.state == 1 && bs->logState)
03633             shiftButton.state = 2;
03634 
03635         // carry out any edge effects from buttons changing states
03636         if (bs->logState != bs->prevLogState)
03637         {
03638             // check to see if this is the Night Mode button
03639             if (cfg.nightMode.btn == i + 1)
03640             {
03641                 // Check the switch type in the config flags.  If flag 0x01 is 
03642                 // set, it's a persistent on/off switch, so the night mode 
03643                 // state simply tracks the current state of the switch.  
03644                 // Otherwise, it's a momentary button, so each button push 
03645                 // (i.e., each transition from logical state OFF to ON) toggles 
03646                 // the night mode state.
03647                 //
03648                 // Note that the "shift" flag (0x02) has no effect in switch
03649                 // mode.  Shifting only works for toggle mode.
03650                 if ((cfg.nightMode.flags & 0x01) != 0)
03651                 {
03652                     // It's an on/off switch.  Night mode simply tracks the
03653                     // current switch state.
03654                     setNightMode(bs->logState);
03655                 }
03656                 else if (bs->logState)
03657                 {
03658                     // It's a momentary toggle switch.  Toggle the night mode 
03659                     // state on each distinct press of the button: that is,
03660                     // whenever the button's logical state transitions from 
03661                     // OFF to ON.
03662                     //
03663                     // The "shift" flag (0x02) tells us whether night mode is
03664                     // assigned to the shifted or unshifted version of the
03665                     // button.
03666                     bool pressed;
03667                     if (shiftButton.index == i)
03668                     {
03669                         // This button is both the Shift button AND the Night
03670                         // Mode button.  This is a special case in that the
03671                         // Shift status is irrelevant, because it's obviously
03672                         // identical to the Night Mode status.  So it doesn't
03673                         // matter whether or not the Night Mode button has the
03674                         // shifted flags; the raw button state is all that
03675                         // counts in this case.
03676                         pressed = true;
03677                     }
03678                     else if ((cfg.nightMode.flags & 0x02) != 0)
03679                     {
03680                         // Shift bit is set - night mode is assigned to the
03681                         // shifted version of the button.  This is a Night
03682                         // Mode toggle only if the Shift button is pressed.
03683                         pressed = (shiftButton.state != 0);
03684                     }
03685                     else
03686                     {
03687                         // No shift bit - night mode is assigned to the
03688                         // regular unshifted button.  The button press only
03689                         // applies if the Shift button is NOT pressed.
03690                         pressed = (shiftButton.state == 0);
03691                     }
03692                     
03693                     // if it's pressed (even after considering the shift mode),
03694                     // toggle night mode
03695                     if (pressed)
03696                         toggleNightMode();
03697                 }
03698             }
03699             
03700             // press or release IR virtual keys on key state changes
03701             uint8_t irc = useShift ? bc->IRCommand2 : bc->IRCommand;
03702             if (irc != 0)
03703                 IR_buttonChange(irc, bs->logState);
03704             
03705             // remember the new state for comparison on the next run
03706             bs->prevLogState = bs->logState;
03707         }
03708 
03709         // if it's pressed, physically or virtually, add it to the appropriate 
03710         // key state list
03711         if (bs->logState || bs->virtState)
03712         {
03713             // Get the key type and code.  Start by assuming that we're
03714             // going to use the normal unshifted meaning.
03715             uint8_t typ, val;
03716             if (useShift)
03717             {
03718                 typ = bc->typ2;
03719                 val = bc->val2;
03720             }
03721             else
03722             {
03723                 typ = bc->typ;
03724                 val = bc->val;
03725             }
03726         
03727             // We've decided on the meaning of the button, so process
03728             // the keyboard or joystick event.
03729             ks.addKey(typ, val);
03730         }
03731     }
03732     
03733     // If an IR input command is in effect, add the IR command's
03734     // assigned key, if any.  If we're in an IR key gap, don't include
03735     // the IR key.
03736     if (IRCommandIn != 0 && !IRKeyGap)
03737     {
03738         IRCommandCfg &irc = cfg.IRCommand[IRCommandIn - 1];
03739         ks.addKey(irc.keytype, irc.keycode);
03740     }
03741     
03742     // We're finished building the new key state.  Update the global
03743     // key state variables to reflect the new state. 
03744     
03745     // set the new joystick buttons (no need to check for changes, as we
03746     // report these on every joystick report whether they changed or not)
03747     jsButtons = ks.js;
03748     
03749     // check for keyboard key changes (we only send keyboard reports when
03750     // something changes)
03751     if (kbState.data[0] != ks.modkeys
03752         || kbState.nkeys != ks.nkeys
03753         || memcmp(ks.keys, &kbState.data[2], 6) != 0)
03754     {
03755         // we have changes - set the change flag and store the new key data
03756         kbState.changed = true;
03757         kbState.data[0] = ks.modkeys;
03758         if (ks.nkeys <= 6) {
03759             // 6 or fewer simultaneous keys - report the key codes
03760             kbState.nkeys = ks.nkeys;
03761             memcpy(&kbState.data[2], ks.keys, 6);
03762         }
03763         else {
03764             // more than 6 simultaneous keys - report rollover (all '1' key codes)
03765             kbState.nkeys = 6;
03766             memset(&kbState.data[2], 1, 6);
03767         }
03768     }        
03769     
03770     // check for media key changes (we only send media key reports when
03771     // something changes)
03772     if (mediaState.data != ks.mediakeys)
03773     {
03774         // we have changes - set the change flag and store the new key data
03775         mediaState.changed = true;
03776         mediaState.data = ks.mediakeys;
03777     }
03778 }
03779 
03780 // Send a button status report
03781 void reportButtonStatus(USBJoystick &js)
03782 {
03783     // start with all buttons off
03784     uint8_t state[(MAX_BUTTONS+7)/8];
03785     memset(state, 0, sizeof(state));
03786 
03787     // pack the button states into bytes, one bit per button
03788     ButtonState *bs = buttonState;
03789     for (int i = 0 ; i < nButtons ; ++i, ++bs)
03790     {
03791         // get the physical state
03792         int b = bs->physState;
03793         
03794         // pack it into the appropriate bit
03795         int idx = bs->cfgIndex;
03796         int si = idx / 8;
03797         int shift = idx & 0x07;
03798         state[si] |= b << shift;
03799     }
03800     
03801     // send the report
03802     js.reportButtonStatus(MAX_BUTTONS, state);
03803 }
03804 
03805 // ---------------------------------------------------------------------------
03806 //
03807 // Customization joystick subbclass
03808 //
03809 
03810 class MyUSBJoystick: public USBJoystick
03811 {
03812 public:
03813     MyUSBJoystick(uint16_t vendor_id, uint16_t product_id, uint16_t product_release,
03814         bool waitForConnect, bool enableJoystick, int axisFormat, bool useKB) 
03815         : USBJoystick(vendor_id, product_id, product_release, waitForConnect, enableJoystick, axisFormat, useKB)
03816     {
03817         sleeping_ = false;
03818         reconnectPending_ = false;
03819         timer_.start();
03820     }
03821     
03822     // show diagnostic LED feedback for connect state
03823     void diagFlash()
03824     {
03825         if (!configured() || sleeping_)
03826         {
03827             // flash once if sleeping or twice if disconnected
03828             for (int j = isConnected() ? 1 : 2 ; j > 0 ; --j)
03829             {
03830                 // short red flash
03831                 diagLED(1, 0, 0);
03832                 wait_us(50000);
03833                 diagLED(0, 0, 0);
03834                 wait_us(50000);
03835             }
03836         }
03837     }
03838     
03839     // are we connected?
03840     int isConnected()  { return configured(); }
03841     
03842     // Are we in sleep mode?  If true, this means that the hardware has
03843     // detected no activity on the bus for 3ms.  This happens when the
03844     // cable is physically disconnected, the computer is turned off, or
03845     // the connection is otherwise disabled.
03846     bool isSleeping() const { return sleeping_; }
03847 
03848     // If necessary, attempt to recover from a broken connection.
03849     //
03850     // This is a hack, to work around an apparent timing bug in the
03851     // KL25Z USB implementation that I haven't been able to solve any
03852     // other way.
03853     //
03854     // The issue: when we have an established connection, and the
03855     // connection is broken by physically unplugging the cable or by
03856     // rebooting the PC, the KL25Z sometimes fails to reconnect when
03857     // the physical connection is re-established.  The failure is 
03858     // sporadic; I'd guess it happens about 25% of the time, but I 
03859     // haven't collected any real statistics on it.  
03860     //
03861     // The proximate cause of the failure is a deadlock in the SETUP
03862     // protocol between the host and device that happens around the
03863     // point where the PC is requesting the configuration descriptor.
03864     // The exact point in the protocol where this occurs varies slightly;
03865     // it can occur a message or two before or after the Get Config
03866     // Descriptor packet.  No matter where it happens, the nature of
03867     // the deadlock is the same: the PC thinks it sees a STALL on EP0
03868     // from the device, so it terminates the connection attempt, which
03869     // stops further traffic on the cable.  The KL25Z USB hardware sees
03870     // the lack of traffic and triggers a SLEEP interrupt (a misnomer
03871     // for what should have been called a BROKEN CONNECTION interrupt).
03872     // Both sides simply stop talking at this point, so the connection
03873     // is effectively dead.  
03874     //
03875     // The strange thing is that, as far as I can tell, the KL25Z isn't
03876     // doing anything to trigger the STALL on its end.  Both the PC
03877     // and the KL25Z are happy up until the very point of the failure 
03878     // and show no signs of anything wrong in the protocol exchange.
03879     // In fact, every detail of the protocol exchange up to this point
03880     // is identical to every successful exchange that does finish the
03881     // whole setup process successfully, on both the KL25Z and Windows
03882     // sides of the connection.  I can't find any point of difference
03883     // between successful and unsuccessful sequences that suggests why
03884     // the fateful message fails.  This makes me suspect that whatever
03885     // is going wrong is inside the KL25Z USB hardware module, which 
03886     // is a pretty substantial black box - it has a lot of internal 
03887     // state that's inaccessible to the software.  Further bolstering 
03888     // this theory is a little experiment where I found that I could 
03889     // reproduce the exact sequence of events of a failed reconnect 
03890     // attempt in an *initial* connection, which is otherwise 100% 
03891     // reliable, by inserting a little bit of artifical time padding 
03892     // (200us per event) into the SETUP interrupt handler.  My
03893     // hypothesis is that the STALL event happens because the KL25Z
03894     // USB hardware is too slow to respond to a message.  I'm not 
03895     // sure why this would only happen after a disconnect and not
03896     // during the initial connection; maybe there's some reset work
03897     // in the hardware that takes a substantial amount of time after
03898     // a disconnect.
03899     //
03900     // The solution: the problem happens during the SETUP exchange,
03901     // after we've been assigned a bus address.  It only happens on
03902     // some percentage of connection requests, so if we can simply
03903     // start over when the failure occurs, we'll eventually succeed
03904     // simply because not every attempt fails.  The ideal would be
03905     // to get the success rate up to 100%, but I can't figure out how
03906     // to fix the underlying problem, so this is the next best thing.
03907     //
03908     // We can detect when the failure occurs by noticing when a SLEEP
03909     // interrupt happens while we have an assigned bus address.
03910     //
03911     // To start a new connection attempt, we have to make the *host*
03912     // try again.  The logical connection is initiated solely by the
03913     // host.  Fortunately, it's easy to get the host to initiate the
03914     // process: if we disconnect on the device side, it effectively
03915     // makes the device look to the PC like it's electrically unplugged.
03916     // When we reconnect on the device side, the PC thinks a new device
03917     // has been plugged in and initiates the logical connection setup.
03918     // We have to remain disconnected for some minimum interval before
03919     // the host notices; the exact minimum is unclear, but 5ms seems 
03920     // reliable in practice.
03921     // 
03922     // Here's the full algorithm:
03923     //
03924     // 1. In the SLEEP interrupt handler, if we have a bus address,
03925     // we disconnect the device.  This happens in ISR context, so we
03926     // can't wait around for 5ms.  Instead, we simply set a flag noting
03927     // that the connection has been broken, and we note the time and
03928     // return.
03929     //
03930     // 2. In our main loop, whenever we find that we're disconnected,
03931     // we call recoverConnection().  The main loop's job is basically a
03932     // bunch of device polling.  We're just one more device to poll, so
03933     // recoverConnection() will be called soon after a disconnect, and
03934     // then will be called in a loop for as long as we're disconnected.
03935     //
03936     // 3. In recoverConnection(), we check the flag we set in the SLEEP
03937     // handler.  If set, we wait until 5ms has elapsed from the SLEEP
03938     // event time that we noted, then we'll reconnect and clear the flag.
03939     // This gives us the required 5ms (or longer) delay between the
03940     // disconnect and reconnect, ensuring that the PC will notice and
03941     // will start over with the connection protocol.
03942     //
03943     // 4. The main loop keeps calling recoverConnection() in a loop for
03944     // as long as we're disconnected, so if the new connection attempt
03945     // triggered in step 3 fails, the SLEEP interrupt will happen again,
03946     // we'll disconnect again, the flag will get set again, and 
03947     // recoverConnection() will reconnect again after another suitable
03948     // delay.  This will repeat until the connection succeeds or hell
03949     // freezes over.  
03950     //
03951     // Each disconnect happens immediately when a reconnect attempt 
03952     // fails, and an entire successful connection only takes about 25ms, 
03953     // so our loop can retry at more than 30 attempts per second.  
03954     // In my testing, lost connections almost always reconnect in
03955     // less than second with this code in place.
03956     void recoverConnection()
03957     {
03958         // if a reconnect is pending, reconnect
03959         if (reconnectPending_)
03960         {
03961             // Loop until we reach 5ms after the last sleep event.
03962             for (bool done = false ; !done ; )
03963             {
03964                 // If we've reached the target time, reconnect.  Do the
03965                 // time check and flag reset atomically, so that we can't
03966                 // have another sleep event sneak in after we've verified
03967                 // the time.  If another event occurs, it has to happen
03968                 // before we check, in which case it'll update the time
03969                 // before we check it, or after we clear the flag, in
03970                 // which case it will reset the flag and we'll do another
03971                 // round the next time we call this routine.
03972                 __disable_irq();
03973                 if (uint32_t(timer_.read_us() - lastSleepTime_) > 5000)
03974                 {
03975                     connect(false);
03976                     reconnectPending_ = false;
03977                     done = true;
03978                 }
03979                 __enable_irq();
03980             }
03981         }
03982     }
03983     
03984 protected:
03985     // Handle a USB SLEEP interrupt.  This interrupt signifies that the
03986     // USB hardware module hasn't seen any token traffic for 3ms, which 
03987     // means that we're either physically or logically disconnected. 
03988     //
03989     // Important: this runs in ISR context.
03990     //
03991     // Note that this is a specialized sense of "sleep" that's unrelated 
03992     // to the similarly named power modes on the PC.  This has nothing
03993     // to do with suspend/sleep mode on the PC, and it's not a low-power
03994     // mode on the KL25Z.  They really should have called this interrupt 
03995     // DISCONNECT or BROKEN CONNECTION.)
03996     virtual void sleepStateChanged(unsigned int sleeping)
03997     { 
03998         // note the new state
03999         sleeping_ = sleeping;
04000         
04001         // If we have a non-zero bus address, we have at least a partial
04002         // connection to the host (we've made it at least as far as the
04003         // SETUP stage).  Explicitly disconnect, and the pending reconnect
04004         // flag, and remember the time of the sleep event.
04005         if (USB0->ADDR != 0x00)
04006         {
04007             disconnect();
04008             lastSleepTime_ = timer_.read_us();
04009             reconnectPending_ = true;
04010         }
04011     }
04012     
04013     // is the USB connection asleep?
04014     volatile bool sleeping_; 
04015     
04016     // flag: reconnect pending after sleep event
04017     volatile bool reconnectPending_;
04018     
04019     // time of last sleep event while connected
04020     volatile uint32_t lastSleepTime_;
04021     
04022     // timer to keep track of interval since last sleep event
04023     Timer timer_;
04024 };
04025 
04026 // ---------------------------------------------------------------------------
04027 // 
04028 // Accelerometer (MMA8451Q)
04029 //
04030 
04031 // The MMA8451Q is the KL25Z's on-board 3-axis accelerometer.
04032 //
04033 // This is a custom wrapper for the library code to interface to the
04034 // MMA8451Q.  This class encapsulates an interrupt handler and 
04035 // automatic calibration.
04036 //
04037 // We collect data at the device's maximum rate of 800kHz (one sample 
04038 // every 1.25ms).  To keep up with the high data rate, we use the 
04039 // device's internal FIFO, and drain the FIFO by polling on each 
04040 // iteration of our main application loop.  In the past, we used an
04041 // interrupt handler to read the device immediately on the arrival of
04042 // each sample, but this created too much latency for the IR remote
04043 // receiver, due to the relatively long time it takes to transfer the
04044 // accelerometer readings via I2C.  The device's on-board FIFO can
04045 // store up to 32 samples, which gives us up to about 40ms between
04046 // polling iterations before the buffer overflows.  Our main loop runs
04047 // in under 2ms, so we can easily keep the FIFO far from overflowing.
04048 //
04049 // The MMA8451Q has three range modes, +/- 2G, 4G, and 8G.  The ADC
04050 // sample is the same bit width (14 bits) in all modes, so the higher
04051 // dynamic range modes trade physical precision for range.  For our
04052 // purposes, precision is more important than range, so we use the
04053 // +/-2G mode.  Further, our joystick range is calibrated for only
04054 // +/-1G.  This was unintentional on my part; I didn't look at the
04055 // MMA8451Q library closely enough to realize it was normalizing to
04056 // actual "G" units, and assumed that it was normalizing to a -1..+1 
04057 // scale.  In practice, a +/-1G scale seems perfectly adequate for
04058 // virtual pinball use, so I'm sticking with that range for now.  But
04059 // there might be some benefit in renormalizing to a +/-2G range, in
04060 // that it would allow for higher dynamic range for very hard nudges.
04061 // Everyone would have to tweak their nudge sensitivity in VP if I
04062 // made that change, though, so I'm keeping it as is for now; it would
04063 // be best to make it a config option ("accelerometer high dynamic range") 
04064 // rather than change it across the board.
04065 //
04066 // We automatically calibrate the accelerometer so that it's not
04067 // necessary to get it exactly level when installing it, and so
04068 // that it's also not necessary to calibrate it manually.  There's
04069 // lots of experience that tells us that manual calibration is a
04070 // terrible solution, mostly because cabinets tend to shift slightly
04071 // during use, requiring frequent recalibration.  Instead, we
04072 // calibrate automatically.  We continuously monitor the acceleration
04073 // data, watching for periods of constant (or nearly constant) values.
04074 // Any time it appears that the machine has been at rest for a while
04075 // (about 5 seconds), we'll average the readings during that rest
04076 // period and use the result as the level rest position.  This is
04077 // is ongoing, so we'll quickly find the center point again if the 
04078 // machine is moved during play (by an especially aggressive bout
04079 // of nudging, say).
04080 //
04081 
04082 // I2C address of the accelerometer (this is a constant of the KL25Z)
04083 const int MMA8451_I2C_ADDRESS = (0x1d<<1);
04084 
04085 // SCL and SDA pins for the accelerometer (constant for the KL25Z)
04086 #define MMA8451_SCL_PIN   PTE25
04087 #define MMA8451_SDA_PIN   PTE24
04088 
04089 // Digital in pin to use for the accelerometer interrupt.  For the KL25Z,
04090 // this can be either PTA14 or PTA15, since those are the pins physically
04091 // wired on this board to the MMA8451 interrupt controller.
04092 #define MMA8451_INT_PIN   PTA15
04093 
04094 
04095 // accelerometer input history item, for gathering calibration data
04096 struct AccHist
04097 {
04098     AccHist() { x = y = dsq = 0; xtot = ytot = 0; cnt = 0; }
04099     void set(int x, int y, AccHist *prv)
04100     {
04101         // save the raw position
04102         this->x = x;
04103         this->y = y;
04104         this->dsq = distanceSquared(prv);
04105     }
04106     
04107     // reading for this entry
04108     int x, y;
04109     
04110     // (distance from previous entry) squared
04111     int dsq;
04112     
04113     // total and count of samples averaged over this period
04114     int xtot, ytot;
04115     int cnt;
04116 
04117     void clearAvg() { xtot = ytot = 0; cnt = 0; }    
04118     void addAvg(int x, int y) { xtot += x; ytot += y; ++cnt; }
04119     int xAvg() const { return xtot/cnt; }
04120     int yAvg() const { return ytot/cnt; }
04121     
04122     int distanceSquared(AccHist *p)
04123         { return square(p->x - x) + square(p->y - y); }
04124 };
04125 
04126 // accelerometer wrapper class
04127 class Accel
04128 {
04129 public:
04130     Accel(PinName sda, PinName scl, int i2cAddr, PinName irqPin, 
04131         int range, int autoCenterMode)
04132         : mma_(sda, scl, i2cAddr)        
04133     {
04134         // remember the interrupt pin assignment
04135         irqPin_ = irqPin;
04136         
04137         // remember the range
04138         range_ = range;
04139         
04140         // set the auto-centering mode
04141         setAutoCenterMode(autoCenterMode);
04142         
04143         // no manual centering request has been received
04144         manualCenterRequest_ = false;
04145 
04146         // reset and initialize
04147         reset();
04148     }
04149     
04150     // Request manual centering.  This applies the trailing average
04151     // of recent measurements and applies it as the new center point
04152     // as soon as we have enough data.
04153     void manualCenterRequest() { manualCenterRequest_ = true; }
04154     
04155     // set the auto-centering mode
04156     void setAutoCenterMode(int mode)
04157     {
04158         // remember the mode
04159         autoCenterMode_ = mode;
04160         
04161         // Set the time between checks.  We check 5 times over the course
04162         // of the centering time, so the check interval is 1/5 of the total.
04163         if (mode == 0)
04164         {
04165             // mode 0 is the old default of 5 seconds, so check every 1s
04166             autoCenterCheckTime_ = 1000000;
04167         }
04168         else if (mode <= 60)
04169         {
04170             // mode 1-60 means reset after 'mode' seconds; the check
04171             // interval is 1/5 of this
04172             autoCenterCheckTime_ = mode*200000;
04173         }
04174         else
04175         {
04176             // Auto-centering is off, but still gather statistics to apply
04177             // when we get a manual centering request.  The check interval
04178             // in this case is 1/5 of the total time for the trailing average
04179             // we apply for the manual centering.  We want this to be long
04180             // enough to smooth out the data, but short enough that it only
04181             // includes recent data.
04182             autoCenterCheckTime_ = 500000;
04183         }
04184     }
04185     
04186     void reset()
04187     {
04188         // clear the center point
04189         cx_ = cy_ = 0;
04190         
04191         // start the auto-centering timer
04192         tCenter_.start();
04193         iAccPrv_ = nAccPrv_ = 0;
04194         
04195         // reset and initialize the MMA8451Q
04196         mma_.init();
04197         
04198         // set the range
04199         mma_.setRange(
04200             range_ == AccelRange4G ? 4 :
04201             range_ == AccelRange8G ? 8 :
04202             2);
04203                 
04204         // set the average accumulators to zero
04205         xSum_ = ySum_ = 0;
04206         nSum_ = 0;
04207         
04208         // read the current registers to clear the data ready flag
04209         mma_.getAccXYZ(ax_, ay_, az_);
04210     }
04211     
04212     void poll()
04213     {
04214         // read samples until we clear the FIFO
04215         while (mma_.getFIFOCount() != 0)
04216         {
04217             int x, y, z;
04218             mma_.getAccXYZ(x, y, z);
04219             
04220             // add the new reading to the running total for averaging
04221             xSum_ += (x - cx_);
04222             ySum_ += (y - cy_);
04223             ++nSum_;
04224             
04225             // store the updates
04226             ax_ = x;
04227             ay_ = y;
04228             az_ = z;
04229         }
04230     }
04231 
04232     void get(int &x, int &y) 
04233     {
04234         // read the shared data and store locally for calculations
04235         int ax = ax_, ay = ay_;
04236         int xSum = xSum_, ySum = ySum_;
04237         int nSum = nSum_;
04238          
04239         // reset the average accumulators for the next run
04240         xSum_ = ySum_ = 0;
04241         nSum_ = 0;
04242 
04243         // add this sample to the current calibration interval's running total
04244         AccHist *p = accPrv_ + iAccPrv_;
04245         p->addAvg(ax, ay);
04246 
04247         // If we're in auto-centering mode, check for auto-centering
04248         // at intervals of 1/5 of the overall time.  If we're not in
04249         // auto-centering mode, check anyway at one-second intervals
04250         // so that we gather averages for manual centering requests.
04251         if (tCenter_.read_us() > autoCenterCheckTime_)
04252         {
04253             // add the latest raw sample to the history list
04254             AccHist *prv = p;
04255             iAccPrv_ = (iAccPrv_ + 1);
04256             if (iAccPrv_ >= maxAccPrv)
04257                iAccPrv_ = 0;
04258             p = accPrv_ + iAccPrv_;
04259             p->set(ax, ay, prv);
04260 
04261             // if we have a full complement, check for auto-centering
04262             if (nAccPrv_ >= maxAccPrv)
04263             {
04264                 // Center if:
04265                 //
04266                 // - Auto-centering is on, and we've been stable over the
04267                 //   whole sample period at our spot-check points
04268                 //
04269                 // - A manual centering request is pending
04270                 //
04271                 static const int accTol = 164*164;  // 1% of range, squared
04272                 AccHist *p0 = accPrv_;
04273                 if (manualCenterRequest_
04274                     || (autoCenterMode_ <= 60
04275                         && p0[0].dsq < accTol
04276                         && p0[1].dsq < accTol
04277                         && p0[2].dsq < accTol
04278                         && p0[3].dsq < accTol
04279                         && p0[4].dsq < accTol))
04280                 {
04281                     // Figure the new calibration point as the average of
04282                     // the samples over the rest period
04283                     cx_ = (p0[0].xAvg() + p0[1].xAvg() + p0[2].xAvg() + p0[3].xAvg() + p0[4].xAvg())/5;
04284                     cy_ = (p0[0].yAvg() + p0[1].yAvg() + p0[2].yAvg() + p0[3].yAvg() + p0[4].yAvg())/5;
04285                     
04286                     // clear any pending manual centering request
04287                     manualCenterRequest_ = false;
04288                 }
04289             }
04290             else
04291             {
04292                // not enough samples yet; just up the count
04293                ++nAccPrv_;
04294             }
04295              
04296             // clear the new item's running totals
04297             p->clearAvg();
04298             
04299             // reset the timer
04300             tCenter_.reset();
04301         }
04302          
04303         // report our integrated velocity reading in x,y
04304         x = rawToReport(xSum/nSum);
04305         y = rawToReport(ySum/nSum);
04306          
04307 #ifdef DEBUG_PRINTF
04308         if (x != 0 || y != 0)        
04309             printf("%f %f %d %d %f\r\n", vx, vy, x, y, dt);
04310 #endif
04311     }    
04312          
04313 private:
04314     // adjust a raw acceleration figure to a usb report value
04315     int rawToReport(int v)
04316     {
04317         // Scale to the joystick report range.  The accelerometer
04318         // readings use the native 14-bit signed integer representation,
04319         // so their scale is 2^13.
04320         //
04321         // The 1G range is special: it uses the 2G native hardware range,
04322         // but rescales the result to a 1G range for the joystick reports.
04323         // So for that mode, we divide by 4096 rather than 8192.  All of
04324         // the other modes map use the hardware scaling directly.
04325         int i = v*JOYMAX;
04326         i = (range_ == AccelRange1G ? i/4096 : i/8192);
04327         
04328         // if it's near the center, scale it roughly as 20*(i/20)^2,
04329         // to suppress noise near the rest position
04330         static const int filter[] = { 
04331             -18, -16, -14, -13, -11, -10, -8, -7, -6, -5, -4, -3, -2, -2, -1, -1, 0, 0, 0, 0,
04332             0,
04333             0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 16, 18
04334         };
04335         return (i > 20 || i < -20 ? i : filter[i+20]);
04336     }
04337 
04338     // underlying accelerometer object
04339     MMA8451Q mma_;
04340     
04341     // last raw acceleration readings, on the device's signed 14-bit 
04342     // scale -8192..+8191
04343     int ax_, ay_, az_;
04344     
04345     // running sum of readings since last get()
04346     int xSum_, ySum_;
04347     
04348     // number of readings since last get()
04349     int nSum_;
04350         
04351     // Calibration reference point for accelerometer.  This is the
04352     // average reading on the accelerometer when in the neutral position
04353     // at rest.
04354     int cx_, cy_;
04355     
04356     // range (AccelRangeXxx value, from config.h)
04357     uint8_t range_;
04358     
04359     // auto-center mode: 
04360     //   0 = default of 5-second auto-centering
04361     //   1-60 = auto-center after this many seconds
04362     //   255 = auto-centering off (manual centering only)
04363     uint8_t autoCenterMode_;
04364     
04365     // flag: a manual centering request is pending
04366     bool manualCenterRequest_;
04367 
04368     // time in us between auto-centering incremental checks
04369     uint32_t autoCenterCheckTime_;
04370     
04371     // atuo-centering timer
04372     Timer tCenter_;
04373 
04374     // Auto-centering history.  This is a separate history list that
04375     // records results spaced out sparsely over time, so that we can
04376     // watch for long-lasting periods of rest.  When we observe nearly
04377     // no motion for an extended period (on the order of 5 seconds), we
04378     // take this to mean that the cabinet is at rest in its neutral 
04379     // position, so we take this as the calibration zero point for the
04380     // accelerometer.  We update this history continuously, which allows
04381     // us to continuously re-calibrate the accelerometer.  This ensures
04382     // that we'll automatically adjust to any actual changes in the
04383     // cabinet's orientation (e.g., if it gets moved slightly by an
04384     // especially strong nudge) as well as any systematic drift in the
04385     // accelerometer measurement bias (e.g., from temperature changes).
04386     uint8_t iAccPrv_, nAccPrv_;
04387     static const uint8_t maxAccPrv = 5;
04388     AccHist accPrv_[maxAccPrv];
04389     
04390     // interurupt pin name
04391     PinName irqPin_;
04392 };
04393 
04394 // ---------------------------------------------------------------------------
04395 //
04396 // Clear the I2C bus for the MMA8451Q.  This seems necessary some of the time
04397 // for reasons that aren't clear to me.  Doing a hard power cycle has the same
04398 // effect, but when we do a soft reset, the hardware sometimes seems to leave
04399 // the MMA's SDA line stuck low.  Forcing a series of 9 clock pulses through
04400 // the SCL line is supposed to clear this condition.  I'm not convinced this
04401 // actually works with the way this component is wired on the KL25Z, but it
04402 // seems harmless, so we'll do it on reset in case it does some good.  What
04403 // we really seem to need is a way to power cycle the MMA8451Q if it ever 
04404 // gets stuck, but this is simply not possible in software on the KL25Z. 
04405 // 
04406 // If the accelerometer does get stuck, and a software reboot doesn't reset
04407 // it, the only workaround is to manually power cycle the whole KL25Z by 
04408 // unplugging both of its USB connections.
04409 //
04410 void clear_i2c()
04411 {
04412     // set up general-purpose output pins to the I2C lines
04413     DigitalOut scl(MMA8451_SCL_PIN);
04414     DigitalIn sda(MMA8451_SDA_PIN);
04415     
04416     // clock the SCL 9 times
04417     for (int i = 0 ; i < 9 ; ++i)
04418     {
04419         scl = 1;
04420         wait_us(20);
04421         scl = 0;
04422         wait_us(20);
04423     }
04424 }
04425 
04426 
04427 // ---------------------------------------------------------------------------
04428 //
04429 // Simple binary (on/off) input debouncer.  Requires an input to be stable 
04430 // for a given interval before allowing an update.
04431 //
04432 class Debouncer
04433 {
04434 public:
04435     Debouncer(bool initVal, float tmin)
04436     {
04437         t.start();
04438         this->stable = this->prv = initVal;
04439         this->tmin = tmin;
04440     }
04441     
04442     // Get the current stable value
04443     bool val() const { return stable; }
04444 
04445     // Apply a new sample.  This tells us the new raw reading from the
04446     // input device.
04447     void sampleIn(bool val)
04448     {
04449         // If the new raw reading is different from the previous
04450         // raw reading, we've detected an edge - start the clock
04451         // on the sample reader.
04452         if (val != prv)
04453         {
04454             // we have an edge - reset the sample clock
04455             t.reset();
04456             
04457             // this is now the previous raw sample for nxt time
04458             prv = val;
04459         }
04460         else if (val != stable)
04461         {
04462             // The new raw sample is the same as the last raw sample,
04463             // and different from the stable value.  This means that
04464             // the sample value has been the same for the time currently
04465             // indicated by our timer.  If enough time has elapsed to
04466             // consider the value stable, apply the new value.
04467             if (t.read() > tmin)
04468                 stable = val;
04469         }
04470     }
04471     
04472 private:
04473     // current stable value
04474     bool stable;
04475 
04476     // last raw sample value
04477     bool prv;
04478     
04479     // elapsed time since last raw input change
04480     Timer t;
04481     
04482     // Minimum time interval for stability, in seconds.  Input readings 
04483     // must be stable for this long before the stable value is updated.
04484     float tmin;
04485 };
04486 
04487 
04488 // ---------------------------------------------------------------------------
04489 //
04490 // TV ON timer.  If this feature is enabled, we toggle a TV power switch
04491 // relay (connected to a GPIO pin) to turn on the cab's TV monitors shortly
04492 // after the system is powered.  This is useful for TVs that don't remember
04493 // their power state and don't turn back on automatically after being
04494 // unplugged and plugged in again.  This feature requires external
04495 // circuitry, which is built in to the expansion board and can also be
04496 // built separately - see the Build Guide for the circuit plan.
04497 //
04498 // Theory of operation: to use this feature, the cabinet must have a 
04499 // secondary PC-style power supply (PSU2) for the feedback devices, and
04500 // this secondary supply must be plugged in to the same power strip or 
04501 // switched outlet that controls power to the TVs.  This lets us use PSU2
04502 // as a proxy for the TV power state - when PSU2 is on, the TV outlet is 
04503 // powered, and when PSU2 is off, the TV outlet is off.  We use a little 
04504 // latch circuit powered by PSU2 to monitor the status.  The latch has a 
04505 // current state, ON or OFF, that we can read via a GPIO input pin, and 
04506 // we can set the state to ON by pulsing a separate GPIO output pin.  As 
04507 // long as PSU2 is powered off, the latch stays in the OFF state, even if 
04508 // we try to set it by pulsing the SET pin.  When PSU2 is turned on after 
04509 // being off, the latch starts receiving power but stays in the OFF state, 
04510 // since this is the initial condition when the power first comes on.  So 
04511 // if our latch state pin is reading OFF, we know that PSU2 is either off 
04512 // now or *was* off some time since we last checked.  We use a timer to 
04513 // check the state periodically.  Each time we see the state is OFF, we 
04514 // try pulsing the SET pin.  If the state still reads as OFF, we know 
04515 // that PSU2 is currently off; if the state changes to ON, though, we 
04516 // know that PSU2 has gone from OFF to ON some time between now and the 
04517 // previous check.  When we see this condition, we start a countdown
04518 // timer, and pulse the TV switch relay when the countdown ends.
04519 //
04520 // This scheme might seem a little convoluted, but it handles a number
04521 // of tricky but likely scenarios:
04522 //
04523 // - Most cabinets systems are set up with "soft" PC power switches, 
04524 //   so that the PC goes into "Soft Off" mode when the user turns off
04525 //   the cabinet by pushing the power button or using the Shut Down
04526 //   command from within Windows.  In Windows parlance, this "soft off"
04527 //   condition is called ACPI State S5.  In this state, the main CPU
04528 //   power is turned off, but the motherboard still provides power to
04529 //   USB devices.  This means that the KL25Z keeps running.  Without
04530 //   the external power sensing circuit, the only hint that we're in 
04531 //   this state is that the USB connection to the host goes into Suspend
04532 //   mode, but that could mean other things as well.  The latch circuit
04533 //   lets us tell for sure that we're in this state.
04534 //
04535 // - Some cabinet builders might prefer to use "hard" power switches,
04536 //   cutting all power to the cabinet, including the PC motherboard (and
04537 //   thus the KL25Z) every time the machine is turned off.  This also
04538 //   applies to the "soft" switch case above when the cabinet is unplugged,
04539 //   a power outage occurs, etc.  In these cases, the KL25Z will do a cold
04540 //   boot when the PC is turned on.  We don't know whether the KL25Z
04541 //   will power up before or after PSU2, so it's not good enough to 
04542 //   observe the current state of PSU2 when we first check.  If PSU2
04543 //   were to come on first, checking only the current state would fool
04544 //   us into thinking that no action is required, because we'd only see
04545 //   that PSU2 is turned on any time we check.  The latch handles this 
04546 //   case by letting us see that PSU2 was indeed off some time before our
04547 //   first check.
04548 //
04549 // - If the KL25Z is rebooted while the main system is running, or the 
04550 //   KL25Z is unplugged and plugged back in, we'll correctly leave the 
04551 //   TVs as they are.  The latch state is independent of the KL25Z's 
04552 //   power or software state, so it's won't affect the latch state when
04553 //   the KL25Z is unplugged or rebooted; when we boot, we'll see that 
04554 //   the latch is already on and that we don't have to turn on the TVs.
04555 //   This is important because TV ON buttons are usually on/off toggles,
04556 //   so we don't want to push the button on a TV that's already on.
04557 //   
04558 
04559 // Current PSU2 power state:
04560 //   1 -> default: latch was on at last check, or we haven't checked yet
04561 //   2 -> latch was off at last check, SET pulsed high
04562 //   3 -> SET pulsed low, ready to check status
04563 //   4 -> TV timer countdown in progress
04564 //   5 -> TV relay on
04565 //   6 -> sending IR signals designed as TV ON signals
04566 uint8_t psu2_state = 1;
04567 
04568 // TV relay state.  The TV relay can be controlled by the power-on
04569 // timer and directly from the PC (via USB commands), so keep a
04570 // separate state for each:
04571 //   0x01 -> turned on by power-on timer
04572 //   0x02 -> turned on by USB command
04573 uint8_t tv_relay_state = 0x00;
04574 const uint8_t TV_RELAY_POWERON = 0x01;
04575 const uint8_t TV_RELAY_USB     = 0x02;
04576 
04577 // pulse timer for manual TV relay pulses
04578 Timer tvRelayManualTimer;
04579 
04580 // TV ON IR command state.  When the main PSU2 power state reaches
04581 // the IR phase, we use this sub-state counter to send the TV ON
04582 // IR signals.  We initialize to state 0 when the main state counter
04583 // reaches the IR step.  In state 0, we start transmitting the first
04584 // (lowest numbered) IR command slot marked as containing a TV ON
04585 // code, and advance to state 1.  In state 1, we check to see if
04586 // the transmitter is still sending; if so, we do nothing, if so
04587 // we start transmitting the second TV ON code and advance to state
04588 // 2.  Continue until we run out of TV ON IR codes, at which point
04589 // we advance to the next main psu2_state step.
04590 uint8_t tvon_ir_state = 0;
04591 
04592 // TV ON switch relay control output pin
04593 DigitalOut *tv_relay;
04594 
04595 // PSU2 power sensing circuit connections
04596 DigitalIn *psu2_status_sense;
04597 DigitalOut *psu2_status_set;
04598 
04599 // Apply the current TV relay state
04600 void tvRelayUpdate(uint8_t bit, bool state)
04601 {
04602     // update the state
04603     if (state)
04604         tv_relay_state |= bit;
04605     else
04606         tv_relay_state &= ~bit;
04607     
04608     // set the relay GPIO to the new state
04609     if (tv_relay != 0)
04610         tv_relay->write(tv_relay_state != 0);
04611 }
04612 
04613 // Does the current power status allow a reboot?  We shouldn't reboot
04614 // in certain power states, because some states are purely internal:
04615 // we can't get enough information from the external power sensor to 
04616 // return to the same state later.  Code that performs discretionary
04617 // reboots should always check here first, and delay any reboot until
04618 // we say it's okay.
04619 static inline bool powerStatusAllowsReboot()
04620 {
04621     // The only safe state for rebooting is state 1, idle/default.
04622     // In other states, we can't reboot, because the external sensor
04623     // and latch circuit doesn't give us enough information to return
04624     // to the same state later.
04625     return psu2_state == 1;
04626 }
04627 
04628 // PSU2 Status update routine.  The main loop calls this from time 
04629 // to time to update the power sensing state and carry out TV ON 
04630 // functions.
04631 Timer powerStatusTimer;
04632 uint32_t tv_delay_time_us;
04633 void powerStatusUpdate(Config &cfg)
04634 {
04635     // If the manual relay pulse timer is past the pulse time, end the
04636     // manual pulse.  The timer only runs when a pulse is active, so
04637     // it'll never read as past the time limit if a pulse isn't on.
04638     if (tvRelayManualTimer.read_us() > 250000)
04639     {
04640         // turn off the relay and disable the timer
04641         tvRelayUpdate(TV_RELAY_USB, false);
04642         tvRelayManualTimer.stop();
04643         tvRelayManualTimer.reset();
04644     }
04645 
04646     // Only update every 1/4 second or so.  Note that if the PSU2
04647     // circuit isn't configured, the initialization routine won't 
04648     // start the timer, so it'll always read zero and we'll always 
04649     // skip this whole routine.
04650     if (powerStatusTimer.read_us() < 250000)
04651         return;
04652         
04653     // reset the update timer for next time
04654     powerStatusTimer.reset();
04655     
04656     // TV ON timer.  We start this timer when we detect a change
04657     // in the PSU2 status from OFF to ON.  When the timer reaches
04658     // the configured TV ON delay time, and the PSU2 power is still
04659     // on, we'll trigger the TV ON relay and send the TV ON IR codes.
04660     static Timer tv_timer;
04661 
04662     // Check our internal state
04663     switch (psu2_state)
04664     {
04665     case 1:
04666         // Default state.  This means that the latch was on last
04667         // time we checked or that this is the first check.  In
04668         // either case, if the latch is off, switch to state 2 and
04669         // try pulsing the latch.  Next time we check, if the latch
04670         // stuck, it means that PSU2 is now on after being off.
04671         if (!psu2_status_sense->read())
04672         {
04673             // switch to OFF state
04674             psu2_state = 2;
04675             
04676             // try setting the latch
04677             psu2_status_set->write(1);
04678         }
04679         powerTimerDiagState = 0;
04680         break;
04681         
04682     case 2:
04683         // PSU2 was off last time we checked, and we tried setting
04684         // the latch.  Drop the SET signal and go to CHECK state.
04685         psu2_status_set->write(0);
04686         psu2_state = 3;
04687         powerTimerDiagState = 0;
04688         break;
04689         
04690     case 3:
04691         // CHECK state: we pulsed SET, and we're now ready to see
04692         // if it stuck.  If the latch is now on, PSU2 has transitioned
04693         // from OFF to ON, so start the TV countdown.  If the latch is
04694         // off, our SET command didn't stick, so PSU2 is still off.
04695         if (psu2_status_sense->read())
04696         {
04697             // The latch stuck, so PSU2 has transitioned from OFF
04698             // to ON.  Start the TV countdown timer.
04699             tv_timer.reset();
04700             tv_timer.start();
04701             psu2_state = 4;
04702             
04703             // start the power timer diagnostic flashes
04704             powerTimerDiagState = 2;
04705         }
04706         else
04707         {
04708             // The latch didn't stick, so PSU2 was still off at
04709             // our last check.  Return to idle state.
04710             psu2_state = 1;
04711         }
04712         break;
04713         
04714     case 4:
04715         // TV timer countdown in progress.  The latch has to stay on during
04716         // the countdown; if the latch turns off, PSU2 power must have gone
04717         // off again before the countdown finished.
04718         if (!psu2_status_sense->read())
04719         {
04720             // power is off - start a new check cycle
04721             psu2_status_set->write(1);
04722             psu2_state = 2;
04723             break;
04724         }
04725         
04726         // Flash the power time diagnostic every two cycles
04727         powerTimerDiagState = (powerTimerDiagState + 1) & 0x03;
04728         
04729         // if we've reached the delay time, pulse the relay
04730         if (tv_timer.read_us() >= tv_delay_time_us)
04731         {
04732             // turn on the relay for one timer interval
04733             tvRelayUpdate(TV_RELAY_POWERON, true);
04734             psu2_state = 5;
04735             
04736             // show solid blue on the diagnostic LED while the relay is on
04737             powerTimerDiagState = 2;
04738         }
04739         break;
04740         
04741     case 5:
04742         // TV timer relay on.  We pulse this for one interval, so
04743         // it's now time to turn it off.
04744         tvRelayUpdate(TV_RELAY_POWERON, false);
04745         
04746         // Proceed to sending any TV ON IR commands
04747         psu2_state = 6;
04748         tvon_ir_state = 0;
04749     
04750         // diagnostic LEDs off for now
04751         powerTimerDiagState = 0;
04752         break;
04753         
04754     case 6:        
04755         // Sending TV ON IR signals.  Start with the assumption that
04756         // we have no IR work to do, in which case we're done with the
04757         // whole TV ON sequence.  So by default return to state 1.
04758         psu2_state = 1;
04759         powerTimerDiagState = 0;
04760         
04761         // If we have an IR emitter, check for TV ON IR commands
04762         if (ir_tx != 0)
04763         {
04764             // check to see if the last transmission is still in progress
04765             if (ir_tx->isSending())
04766             {
04767                 // We're still sending the last transmission.  Stay in
04768                 // state 6.
04769                 psu2_state = 6;
04770                 powerTimerDiagState = 4;
04771                 break;
04772             }
04773                 
04774             // The last transmission is done, so check for a new one.
04775             // Look for the Nth TV ON IR slot, where N is our state
04776             // number.
04777             for (int i = 0, n = 0 ; i < MAX_IR_CODES ; ++i)
04778             {
04779                 // is this a TV ON command?
04780                 if ((cfg.IRCommand[i].flags & IRFlagTVON) != 0)
04781                 {
04782                     // It's a TV ON command - check if it's the one we're
04783                     // looking for.  We can match any code starting at the
04784                     // current state.  (We ignore codes BEFORE the current
04785                     // state, because we've already processed them on past
04786                     // iterations.)
04787                     if (n >= tvon_ir_state)
04788                     {
04789                         // It's the one.  Start transmitting it by
04790                         // pushing its virtual button.
04791                         int vb = IRConfigSlotToVirtualButton[i];
04792                         ir_tx->pushButton(vb, true);
04793                         
04794                         // Pushing the button starts transmission, and once
04795                         // started, the transmission runs to completion even
04796                         // if the button is no longer pushed.  So we can 
04797                         // immediately un-push the button, since we only need
04798                         // to send the code once.
04799                         ir_tx->pushButton(vb, false);
04800                         
04801                         // Advance to the next TV ON IR state, where we'll
04802                         // await the end of this transmission and move on to
04803                         // the next one.
04804                         psu2_state = 6;
04805                         tvon_ir_state++;
04806                         break;
04807                     }
04808                     
04809                     // it's not ours - count it and keep looking
04810                     ++n;
04811                 }
04812             }
04813         }
04814         break;
04815     }
04816     
04817     // update the diagnostic LEDs
04818     diagLED();
04819 }
04820 
04821 // Start the power status timer.  If the status sense circuit is enabled 
04822 // in the configuration, we'll set up the pin connections and start the
04823 // timer for our periodic status checks.  Does nothing if any of the pins 
04824 // are configured as NC.
04825 void startPowerStatusTimer(Config &cfg)
04826 {
04827     // only start the timer if the pins are configured and the delay
04828     // time is nonzero
04829     powerStatusTimer.reset();
04830     if (cfg.TVON.statusPin != 0xFF 
04831         && cfg.TVON.latchPin != 0xFF)
04832     {
04833         // set up the power sensing circuit connections
04834         psu2_status_sense = new DigitalIn(wirePinName(cfg.TVON.statusPin));
04835         psu2_status_set = new DigitalOut(wirePinName(cfg.TVON.latchPin));
04836         
04837         // if there's a TV ON relay, set up its control pin
04838         if (cfg.TVON.relayPin != 0xFF)
04839             tv_relay = new DigitalOut(wirePinName(cfg.TVON.relayPin));
04840             
04841         // Set the TV ON delay time.  We store the time internally in
04842         // microseconds, but the configuration stores it in units of
04843         // 1/100 second = 10ms = 10000us.
04844         tv_delay_time_us = cfg.TVON.delayTime * 10000;;
04845     
04846         // Start the TV timer
04847         powerStatusTimer.start();
04848     }
04849 }
04850 
04851 // Operate the TV ON relay.  This allows manual control of the relay
04852 // from the PC.  See protocol message 65 submessage 11.
04853 //
04854 // Mode:
04855 //    0 = turn relay off
04856 //    1 = turn relay on
04857 //    2 = pulse relay 
04858 void TVRelay(int mode)
04859 {
04860     // if there's no TV relay control pin, ignore this
04861     if (tv_relay == 0)
04862         return;
04863     
04864     switch (mode)
04865     {
04866     case 0:
04867         // relay off
04868         tvRelayUpdate(TV_RELAY_USB, false);
04869         break;
04870         
04871     case 1:
04872         // relay on
04873         tvRelayUpdate(TV_RELAY_USB, true);
04874         break;
04875         
04876     case 2:
04877         // Turn the relay on and reset the manual TV pulse timer
04878         tvRelayUpdate(TV_RELAY_USB, true);
04879         tvRelayManualTimer.reset();
04880         tvRelayManualTimer.start();
04881         break;
04882     }
04883 }
04884 
04885 
04886 // ---------------------------------------------------------------------------
04887 //
04888 // In-memory configuration data structure.  This is the live version in RAM
04889 // that we use to determine how things are set up.
04890 //
04891 // When we save the configuration settings, we copy this structure to
04892 // non-volatile flash memory.  At startup, we check the flash location where
04893 // we might have saved settings on a previous run, and it's valid, we copy 
04894 // the flash data to this structure.  Firmware updates wipe the flash
04895 // memory area, so you have to use the PC config tool to send the settings
04896 // again each time the firmware is updated.
04897 //
04898 NVM nvm;
04899 
04900 // Save Config followup time, in seconds.  After a successful save,
04901 // we leave the success flag on in the status for this interval.  At
04902 // the end of the interval, we reboot the device if requested.
04903 uint8_t saveConfigFollowupTime;
04904 
04905 // is a reboot pending at the end of the config save followup interval?
04906 uint8_t saveConfigRebootPending;
04907 
04908 // status flag for successful config save - set to 0x40 on success
04909 uint8_t saveConfigSucceededFlag;
04910 
04911 // Timer for configuration change followup timer
04912 ExtTimer saveConfigFollowupTimer;    
04913 
04914 
04915 // For convenience, a macro for the Config part of the NVM structure
04916 #define cfg (nvm.d.c)
04917 
04918 // flash memory controller interface
04919 FreescaleIAP iap;
04920 
04921 // figure the flash address for the config data
04922 const NVM *configFlashAddr()
04923 {
04924     // figure the number of sectors we need, rounding up
04925     int nSectors = (sizeof(NVM) + SECTOR_SIZE - 1)/SECTOR_SIZE;
04926     
04927     // figure the total size required from the number of sectors
04928     int reservedSize = nSectors * SECTOR_SIZE;
04929     
04930     // locate it at the top of memory
04931     uint32_t addr = iap.flashSize() - reservedSize;
04932     
04933     // return it as a read-only NVM pointer
04934     return (const NVM *)addr;
04935 }
04936 
04937 // Load the config from flash.  Returns true if a valid non-default
04938 // configuration was loaded, false if we not.  If we return false,
04939 // we load the factory defaults, so the configuration object is valid 
04940 // in either case.
04941 bool loadConfigFromFlash()
04942 {
04943     // We want to use the KL25Z's on-board flash to store our configuration
04944     // data persistently, so that we can restore it across power cycles.
04945     // Unfortunatly, the mbed platform doesn't explicitly support this.
04946     // mbed treats the on-board flash as a raw storage device for linker
04947     // output, and assumes that the linker output is the only thing
04948     // stored there.  There's no file system and no allowance for shared
04949     // use for other purposes.  Fortunately, the linker ues the space in
04950     // the obvious way, storing the entire linked program in a contiguous
04951     // block starting at the lowest flash address.  This means that the
04952     // rest of flash - from the end of the linked program to the highest
04953     // flash address - is all unused free space.  Writing our data there
04954     // won't conflict with anything else.  Since the linker doesn't give
04955     // us any programmatic access to the total linker output size, it's
04956     // safest to just store our config data at the very end of the flash
04957     // region (i.e., the highest address).  As long as it's smaller than
04958     // the free space, it won't collide with the linker area.
04959     
04960     // Figure how many sectors we need for our structure
04961     const NVM *flash = configFlashAddr();
04962     
04963     // if the flash is valid, load it; otherwise initialize to defaults
04964     bool nvm_valid = flash->valid();
04965     if (nvm_valid) 
04966     {
04967         // flash is valid - load it into the RAM copy of the structure
04968         memcpy(&nvm, flash, sizeof(NVM));
04969     }
04970     else 
04971     {
04972         // flash is invalid - load factory settings into RAM structure
04973         cfg.setFactoryDefaults();
04974     }
04975     
04976     // tell the caller what happened
04977     return nvm_valid;
04978 }
04979 
04980 // Save the config.  Returns true on success, false on failure.
04981 // 'tFollowup' is the follow-up time in seconds.  If the write is
04982 // successful, we'll turn on the success flag in the status reports
04983 // and leave it on for this interval.  If 'reboot' is true, we'll
04984 // also schedule a reboot at the end of the followup interval.
04985 bool saveConfigToFlash(int tFollowup, bool reboot)
04986 {
04987     // get the config block location in the flash memory
04988     uint32_t addr = uint32_t(configFlashAddr());
04989 
04990     // save the data
04991     bool ok = nvm.save(iap, addr);
04992 
04993     // if the save succeeded, do post-save work
04994     if (ok)
04995     {
04996         // success - report the successful save in the status flags
04997         saveConfigSucceededFlag = 0x40;
04998             
04999         // start the followup timer
05000         saveConfigFollowupTime = tFollowup;
05001         saveConfigFollowupTimer.reset();
05002         saveConfigFollowupTimer.start();
05003         
05004         // if a reboot is pending, flag it
05005         saveConfigRebootPending = reboot;
05006     }
05007     
05008     // return the success indication
05009     return ok;
05010 }
05011 
05012 // ---------------------------------------------------------------------------
05013 //
05014 // Host-loaded configuration.  The Flash NVM block above is designed to be
05015 // stored from within the firmware; in contrast, the host-loaded config is
05016 // stored by the host, by patching the firwmare binary (.bin) file before
05017 // downloading it to the device.
05018 //
05019 // Ideally, we'd use the host-loaded memory for all configuration updates
05020 // from the host - that is, any time the host wants to update config settings,
05021 // such as via user input in the config tool.  In the past, I wanted to do
05022 // it this way because it seemed to be unreliable to write flash memory via
05023 // the device.  But that turned out to be due to a bug in the mbed Ticker 
05024 // code (of all things!), which we've fixed - since then, flash writing on
05025 // the device has been bulletproof.  Even so, doing host-to-device flash
05026 // writing for config updates would be nice just for the sake of speed, as
05027 // the alternative is that we send the variables one at a time by USB, which
05028 // takes noticeable time when reprogramming the whole config set.  But 
05029 // there's no way to accomplish a single-sector flash write via OpenSDA; you 
05030 // can only rewrite the entire flash memory as a unit.
05031 // 
05032 // We can at least use this approach to do a fast configuration restore
05033 // when downloading new firmware.  In that case, we're rewriting all of
05034 // flash memory anyway, so we might as well include the config data.
05035 //
05036 // The memory here is stored using the same format as the USB "Set Config
05037 // Variable" command.  These messages are 8 bytes long and start with a
05038 // byte value 66, followed by the variable ID, followed by the variable
05039 // value data in a format defined separately for each variable.  To load
05040 // the data, we'll start at the first byte after the signature, and 
05041 // interpret each 8-byte block as a type 66 message.  If the first byte
05042 // of a block is not 66, we'll take it as the end of the data.
05043 //
05044 // We provide a block of storage here big enough for 1,024 variables.
05045 // The header consists of a 30-byte signature followed by two bytes giving
05046 // the available space in the area, in this case 8192 == 0x0200.  The
05047 // length is little-endian.  Note that the linker will implicitly zero
05048 // the rest of the block, so if the host doesn't populate it, we'll see
05049 // that it's empty by virtue of not containing the required '66' byte
05050 // prefix for the first 8-byte variable block.
05051 static const uint8_t hostLoadedConfig[8192+32]
05052     __attribute__ ((aligned(SECTOR_SIZE))) =
05053     "///Pinscape.HostLoadedConfig//\0\040";   // 30 byte signature + 2 byte length
05054 
05055 // Get a pointer to the first byte of the configuration data
05056 const uint8_t *getHostLoadedConfigData()
05057 {
05058     // the first configuration variable byte immediately follows the
05059     // 32-byte signature header
05060     return hostLoadedConfig + 32;
05061 };
05062 
05063 // forward reference to config var store function
05064 void configVarSet(const uint8_t *);
05065 
05066 // Load the host-loaded configuration data into the active (RAM)
05067 // configuration object.
05068 void loadHostLoadedConfig()
05069 {
05070     // Start at the first configuration variable.  Each variable
05071     // block is in the format of a Set Config Variable command in
05072     // the USB protocol, so each block starts with a byte value of
05073     // 66 and is 8 bytes long.  Continue as long as we find valid
05074     // variable blocks, or reach end end of the block.
05075     const uint8_t *start = getHostLoadedConfigData();
05076     const uint8_t *end = hostLoadedConfig + sizeof(hostLoadedConfig);
05077     for (const uint8_t *p = getHostLoadedConfigData() ; start < end && *p == 66 ; p += 8)
05078     {
05079         // load this variable
05080         configVarSet(p);
05081     }
05082 }
05083 
05084 // ---------------------------------------------------------------------------
05085 //
05086 // Pixel dump mode - the host requested a dump of image sensor pixels
05087 // (helpful for installing and setting up the sensor and light source)
05088 //
05089 bool reportPlungerStat = false;
05090 uint8_t reportPlungerStatFlags; // plunger pixel report flag bits (see ccdSensor.h)
05091 uint8_t reportPlungerStatTime;  // extra exposure time for plunger pixel report
05092 uint8_t tReportPlungerStat;     // timestamp of most recent plunger status request
05093 
05094 
05095 // ---------------------------------------------------------------------------
05096 //
05097 // Night mode setting updates
05098 //
05099 
05100 // Turn night mode on or off
05101 static void setNightMode(bool on)
05102 {
05103     // Set the new night mode flag in the noisy output class.  Note
05104     // that we use the status report bit flag value 0x02 when on, so
05105     // that we can just '|' this into the overall status bits.
05106     nightMode = on ? 0x02 : 0x00;
05107     
05108     // update the special output pin that shows the night mode state
05109     int port = int(cfg.nightMode.port) - 1;
05110     if (port >= 0 && port < numOutputs)
05111         lwPin[port]->set(nightMode ? 255 : 0);
05112         
05113     // Reset all outputs at their current value, so that the underlying
05114     // physical outputs get turned on or off as appropriate for the night
05115     // mode change.
05116     for (int i = 0 ; i < numOutputs ; ++i)
05117         lwPin[i]->set(outLevel[i]);
05118         
05119     // update 74HC595 outputs
05120     if (hc595 != 0)
05121         hc595->update();
05122 }
05123 
05124 // Toggle night mode
05125 static void toggleNightMode()
05126 {
05127     setNightMode(!nightMode);
05128 }
05129 
05130 
05131 // ---------------------------------------------------------------------------
05132 //
05133 // Plunger Sensor
05134 //
05135 
05136 // the plunger sensor interface object
05137 PlungerSensor *plungerSensor = 0;
05138 
05139 
05140 // Create the plunger sensor based on the current configuration.  If 
05141 // there's already a sensor object, we'll delete it.
05142 void createPlunger()
05143 {
05144     // create the new sensor object according to the type
05145     switch (cfg.plunger.sensorType)
05146     {
05147     case PlungerType_TSL1410R:
05148         // TSL1410R, shadow edge detector
05149         // pins are: SI, CLOCK, AO
05150         plungerSensor = new PlungerSensorTSL1410R(
05151             wirePinName(cfg.plunger.sensorPin[0]), 
05152             wirePinName(cfg.plunger.sensorPin[1]),
05153             wirePinName(cfg.plunger.sensorPin[2]));
05154         break;
05155         
05156     case PlungerType_TSL1412S:
05157         // TSL1412S, shadow edge detector
05158         // pins are: SI, CLOCK, AO
05159         plungerSensor = new PlungerSensorTSL1412R(
05160             wirePinName(cfg.plunger.sensorPin[0]),
05161             wirePinName(cfg.plunger.sensorPin[1]), 
05162             wirePinName(cfg.plunger.sensorPin[2]));
05163         break;
05164     
05165     case PlungerType_Pot:
05166         // Potentiometer (or any other sensor with a linear analog voltage
05167         // reading as the proxy for the position)
05168         // pins are: AO (analog in)
05169         plungerSensor = new PlungerSensorPot(
05170             wirePinName(cfg.plunger.sensorPin[0]));
05171         break;
05172         
05173     case PlungerType_OptQuad:
05174         // Optical quadrature sensor, AEDR8300-K or similar.  The -K is
05175         // designed for a 75 LPI scale, which translates to 300 pulses/inch.
05176         // Pins are: CHA, CHB (quadrature pulse inputs).
05177         plungerSensor = new PlungerSensorQuad(
05178             300,
05179             wirePinName(cfg.plunger.sensorPin[0]),
05180             wirePinName(cfg.plunger.sensorPin[1]));
05181         break;
05182     
05183     case PlungerType_TSL1401CL:
05184         // TSL1401CL, absolute position encoder with bar code scale
05185         // pins are: SI, CLOCK, AO
05186         plungerSensor = new PlungerSensorTSL1401CL(
05187             wirePinName(cfg.plunger.sensorPin[0]), 
05188             wirePinName(cfg.plunger.sensorPin[1]),
05189             wirePinName(cfg.plunger.sensorPin[2]));
05190         break;
05191         
05192     case PlungerType_VL6180X:
05193         // VL6180X time-of-flight IR distance sensor
05194         // pins are: SDL, SCL, GPIO0/CE
05195         plungerSensor = new PlungerSensorVL6180X(
05196             wirePinName(cfg.plunger.sensorPin[0]),
05197             wirePinName(cfg.plunger.sensorPin[1]),
05198             wirePinName(cfg.plunger.sensorPin[2]));
05199         break;
05200         
05201     case PlungerType_AEAT6012:
05202         // Broadcom AEAT-6012-A06 magnetic rotary encoder
05203         // pins are: CS (chip select, dig out), CLK (dig out), DO (data, dig in)
05204         plungerSensor = new PlungerSensorAEAT601X<12>(
05205             wirePinName(cfg.plunger.sensorPin[0]),
05206             wirePinName(cfg.plunger.sensorPin[1]),
05207             wirePinName(cfg.plunger.sensorPin[2]));
05208         break;
05209         
05210     case PlungerType_TCD1103:
05211         // Toshiba TCD1103GFG linear CCD, optical edge detection, with
05212         // inverted logic gates.
05213         //
05214         // Pins are: fM (master clock, PWM), OS (sample data, analog in), 
05215         // ICG (integration clear gate, dig out), SH (shift gate, dig out)
05216         plungerSensor = new PlungerSensorTCD1103<true>(
05217             wirePinName(cfg.plunger.sensorPin[0]),
05218             wirePinName(cfg.plunger.sensorPin[1]),
05219             wirePinName(cfg.plunger.sensorPin[2]),
05220             wirePinName(cfg.plunger.sensorPin[3]));
05221         break;
05222         
05223     case PlungerType_None:
05224     default:
05225         plungerSensor = new PlungerSensorNull();
05226         break;
05227     }
05228 
05229     // initialize the plunger from the saved configuration
05230     plungerSensor->restoreCalibration(cfg);
05231     
05232     // initialize the config variables affecting the plunger
05233     plungerSensor->onConfigChange(19, cfg);
05234     plungerSensor->onConfigChange(20, cfg);
05235 }
05236 
05237 // Global plunger calibration mode flag
05238 bool plungerCalMode;
05239 
05240 // Plunger reader
05241 //
05242 // This class encapsulates our plunger data processing.  At the simplest
05243 // level, we read the position from the sensor, adjust it for the
05244 // calibration settings, and report the calibrated position to the host.
05245 //
05246 // In addition, we constantly monitor the data for "firing" motions.
05247 // A firing motion is when the user pulls back the plunger and releases
05248 // it, allowing it to shoot forward under the force of the main spring.
05249 // When we detect that this is happening, we briefly stop reporting the
05250 // real physical position that we're reading from the sensor, and instead
05251 // report a synthetic series of positions that depicts an idealized 
05252 // firing motion.
05253 //
05254 // The point of the synthetic reports is to correct for distortions
05255 // created by the joystick interface conventions used by VP and other
05256 // PC pinball emulators.  The convention they use is simply to have the
05257 // plunger device report the instantaneous position of the real plunger.
05258 // The PC software polls this reported position periodically, and moves 
05259 // the on-screen virtual plunger in sync with the real plunger.  This
05260 // works fine for human-scale motion when the user is manually moving
05261 // the plunger.  But it doesn't work for the high speed motion of a 
05262 // release.  The plunger simply moves too fast.  VP polls in about 10ms
05263 // intervals; the plunger takes about 50ms to travel from fully
05264 // retracted to the park position when released.  The low sampling
05265 // rate relative to the rate of change of the sampled data creates
05266 // a classic digital aliasing effect.  
05267 //
05268 // The synthetic reporting scheme compensates for the interface
05269 // distortions by essentially changing to a coarse enough timescale
05270 // that VP can reliably interpret the readings.  Conceptually, there
05271 // are three steps involved in doing this.  First, we analyze the
05272 // actual sensor data to detect and characterize the release motion.
05273 // Second, once we think we have a release in progress, we fit the 
05274 // data to a mathematical model of the release.  The model we use is 
05275 // dead simple: we consider the release to have one parameter, namely
05276 // the retraction distance at the moment the user lets go.  This is an 
05277 // excellent proxy in the real physical system for the final speed 
05278 // when the plunger hits the ball, and it also happens to match how 
05279 // VP models it internally.  Third, we construct synthetic reports
05280 // that will make VP's internal state match our model.  This is also
05281 // pretty simple: we just need to send VP the maximum retraction
05282 // distance for long enough to be sure that it polls it at least
05283 // once, and then send it the park position for long enough to 
05284 // ensure that VP will complete the same firing motion.  The 
05285 // immediate jump from the maximum point to the zero point will
05286 // cause VP to move its simulation model plunger forward from the
05287 // starting point at its natural spring acceleration rate, which 
05288 // is exactly what the real plunger just did.
05289 //
05290 class PlungerReader
05291 {
05292 public:
05293     PlungerReader()
05294     {
05295         // not in a firing event yet
05296         firing = 0;
05297     }
05298     
05299     // Collect a reading from the plunger sensor.  The main loop calls
05300     // this frequently to read the current raw position data from the
05301     // sensor.  We analyze the raw data to produce the calibrated
05302     // position that we report to the PC via the joystick interface.
05303     void read()
05304     {
05305         // if the sensor is busy, skip the reading on this round
05306         if (!plungerSensor->ready())
05307             return;
05308         
05309         // Read a sample from the sensor
05310         PlungerReading r;
05311         if (plungerSensor->read(r))
05312         {
05313             // check for calibration mode
05314             if (plungerCalMode)
05315             {
05316                 // Calibration mode.  Adjust the calibration bounds to fit
05317                 // the value.  If this value is beyond the current min or max,
05318                 // expand the envelope to include this new value.
05319                 if (r.pos > cfg.plunger.cal.max)
05320                     cfg.plunger.cal.max = r.pos;
05321                 if (r.pos < cfg.plunger.cal.min)
05322                     cfg.plunger.cal.min = r.pos;
05323                     
05324                 // update our cached calibration data
05325                 onUpdateCal();
05326 
05327                 // If we're in calibration state 0, we're waiting for the
05328                 // plunger to come to rest at the park position so that we
05329                 // can take a sample of the park position.  Check to see if
05330                 // we've been at rest for a minimum interval.
05331                 if (calState == 0)
05332                 {
05333                     if (abs(r.pos - calZeroStart.pos) < 65535/3/50)
05334                     {
05335                         // we're close enough - make sure we've been here long enough
05336                         if (uint32_t(r.t - calZeroStart.t) > 100000UL)
05337                         {
05338                             // we've been at rest long enough - count it
05339                             calZeroPosSum += r.pos;
05340                             calZeroPosN += 1;
05341                             
05342                             // update the zero position from the new average
05343                             cfg.plunger.cal.zero = uint16_t(calZeroPosSum / calZeroPosN);
05344                             onUpdateCal();
05345                             
05346                             // switch to calibration state 1 - at rest
05347                             calState = 1;
05348                         }
05349                     }
05350                     else
05351                     {
05352                         // we're not close to the last position - start again here
05353                         calZeroStart = r;
05354                     }
05355                 }
05356                 
05357                 // Rescale to the joystick range, and adjust for the current
05358                 // park position, but don't calibrate.  We don't know the maximum
05359                 // point yet, so we can't calibrate the range.
05360                 r.pos = int(
05361                     (long(r.pos - cfg.plunger.cal.zero) * JOYMAX)
05362                     / (65535 - cfg.plunger.cal.zero));
05363             }
05364             else
05365             {
05366                 // Not in calibration mode.  Apply the existing calibration and 
05367                 // rescale to the joystick range.
05368                 r.pos = applyCal(r.pos);
05369                     
05370                 // limit the result to the valid joystick range
05371                 if (r.pos > JOYMAX)
05372                     r.pos = JOYMAX;
05373                 else if (r.pos < -JOYMAX)
05374                     r.pos = -JOYMAX;
05375             }
05376 
05377             // Look for a firing event - the user releasing the plunger and
05378             // allowing it to shoot forward at full speed.  Wait at least 5ms
05379             // between samples for this, to help distinguish random motion 
05380             // from the rapid motion of a firing event.  
05381             //
05382             // There's a trade-off in the choice of minimum sampling interval.
05383             // The longer we wait, the more certain we can be of the trend.
05384             // But if we wait too long, the user will perceive a delay.  We
05385             // also want to sample frequently enough to see the release motion
05386             // at intermediate steps along the way, so the sampling has to be
05387             // considerably faster than the whole travel time, which is about
05388             // 25-50ms.
05389             if (uint32_t(r.t - prv.t) < 5000UL)
05390                 return;
05391                 
05392             // assume that we'll report this reading as-is
05393             z = r.pos;
05394                 
05395             // Firing event detection.
05396             //
05397             // A "firing event" is when the player releases the plunger from
05398             // a retracted position, allowing it to shoot forward under the
05399             // spring tension.
05400             //
05401             // We monitor the plunger motion for these events, and when they
05402             // occur, we report an "idealized" version of the motion to the
05403             // PC.  The idealized version consists of a series of readings
05404             // frozen at the fully retracted position for the whole duration 
05405             // of the forward travel, followed by a series of readings at the
05406             // fully forward position for long enough for the plunger to come
05407             // mostly to rest.  The series of frozen readings aren't meant to
05408             // be perceptible to the player - we try to keep them short enough
05409             // that they're not apparent as delay.  Instead, they're for the
05410             // PC client software's benefit.  PC joystick clients use polling,
05411             // so they only see an unpredictable subset of the readings we
05412             // send.  The only way to be sure that the client sees a particular 
05413             // reading is to hold it for long enough that the client is sure to
05414             // poll within the hold interval.  In the case of the plunger 
05415             // firing motion, it's important that the client sees the *ends*
05416             // of the travel - the fully retracted starting position in
05417             // particular.  If the PC client only polls for a sample while the
05418             // plunger is somewhere in the middle of the travel, the PC will
05419             // think that the firing motion *started* in that middle position,
05420             // so it won't be able to model the right amount of momentum when
05421             // the plunger hits the ball.  We try to ensure that the PC sees
05422             // the right starting point by reporting the starting point for 
05423             // extra time during the forward motion.  By the same token, we
05424             // want the PC to know that the plunger has moved all the way
05425             // forward, rather than mistakenly thinking that it stopped
05426             // somewhere in the middle of the travel, so we freeze at the
05427             // forward position for a short time.
05428             //
05429             // To detect a firing event, we look for forward motion that's
05430             // fast enough to be a firing event.  To determine how fast is
05431             // fast enough, we use a simple model of the plunger motion where 
05432             // the acceleration is constant.  This is only an approximation, 
05433             // as the spring force actually varies with spring's compression, 
05434             // but it's close enough for our purposes here.
05435             //
05436             // Do calculations in fixed-point 2^48 scale with 64-bit ints.
05437             // acc2 = acceleration/2 for 50ms release time, units of unit
05438             // distances per microsecond squared, where the unit distance
05439             // is the overall travel from the starting retracted position
05440             // to the park position.
05441             const int32_t acc2 = 112590;  // 2^48 scale
05442             switch (firing)
05443             {
05444             case 0:
05445                 // Not in firing mode.  If we're retracted a bit, and the
05446                 // motion is forward at a fast enough rate to look like a
05447                 // release, enter firing mode.
05448                 if (r.pos > JOYMAX/6)
05449                 {
05450                     const uint32_t dt = uint32_t(r.t - prv.t);
05451                     const uint32_t dt2 = dt*dt;  // dt^2
05452                     if (r.pos < prv.pos - int((prv.pos*acc2*uint64_t(dt2)) >> 48))
05453                     {
05454                         // Tentatively enter firing mode.  Use the prior reading
05455                         // as the starting point, and freeze reports for now.
05456                         firingMode(1);
05457                         f0 = prv;
05458                         z = f0.pos;
05459 
05460                         // if in calibration state 1 (at rest), switch to 
05461                         // state 2 (not at rest)
05462                         if (calState == 1)
05463                             calState = 2;
05464                     }
05465                 }
05466                 break;
05467                 
05468             case 1:
05469                 // Tentative firing mode: the plunger was moving forward
05470                 // at last check.  To stay in firing mode, the plunger has
05471                 // to keep moving forward fast enough to look like it's 
05472                 // moving under spring force.  To figure out how fast is
05473                 // fast enough, we use a simple model where the acceleration
05474                 // is constant over the whole travel distance and the total
05475                 // travel time is 50ms.  The acceleration actually varies
05476                 // slightly since it comes from the spring force, which
05477                 // is linear in the displacement; but the plunger spring is
05478                 // fairly compressed even when the plunger is all the way
05479                 // forward, so the difference in tension from one end of
05480                 // the travel to the other is fairly small, so it's not too
05481                 // far off to model it as constant.  And the real travel
05482                 // time obviously isn't a constant, but all we need for 
05483                 // that is an upper bound.  So: we'll figure the time since 
05484                 // we entered firing mode, and figure the distance we should 
05485                 // have traveled to complete the trip within the maximum
05486                 // time allowed.  If we've moved far enough, we'll stay
05487                 // in firing mode; if not, we'll exit firing mode.  And if
05488                 // we cross the finish line while still in firing mode,
05489                 // we'll switch to the next phase of the firing event.
05490                 if (r.pos <= 0)
05491                 {
05492                     // We crossed the park position.  Switch to the second
05493                     // phase of the firing event, where we hold the reported
05494                     // position at the "bounce" position (where the plunger
05495                     // is all the way forward, compressing the barrel spring).
05496                     // We'll stick here long enough to ensure that the PC
05497                     // client (Visual Pinball or whatever) sees the reading
05498                     // and processes the release motion via the simulated
05499                     // physics.
05500                     firingMode(2);
05501                     
05502                     // if in calibration mode, and we're in state 2 (moving), 
05503                     // collect firing statistics for calibration purposes
05504                     if (plungerCalMode && calState == 2)
05505                     {
05506                         // collect a new zero point for the average when we 
05507                         // come to rest
05508                         calState = 0;
05509                         
05510                         // collect average firing time statistics in millseconds,
05511                         // if it's in range (20 to 255 ms)
05512                         const int dt = uint32_t(r.t - f0.t)/1000UL;
05513                         if (dt >= 15 && dt <= 255)
05514                         {
05515                             calRlsTimeSum += dt;
05516                             calRlsTimeN += 1;
05517                             cfg.plunger.cal.tRelease = uint8_t(calRlsTimeSum / calRlsTimeN);
05518                         }
05519                     }
05520 
05521                     // Figure the "bounce" position as forward of the park
05522                     // position by 1/6 of the starting retraction distance.
05523                     // This simulates the momentum of the plunger compressing
05524                     // the barrel spring on the rebound.  The barrel spring
05525                     // can compress by about 1/6 of the maximum retraction 
05526                     // distance, so we'll simply treat its compression as
05527                     // proportional to the retraction.  (It might be more
05528                     // realistic to use a slightly higher value here, maybe
05529                     // 1/4 or 1/3 or the retraction distance, capping it at
05530                     // a maximum of 1/6, because the real plunger probably 
05531                     // compresses the barrel spring by 100% with less than 
05532                     // 100% retraction.  But that won't affect the physics
05533                     // meaningfully, just the animation, and the effect is
05534                     // small in any case.)
05535                     z = f0.pos = -f0.pos / 6;
05536                     
05537                     // reset the starting time for this phase
05538                     f0.t = r.t;
05539                 }
05540                 else
05541                 {
05542                     // check for motion since the start of the firing event
05543                     const uint32_t dt = uint32_t(r.t - f0.t);
05544                     const uint32_t dt2 = dt*dt;  // dt^2
05545                     if (dt < 50000 
05546                         && r.pos < f0.pos - int((f0.pos*acc2*uint64_t(dt2)) >> 48))
05547                     {
05548                         // It's moving fast enough to still be in a release
05549                         // motion.  Continue reporting the start position, and
05550                         // stay in the first release phase.
05551                         z = f0.pos;
05552                     }
05553                     else
05554                     {
05555                         // It's not moving fast enough to be a release
05556                         // motion.  Return to the default state.
05557                         firingMode(0);
05558                         calState = 1;
05559                     }
05560                 }
05561                 break;
05562                 
05563             case 2:
05564                 // Firing mode, holding at forward compression position.
05565                 // Hold here for 25ms.
05566                 if (uint32_t(r.t - f0.t) < 25000)
05567                 {
05568                     // stay here for now
05569                     z = f0.pos;
05570                 }
05571                 else
05572                 {
05573                     // advance to the next phase, where we report the park
05574                     // position until the plunger comes to rest
05575                     firingMode(3);
05576                     z = 0;
05577 
05578                     // remember when we started
05579                     f0.t = r.t;
05580                 }
05581                 break;
05582                 
05583             case 3:
05584                 // Firing event, holding at park position.  Stay here for
05585                 // a few moments so that the PC client can simulate the
05586                 // full release motion, then return to real readings.
05587                 if (uint32_t(r.t - f0.t) < 250000)
05588                 {
05589                     // stay here a while longer
05590                     z = 0;
05591                 }
05592                 else
05593                 {
05594                     // it's been long enough - return to normal mode
05595                     firingMode(0);
05596                 }
05597                 break;
05598             }
05599             
05600             // Check for auto-zeroing, if enabled
05601             if ((cfg.plunger.autoZero.flags & PlungerAutoZeroEnabled) != 0)
05602             {
05603                 // If we moved since the last reading, reset and restart the 
05604                 // auto-zero timer.  Otherwise, if the timer has reached the 
05605                 // auto-zero timeout, it means we've been motionless for that 
05606                 // long, so auto-zero now.                
05607                 if (r.pos != prv.pos)
05608                 {
05609                     // movement detected - reset the timer
05610                     autoZeroTimer.reset();
05611                     autoZeroTimer.start();
05612                 }
05613                 else if (autoZeroTimer.read_us() > cfg.plunger.autoZero.t * 1000000UL)
05614                 {
05615                     // auto-zero now
05616                     plungerSensor->autoZero();
05617                     
05618                     // stop the timer so that we don't keep repeating this
05619                     // if the plunger stays still for a long time
05620                     autoZeroTimer.stop();
05621                     autoZeroTimer.reset();
05622                 }
05623             }
05624             
05625             // this new reading becomes the previous reading for next time
05626             prv = r;
05627         }
05628     }
05629     
05630     // Get the current value to report through the joystick interface
05631     int16_t getPosition()
05632     {
05633         // return the last reading
05634         return z;
05635     }
05636         
05637     // Set calibration mode on or off
05638     void setCalMode(bool f) 
05639     {
05640         // check to see if we're entering calibration mode
05641         if (f && !plungerCalMode)
05642         {
05643             // reset the calibration in the configuration
05644             cfg.plunger.cal.begin();
05645             
05646             // start in state 0 (waiting to settle)
05647             calState = 0;
05648             calZeroPosSum = 0;
05649             calZeroPosN = 0;
05650             calRlsTimeSum = 0;
05651             calRlsTimeN = 0;
05652             
05653             // tell the plunger we're starting calibration
05654             plungerSensor->beginCalibration(cfg);
05655             
05656             // set the initial zero point to the current position
05657             PlungerReading r;
05658             if (plungerSensor->read(r))
05659             {
05660                 // got a reading - use it as the initial zero point
05661                 cfg.plunger.cal.zero = r.pos;
05662                 onUpdateCal();
05663                 
05664                 // use it as the starting point for the settling watch
05665                 calZeroStart = r;
05666             }
05667             else
05668             {
05669                 // no reading available - use the default 1/6 position
05670                 cfg.plunger.cal.zero = 0xffff/6;
05671                 onUpdateCal();
05672                 
05673                 // we don't have a starting point for the setting watch
05674                 calZeroStart.pos = -65535;
05675                 calZeroStart.t = 0;
05676             }
05677         }
05678         else if (!f && plungerCalMode)
05679         {
05680             // Leaving calibration mode.  Make sure the max is past the
05681             // zero point - if it's not, we'd have a zero or negative
05682             // denominator for the scaling calculation, which would be
05683             // physically meaningless.
05684             if (cfg.plunger.cal.max <= cfg.plunger.cal.zero)
05685             {
05686                 // bad settings - reset to defaults
05687                 cfg.plunger.cal.max = 0xffff;
05688                 cfg.plunger.cal.zero = 0xffff/6;
05689             }
05690             
05691             // finalize the configuration in the plunger object
05692             plungerSensor->endCalibration(cfg);
05693 
05694             // update our internal cached information for the new calibration
05695             onUpdateCal();
05696         }
05697             
05698         // remember the new mode
05699         plungerCalMode = f; 
05700     }
05701     
05702     // Cached inverse of the calibration range.  This is for calculating
05703     // the calibrated plunger position given a raw sensor reading.  The
05704     // cached inverse is calculated as
05705     //
05706     //    64K * JOYMAX / (cfg.plunger.cal.max - cfg.plunger.cal.zero)
05707     //
05708     // To convert a raw sensor reading to a calibrated position, calculate
05709     //
05710     //    ((reading - cfg.plunger.cal.zero)*invCalRange) >> 16
05711     //
05712     // That yields the calibration result without performing a division.
05713     int invCalRange;
05714     
05715     // apply the calibration range to a reading
05716     inline int applyCal(int reading)
05717     {
05718         return ((reading - cfg.plunger.cal.zero)*invCalRange) >> 16;
05719     }
05720     
05721     void onUpdateCal()
05722     {
05723         invCalRange = (JOYMAX << 16)/(cfg.plunger.cal.max - cfg.plunger.cal.zero);
05724     }
05725 
05726     // is a firing event in progress?
05727     bool isFiring() { return firing == 3; }
05728     
05729 private:
05730     // current reported joystick reading
05731     int z;
05732     
05733     // previous reading
05734     PlungerReading prv;
05735 
05736     // Calibration state.  During calibration mode, we watch for release
05737     // events, to measure the time it takes to complete the release
05738     // motion; and we watch for the plunger to come to reset after a
05739     // release, to gather statistics on the rest position.
05740     //   0 = waiting to settle
05741     //   1 = at rest
05742     //   2 = retracting
05743     //   3 = possibly releasing
05744     uint8_t calState;
05745     
05746     // Calibration zero point statistics.
05747     // During calibration mode, we collect data on the rest position (the 
05748     // zero point) by watching for the plunger to come to rest after each 
05749     // release.  We average these rest positions to get the calibrated 
05750     // zero point.  We use the average because the real physical plunger 
05751     // itself doesn't come to rest at exactly the same spot every time, 
05752     // largely due to friction in the mechanism.  To calculate the average,
05753     // we keep a sum of the readings and a count of samples.
05754     PlungerReading calZeroStart;
05755     long calZeroPosSum;
05756     int calZeroPosN;
05757     
05758     // Calibration release time statistics.
05759     // During calibration, we collect an average for the release time.
05760     long calRlsTimeSum;
05761     int calRlsTimeN;
05762 
05763     // Auto-zeroing timer
05764     Timer autoZeroTimer;
05765     
05766     // set a firing mode
05767     inline void firingMode(int m) 
05768     {
05769         firing = m;
05770     }
05771     
05772     // Firing event state.
05773     //
05774     //   0 - Default state: not in firing event.  We report the true
05775     //       instantaneous plunger position to the joystick interface.
05776     //
05777     //   1 - Moving forward at release speed
05778     //
05779     //   2 - Firing - reporting the bounce position
05780     //
05781     //   3 - Firing - reporting the park position
05782     //
05783     int firing;
05784     
05785     // Starting position for current firing mode phase
05786     PlungerReading f0;
05787 };
05788 
05789 // plunger reader singleton
05790 PlungerReader plungerReader;
05791 
05792 // ---------------------------------------------------------------------------
05793 //
05794 // Handle the ZB Launch Ball feature.
05795 //
05796 // The ZB Launch Ball feature, if enabled, lets the mechanical plunger
05797 // serve as a substitute for a physical Launch Ball button.  When a table
05798 // is loaded in VP, and the table has the ZB Launch Ball LedWiz port
05799 // turned on, we'll disable mechanical plunger reports through the
05800 // joystick interface and instead use the plunger only to simulate the
05801 // Launch Ball button.  When the mode is active, pulling back and 
05802 // releasing the plunger causes a brief simulated press of the Launch
05803 // button, and pushing the plunger forward of the rest position presses
05804 // the Launch button as long as the plunger is pressed forward.
05805 //
05806 // This feature has two configuration components:
05807 //
05808 //   - An LedWiz port number.  This port is a "virtual" port that doesn't
05809 //     have to be attached to any actual output.  DOF uses it to signal 
05810 //     that the current table uses a Launch button instead of a plunger.
05811 //     DOF simply turns the port on when such a table is loaded and turns
05812 //     it off at all other times.  We use it to enable and disable the
05813 //     plunger/launch button connection.
05814 //
05815 //   - A joystick button ID.  We simulate pressing this button when the
05816 //     launch feature is activated via the LedWiz port and the plunger is
05817 //     either pulled back and releasd, or pushed forward past the rest
05818 //     position.
05819 //
05820 class ZBLaunchBall
05821 {
05822 public:
05823     ZBLaunchBall()
05824     {
05825         // start in the default state
05826         lbState = 0;
05827         btnState = false;
05828     }
05829 
05830     // Update state.  This checks the current plunger position and
05831     // the timers to see if the plunger is in a position that simulates
05832     // a Launch Ball button press via the ZB Launch Ball feature.
05833     // Updates the simulated button vector according to the current
05834     // launch ball state.  The main loop calls this before each 
05835     // joystick update to figure the new simulated button state.
05836     void update()
05837     {
05838         // If the ZB Launch Ball led wiz output is ON, check for a 
05839         // plunger firing event
05840         if (zbLaunchOn)
05841         {                
05842             // note the new position
05843             int znew = plungerReader.getPosition();
05844             
05845             // figure the push threshold from the configuration data
05846             const int pushThreshold = int(-JOYMAX/3.0 * cfg.plunger.zbLaunchBall.pushDistance/1000.0);
05847 
05848             // check the state
05849             switch (lbState)
05850             {
05851             case 0:
05852                 // Default state.  If a launch event has been detected on
05853                 // the plunger, activate a timed pulse and switch to state 1.
05854                 // If the plunger is pushed forward of the threshold, push
05855                 // the button.
05856                 if (plungerReader.isFiring())
05857                 {
05858                     // firing event - start a timed Launch button pulse
05859                     lbTimer.reset();
05860                     lbTimer.start();
05861                     setButton(true);
05862                     
05863                     // switch to state 1
05864                     lbState = 1;
05865                 }
05866                 else if (znew <= pushThreshold)
05867                 {
05868                     // pushed forward without a firing event - hold the
05869                     // button as long as we're pushed forward
05870                     setButton(true);
05871                 }
05872                 else
05873                 {
05874                     // not pushed forward - turn off the Launch button
05875                     setButton(false);
05876                 }
05877                 break;
05878                 
05879             case 1:
05880                 // State 1: Timed Launch button pulse in progress after a
05881                 // firing event.  Wait for the timer to expire.
05882                 if (lbTimer.read_us() > 200000UL)
05883                 {
05884                     // timer expired - turn off the button
05885                     setButton(false);
05886                     
05887                     // switch to state 2
05888                     lbState = 2;
05889                 }
05890                 break;
05891                 
05892             case 2:
05893                 // State 2: Timed Launch button pulse done.  Wait for the
05894                 // plunger launch event to end.
05895                 if (!plungerReader.isFiring())
05896                 {
05897                     // firing event done - return to default state
05898                     lbState = 0;
05899                 }
05900                 break;
05901             }
05902         }
05903         else
05904         {
05905             // ZB Launch Ball disabled - turn off the button if it was on
05906             setButton(false);
05907                 
05908             // return to the default state
05909             lbState = 0;
05910         }
05911     }
05912     
05913     // Set the button state
05914     void setButton(bool on)
05915     {
05916         if (btnState != on)
05917         {
05918             // remember the new state
05919             btnState = on;
05920             
05921             // update the virtual button state
05922             buttonState[zblButtonIndex].virtPress(on);
05923         }
05924     }
05925     
05926 private:
05927     // Simulated Launch Ball button state.  If a "ZB Launch Ball" port is
05928     // defined for our LedWiz port mapping, any time that port is turned ON,
05929     // we'll simulate pushing the Launch Ball button if the player pulls 
05930     // back and releases the plunger, or simply pushes on the plunger from
05931     // the rest position.  This allows the plunger to be used in lieu of a
05932     // physical Launch Ball button for tables that don't have plungers.
05933     //
05934     // States:
05935     //   0 = default
05936     //   1 = firing (firing event has activated a Launch button pulse)
05937     //   2 = firing done (Launch button pulse ended, waiting for plunger
05938     //       firing event to end)
05939     uint8_t lbState;
05940     
05941     // button state
05942     bool btnState;
05943     
05944     // Time since last lbState transition.  Some of the states are time-
05945     // sensitive.  In the "uncocked" state, we'll return to state 0 if
05946     // we remain in this state for more than a few milliseconds, since
05947     // it indicates that the plunger is being slowly returned to rest
05948     // rather than released.  In the "launching" state, we need to release 
05949     // the Launch Ball button after a moment, and we need to wait for 
05950     // the plunger to come to rest before returning to state 0.
05951     Timer lbTimer;
05952 };
05953 
05954 // ---------------------------------------------------------------------------
05955 //
05956 // Reboot - resets the microcontroller
05957 //
05958 void reboot(USBJoystick &js, bool disconnect = true, long pause_us = 2000000L)
05959 {
05960     // disconnect from USB
05961     if (disconnect)
05962         js.disconnect();
05963     
05964     // wait a few seconds to make sure the host notices the disconnect
05965     wait_us(pause_us);
05966     
05967     // reset the device
05968     NVIC_SystemReset();
05969     while (true) { }
05970 }
05971 
05972 // ---------------------------------------------------------------------------
05973 //
05974 // Translate joystick readings from raw values to reported values, based
05975 // on the orientation of the controller card in the cabinet.
05976 //
05977 void accelRotate(int &x, int &y)
05978 {
05979     int tmp;
05980     switch (cfg.accel.orientation)
05981     {
05982     case OrientationFront:
05983         tmp = x;
05984         x = y;
05985         y = tmp;
05986         break;
05987     
05988     case OrientationLeft:
05989         x = -x;
05990         break;
05991     
05992     case OrientationRight:
05993         y = -y;
05994         break;
05995     
05996     case OrientationRear:
05997         tmp = -x;
05998         x = -y;
05999         y = tmp;
06000         break;
06001     }
06002 }
06003 
06004 // ---------------------------------------------------------------------------
06005 //
06006 // Calibration button state:
06007 //  0 = not pushed
06008 //  1 = pushed, not yet debounced
06009 //  2 = pushed, debounced, waiting for hold time
06010 //  3 = pushed, hold time completed - in calibration mode
06011 int calBtnState = 0;
06012 
06013 // calibration button debounce timer
06014 Timer calBtnTimer;
06015 
06016 // calibration button light state
06017 int calBtnLit = false;
06018     
06019 
06020 // ---------------------------------------------------------------------------
06021 //
06022 // Configuration variable get/set message handling
06023 //
06024 
06025 // Handle SET messages - write configuration variables from USB message data
06026 #define if_msg_valid(test)  if (test)
06027 #define v_byte(var, ofs)    cfg.var = data[ofs]
06028 #define v_byte_wo(var, ofs) cfg.var = data[ofs]
06029 #define v_ui16(var, ofs)    cfg.var = wireUI16(data+(ofs))
06030 #define v_ui32(var, ofs)    cfg.var = wireUI32(data+(ofs))
06031 #define v_pin(var, ofs)     cfg.var = wirePinName(data[ofs])
06032 #define v_byte_ro(val, ofs) // ignore read-only variables on SET
06033 #define v_ui32_ro(val, ofs) // ignore read-only variables on SET
06034 #define VAR_MODE_SET 1      // we're in SET mode
06035 #define v_func configVarSet(const uint8_t *data)
06036 #include "cfgVarMsgMap.h"
06037 
06038 // redefine everything for the SET messages
06039 #undef if_msg_valid
06040 #undef v_byte
06041 #undef v_ui16
06042 #undef v_ui32
06043 #undef v_pin
06044 #undef v_byte_ro
06045 #undef v_byte_wo
06046 #undef v_ui32_ro
06047 #undef VAR_MODE_SET
06048 #undef v_func
06049 
06050 // Handle GET messages - read variable values and return in USB message data
06051 #define if_msg_valid(test)
06052 #define v_byte(var, ofs)    data[ofs] = cfg.var
06053 #define v_ui16(var, ofs)    ui16Wire(data+(ofs), cfg.var)
06054 #define v_ui32(var, ofs)    ui32Wire(data+(ofs), cfg.var)
06055 #define v_pin(var, ofs)     pinNameWire(data+(ofs), cfg.var)
06056 #define v_byte_ro(val, ofs) data[ofs] = (val)
06057 #define v_ui32_ro(val, ofs) ui32Wire(data+(ofs), val);
06058 #define VAR_MODE_SET 0      // we're in GET mode
06059 #define v_byte_wo(var, ofs) // ignore write-only variables in GET mode
06060 #define v_func  configVarGet(uint8_t *data)
06061 #include "cfgVarMsgMap.h"
06062 
06063 
06064 // ---------------------------------------------------------------------------
06065 //
06066 // Timer for timestamping input requests
06067 //
06068 Timer requestTimestamper;
06069 
06070 // ---------------------------------------------------------------------------
06071 //
06072 // Handle an input report from the USB host.  Input reports use our extended
06073 // LedWiz protocol.
06074 //
06075 void handleInputMsg(LedWizMsg &lwm, USBJoystick &js, Accel &accel)
06076 {
06077     // LedWiz commands come in two varieties:  SBA and PBA.  An
06078     // SBA is marked by the first byte having value 64 (0x40).  In
06079     // the real LedWiz protocol, any other value in the first byte
06080     // means it's a PBA message.  However, *valid* PBA messages
06081     // always have a first byte (and in fact all 8 bytes) in the
06082     // range 0-49 or 129-132.  Anything else is invalid.  We take
06083     // advantage of this to implement private protocol extensions.
06084     // So our full protocol is as follows:
06085     //
06086     // first byte =
06087     //   0-48     -> PBA
06088     //   64       -> SBA 
06089     //   65       -> private control message; second byte specifies subtype
06090     //   129-132  -> PBA
06091     //   200-228  -> extended bank brightness set for outputs N to N+6, where
06092     //               N is (first byte - 200)*7
06093     //   other    -> reserved for future use
06094     //
06095     uint8_t *data = lwm.data;
06096     if (data[0] == 64)
06097     {
06098         // 64 = SBA (original LedWiz command to set on/off switches for ports 1-32)
06099         //printf("SBA %02x %02x %02x %02x, speed %02x\r\n",
06100         //       data[1], data[2], data[3], data[4], data[5]);
06101         sba_sbx(0, data);
06102 
06103         // SBA resets the PBA port group counter
06104         pbaIdx = 0;
06105     }
06106     else if (data[0] == 65)
06107     {
06108         // Private control message.  This isn't an LedWiz message - it's
06109         // an extension for this device.  65 is an invalid PBA setting,
06110         // and isn't used for any other LedWiz message, so we appropriate
06111         // it for our own private use.  The first byte specifies the 
06112         // message type.
06113         switch (data[1])
06114         {
06115         case 0:
06116             // No Op
06117             break;
06118             
06119         case 1:
06120             // 1 = Old Set Configuration:
06121             //     data[2] = LedWiz unit number (0x00 to 0x0f)
06122             //     data[3] = feature enable bit mask:
06123             //               0x01 = enable plunger sensor
06124             {
06125     
06126                 // get the new LedWiz unit number - this is 0-15, whereas we
06127                 // we save the *nominal* unit number 1-16 in the config                
06128                 uint8_t newUnitNo = (data[2] & 0x0f) + 1;
06129     
06130                 // we'll need a reboot if the LedWiz unit number is changing
06131                 bool reboot = (newUnitNo != cfg.psUnitNo);
06132                 
06133                 // set the configuration parameters from the message
06134                 cfg.psUnitNo = newUnitNo;
06135                 cfg.plunger.enabled = data[3] & 0x01;
06136                 
06137                 // set the flag to do the save
06138                 saveConfigToFlash(0, reboot);
06139             }
06140             break;
06141             
06142         case 2:
06143             // 2 = Calibrate plunger
06144             // (No parameters)
06145             
06146             // enter calibration mode
06147             calBtnState = 3;
06148             plungerReader.setCalMode(true);
06149             calBtnTimer.reset();
06150             break;
06151             
06152         case 3:
06153             // 3 = plunger sensor status report
06154             //     data[2] = flag bits
06155             //     data[3] = extra exposure time, 100us (.1ms) increments
06156             reportPlungerStat = true;
06157             reportPlungerStatFlags = data[2];
06158             reportPlungerStatTime = data[3];
06159             
06160             // set the extra integration time in the sensor
06161             plungerSensor->setExtraIntegrationTime(reportPlungerStatTime * 100);
06162             
06163             // make a note of the request timestamp
06164             tReportPlungerStat = requestTimestamper.read_us();
06165             
06166             // show purple until we finish sending the report
06167             diagLED(1, 0, 1);
06168             break;
06169             
06170         case 4:
06171             // 4 = hardware configuration query
06172             // (No parameters)
06173             js.reportConfig(
06174                 numOutputs, 
06175                 cfg.psUnitNo - 1,   // report 0-15 range for unit number (we store 1-16 internally)
06176                 cfg.plunger.cal.zero, cfg.plunger.cal.max, cfg.plunger.cal.tRelease,
06177                 nvm.valid(),        // a config is loaded if the config memory block is valid
06178                 true,               // we support sbx/pbx extensions
06179                 true,               // we support the new accelerometer settings
06180                 true,               // we support the "flash write ok" status bit in joystick reports
06181                 true,               // we support the configurable joystick report timing features
06182                 true,               // chime logic is supported
06183                 mallocBytesFree()); // remaining memory size
06184             break;
06185             
06186         case 5:
06187             // 5 = all outputs off, reset to LedWiz defaults
06188             allOutputsOff();
06189             break;
06190             
06191         case 6:
06192             // 6 = Save configuration to flash.  Optionally reboot after the 
06193             // delay time in seconds given in data[2].
06194             //
06195             // data[2] = delay time in seconds
06196             // data[3] = flags:
06197             //           0x01 -> do not reboot
06198             saveConfigToFlash(data[2], !(data[3] & 0x01));
06199             break;
06200             
06201         case 7:
06202             // 7 = Device ID report
06203             //     data[2] = ID index: 1=CPU ID, 2=OpenSDA TUID
06204             js.reportID(data[2]);
06205             break;
06206             
06207         case 8:
06208             // 8 = Engage/disengage night mode.
06209             //     data[2] = 1 to engage, 0 to disengage
06210             setNightMode(data[2]);
06211             break;
06212             
06213         case 9:
06214             // 9 = Config variable query.
06215             //     data[2] = config var ID
06216             //     data[3] = array index (for array vars: button assignments, output ports)
06217             {
06218                 // set up the reply buffer with the variable ID data, and zero out
06219                 // the rest of the buffer
06220                 uint8_t reply[8];
06221                 reply[1] = data[2];
06222                 reply[2] = data[3];
06223                 memset(reply+3, 0, sizeof(reply)-3);
06224                 
06225                 // query the value
06226                 configVarGet(reply);
06227                 
06228                 // send the reply
06229                 js.reportConfigVar(reply + 1);
06230             }
06231             break;
06232             
06233         case 10:
06234             // 10 = Build ID query.
06235             js.reportBuildInfo(getBuildID());
06236             break;
06237             
06238         case 11:
06239             // 11 = TV ON relay control.
06240             //      data[2] = operation:
06241             //         0 = turn relay off
06242             //         1 = turn relay on
06243             //         2 = pulse relay (as though the power-on timer fired)
06244             TVRelay(data[2]);
06245             break;
06246             
06247         case 12:
06248             // 12 = Learn IR code.  This enters IR learning mode.  While
06249             // in learning mode, we report raw IR signals and the first IR
06250             // command decoded through the special IR report format.  IR
06251             // learning mode automatically ends after a timeout expires if
06252             // no command can be decoded within the time limit.
06253             
06254             // enter IR learning mode
06255             IRLearningMode = 1;
06256             
06257             // cancel any regular IR input in progress
06258             IRCommandIn = 0;
06259             
06260             // reset and start the learning mode timeout timer
06261             IRTimer.reset();
06262             break;
06263             
06264         case 13:
06265             // 13 = Send button status report
06266             reportButtonStatus(js);
06267             break;
06268             
06269         case 14:
06270             // 14 = manually center the accelerometer
06271             accel.manualCenterRequest();
06272             break;
06273             
06274         case 15:
06275             // 15 = set up ad hoc IR command, part 1.  Mark the command
06276             // as not ready, and save the partial data from the message.
06277             IRAdHocCmd.ready = 0;
06278             IRAdHocCmd.protocol = data[2];
06279             IRAdHocCmd.dittos = (data[3] & IRFlagDittos) != 0;
06280             IRAdHocCmd.code = wireUI32(&data[4]);
06281             break;
06282             
06283         case 16:
06284             // 16 = send ad hoc IR command, part 2.  Fill in the rest
06285             // of the data from the message and mark the command as
06286             // ready.  The IR polling routine will send this as soon
06287             // as the IR transmitter is free.
06288             IRAdHocCmd.code |= (uint64_t(wireUI32(&data[2])) << 32);
06289             IRAdHocCmd.ready = 1;
06290             break;
06291             
06292         case 17:
06293             // 17 = send pre-programmed IR command.  This works just like
06294             // sending an ad hoc command above, but we get the command data
06295             // from an IR slot in the config rather than from the client.
06296             // First make sure we have a valid slot number.
06297             if (data[2] >= 1 && data[2] <= MAX_IR_CODES)
06298             {
06299                 // get the IR command slot in the config
06300                 IRCommandCfg &cmd = cfg.IRCommand[data[2] - 1];
06301                 
06302                 // copy the IR command data from the config
06303                 IRAdHocCmd.protocol = cmd.protocol;
06304                 IRAdHocCmd.dittos = (cmd.flags & IRFlagDittos) != 0;
06305                 IRAdHocCmd.code = (uint64_t(cmd.code.hi) << 32) | cmd.code.lo;
06306                 
06307                 // mark the command as ready - this will trigger the polling
06308                 // routine to send the command as soon as the transmitter
06309                 // is free
06310                 IRAdHocCmd.ready = 1;
06311             }
06312             break;
06313         }
06314     }
06315     else if (data[0] == 66)
06316     {
06317         // Extended protocol - Set configuration variable.
06318         // The second byte of the message is the ID of the variable
06319         // to update, and the remaining bytes give the new value,
06320         // in a variable-dependent format.
06321         configVarSet(data);
06322         
06323         // notify the plunger, so that it can update relevant variables
06324         // dynamically
06325         plungerSensor->onConfigChange(data[1], cfg);
06326     }
06327     else if (data[0] == 67)
06328     {
06329         // SBX - extended SBA message.  This is the same as SBA, except
06330         // that the 7th byte selects a group of 32 ports, to allow access
06331         // to ports beyond the first 32.
06332         sba_sbx(data[6], data);
06333     }
06334     else if (data[0] == 68)
06335     {
06336         // PBX - extended PBA message.  This is similar to PBA, but
06337         // allows access to more than the first 32 ports by encoding
06338         // a port group byte that selects a block of 8 ports.
06339         
06340         // get the port group - the first port is 8*group
06341         int portGroup = data[1];
06342         
06343         // unpack the brightness values
06344         uint32_t tmp1 = data[2] | (data[3]<<8) | (data[4]<<16);
06345         uint32_t tmp2 = data[5] | (data[6]<<8) | (data[7]<<16);
06346         uint8_t bri[8] = {
06347             tmp1 & 0x3F, (tmp1>>6) & 0x3F, (tmp1>>12) & 0x3F, (tmp1>>18) & 0x3F,
06348             tmp2 & 0x3F, (tmp2>>6) & 0x3F, (tmp2>>12) & 0x3F, (tmp2>>18) & 0x3F
06349         };
06350         
06351         // map the flash levels: 60->129, 61->130, 62->131, 63->132
06352         for (int i = 0 ; i < 8 ; ++i)
06353         {
06354             if (bri[i] >= 60)
06355                 bri[i] += 129-60;
06356         }
06357         
06358         // Carry out the PBA
06359         pba_pbx(portGroup*8, bri);
06360     }
06361     else if (data[0] >= 200 && data[0] <= 228)
06362     {
06363         // Extended protocol - Extended output port brightness update.  
06364         // data[0]-200 gives us the bank of 7 outputs we're setting:
06365         // 200 is outputs 0-6, 201 is outputs 7-13, 202 is 14-20, etc.
06366         // The remaining bytes are brightness levels, 0-255, for the
06367         // seven outputs in the selected bank.  The LedWiz flashing 
06368         // modes aren't accessible in this message type; we can only 
06369         // set a fixed brightness, but in exchange we get 8-bit 
06370         // resolution rather than the paltry 0-48 scale that the real
06371         // LedWiz uses.  There's no separate on/off status for outputs
06372         // adjusted with this message type, either, as there would be
06373         // for a PBA message - setting a non-zero value immediately
06374         // turns the output, overriding the last SBA setting.
06375         //
06376         // For outputs 0-31, this overrides any previous PBA/SBA
06377         // settings for the port.  Any subsequent PBA/SBA message will
06378         // in turn override the setting made here.  It's simple - the
06379         // most recent message of either type takes precedence.  For
06380         // outputs above the LedWiz range, PBA/SBA messages can't
06381         // address those ports anyway.
06382         
06383         // figure the block of 7 ports covered in the message
06384         int i0 = (data[0] - 200)*7;
06385         int i1 = i0 + 7 < numOutputs ? i0 + 7 : numOutputs; 
06386         
06387         // update each port
06388         for (int i = i0 ; i < i1 ; ++i)
06389         {
06390             // set the brightness level for the output
06391             uint8_t b = data[i-i0+1];
06392             outLevel[i] = b;
06393             
06394             // set the port's LedWiz state to the nearest equivalent, so
06395             // that it maintains its current setting if we switch back to
06396             // LedWiz mode on a future update
06397             if (b != 0)
06398             {
06399                 // Non-zero brightness - set the SBA switch on, and set the
06400                 // PBA brightness to the DOF brightness rescaled to the 1..48
06401                 // LedWiz range.  If the port is subsequently addressed by an
06402                 // LedWiz command, this will carry the current DOF setting
06403                 // forward unchanged.
06404                 wizOn[i] = 1;
06405                 wizVal[i] = dof_to_lw[b];
06406             }
06407             else
06408             {
06409                 // Zero brightness.  Set the SBA switch off, and leave the
06410                 // PBA brightness the same as it was.
06411                 wizOn[i] = 0;
06412             }
06413             
06414             // set the output
06415             lwPin[i]->set(b);
06416         }
06417         
06418         // update 74HC595 outputs, if attached
06419         if (hc595 != 0)
06420             hc595->update();
06421     }
06422     else 
06423     {
06424         // Everything else is an LedWiz PBA message.  This is a full 
06425         // "profile" dump from the host for one bank of 8 outputs.  Each
06426         // byte sets one output in the current bank.  The current bank
06427         // is implied; the bank starts at 0 and is reset to 0 by any SBA
06428         // message, and is incremented to the next bank by each PBA.  Our
06429         // variable pbaIdx keeps track of the current bank.  There's no 
06430         // direct way for the host to select the bank; it just has to count
06431         // on us staying in sync.  In practice, clients always send the
06432         // full set of 4 PBA messages in a row to set all 32 outputs.
06433         //
06434         // Note that a PBA implicitly overrides our extended profile
06435         // messages (message prefix 200-219), because this sets the
06436         // wizVal[] entry for each output, and that takes precedence
06437         // over the extended protocol settings when we're in LedWiz
06438         // protocol mode.
06439         //
06440         //printf("LWZ-PBA[%d] %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
06441         //       pbaIdx, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
06442 
06443         // carry out the PBA
06444         pba_pbx(pbaIdx, data);
06445         
06446         // update the PBX index state for the next message
06447         pbaIdx = (pbaIdx + 8) % 32;
06448     }
06449 }
06450 
06451 // ---------------------------------------------------------------------------
06452 //
06453 // Main program loop.  This is invoked on startup and runs forever.  Our
06454 // main work is to read our devices (the accelerometer and the CCD), process
06455 // the readings into nudge and plunger position data, and send the results
06456 // to the host computer via the USB joystick interface.  We also monitor
06457 // the USB connection for incoming LedWiz commands and process those into
06458 // port outputs.
06459 //
06460 int main(void)
06461 {
06462     // say hello to the debug console, in case it's connected
06463     printf("\r\nPinscape Controller starting\r\n");
06464     
06465     // Set the default PWM period to 0.5ms = 2 kHz.  This will be used 
06466     // for PWM channels on PWM units whose periods aren't changed 
06467     // explicitly, so it'll apply to LW outputs assigned to GPIO pins.
06468     // The KL25Z only allows the period to be set at the TPM unit
06469     // level, not per channel, so all channels on a given unit will
06470     // necessarily use the same frequency.  We (currently) have two
06471     // subsystems that need specific PWM frequencies: TLC5940NT (which
06472     // uses PWM to generate the grayscale clock signal) and IR remote
06473     // (which uses PWM to generate the IR carrier signal).  Since
06474     // those require specific PWM frequencies, it's important to assign
06475     // those to separate TPM units if both are in use simultaneously;
06476     // the Config Tool includes checks to ensure that will happen when
06477     // setting a config interactively.  In addition, for the greatest
06478     // flexibility, we take care NOT to assign explicit PWM frequencies
06479     // to pins that don't require special frequences.  That way, if a
06480     // pin that doesn't need anything special happens to be sharing a
06481     // TPM unit with a pin that does require a specific frequency, the
06482     // two will co-exist peacefully on the TPM.
06483     //
06484     // We set this default first, before we create any PWM GPIOs, so
06485     // that it will apply to all channels by default but won't override
06486     // any channels that need specific frequences.  Currently, the only
06487     // frequency-agnostic PWM user is the LW outputs, so we can choose
06488     // the default to be suitable for those.  This is chosen to minimize
06489     // flicker on attached LEDs.
06490     NewPwmUnit::defaultPeriod = 0.0005f;
06491         
06492     // clear the I2C connection
06493     clear_i2c();
06494     
06495     // Elevate GPIO pin interrupt priorities, so that they can preempt
06496     // other interrupts.  This is important for some external peripherals,
06497     // particularly the quadrature plunger sensors, which can generate
06498     // high-speed interrupts that need to be serviced quickly to keep
06499     // proper count of the quadrature position.
06500     FastInterruptIn::elevatePriority();
06501 
06502     // Load the saved configuration.  There are two sources of the
06503     // configuration data:
06504     //
06505     // - Look for an NVM (flash non-volatile memory) configuration.
06506     // If this is valid, we'll load it.  The NVM is config data that can 
06507     // be updated dynamically by the host via USB commands and then stored 
06508     // in the flash by the firmware itself.  If this exists, it supersedes 
06509     // any of the other settings stores.  The Windows config tool uses this
06510     // to store user settings updates.
06511     //
06512     // - If there's no NVM, we'll load the factory defaults, then we'll
06513     // load any settings stored in the host-loaded configuration.  The
06514     // host can patch a set of configuration variable settings into the
06515     // .bin file when loading new firmware, in the host-loaded config
06516     // area that we reserve for this purpose.  This allows the host to
06517     // restore a configuration at the same time it installs firmware,
06518     // without a separate download of the config data.
06519     //
06520     // The NVM supersedes the host-loaded config, since it can be updated
06521     // between firmware updated and is thus presumably more recent if it's
06522     // present.  (Note that the NVM and host-loaded config are both in    
06523     // flash, so in principle we could just have a single NVM store that
06524     // the host patches.  The only reason we don't is that the NVM store
06525     // is an image of our in-memory config structure, which is a native C
06526     // struct, and we don't want the host to have to know the details of 
06527     // its byte layout, for obvious reasons.  The host-loaded config, in
06528     // contrast, uses the wire protocol format, which has a well-defined
06529     // byte layout that's independent of the firmware version or the
06530     // details of how the C compiler arranges the struct memory.)
06531     if (!loadConfigFromFlash())
06532         loadHostLoadedConfig();
06533     
06534     // initialize the diagnostic LEDs
06535     initDiagLEDs(cfg);
06536 
06537     // we're not connected/awake yet
06538     bool connected = false;
06539     Timer connectChangeTimer;
06540 
06541     // create the plunger sensor interface
06542     createPlunger();
06543     
06544     // update the plunger reader's cached calibration data
06545     plungerReader.onUpdateCal();
06546 
06547     // set up the TLC5940 interface, if these chips are present
06548     init_tlc5940(cfg);
06549 
06550     // initialize the TLC5916 interface, if these chips are present
06551     init_tlc59116(cfg);
06552     
06553     // set up 74HC595 interface, if these chips are present
06554     init_hc595(cfg);
06555     
06556     // Initialize the LedWiz ports.  Note that the ordering here is important:
06557     // this has to come after we create the TLC5940 and 74HC595 object instances
06558     // (which we just did above), since we need to access those objects to set
06559     // up ports assigned to the respective chips.
06560     initLwOut(cfg);
06561 
06562     // start the TLC5940 refresh cycle clock
06563     if (tlc5940 != 0)
06564         tlc5940->start();
06565         
06566     // Assume that nothing uses keyboard keys.  We'll check for keyboard
06567     // usage when initializing the various subsystems that can send keys
06568     // (buttons, IR).  If we find anything that does, we'll create the
06569     // USB keyboard interface.
06570     bool kbKeys = false;
06571 
06572     // set up the IR remote control emitter & receiver, if present
06573     init_IR(cfg, kbKeys);
06574 
06575     // start the power status time, if applicable
06576     startPowerStatusTimer(cfg);
06577 
06578     // initialize the button input ports
06579     initButtons(cfg, kbKeys);
06580     
06581     // Create the joystick USB client.  Note that the USB vendor/product ID
06582     // information comes from the saved configuration.  Also note that we have
06583     // to wait until after initializing the input buttons (which we just did
06584     // above) to set up the interface, since the button setup will determine
06585     // whether or not we need to present a USB keyboard interface in addition
06586     // to the joystick interface.
06587     MyUSBJoystick js(cfg.usbVendorID, cfg.usbProductID, USB_VERSION_NO, false, 
06588         cfg.joystickEnabled, cfg.joystickAxisFormat, kbKeys);
06589         
06590     // start the request timestamp timer
06591     requestTimestamper.start();
06592         
06593     // Wait for the USB connection to start up.  Show a distinctive diagnostic
06594     // flash pattern while waiting.
06595     Timer connTimeoutTimer, connFlashTimer;
06596     connTimeoutTimer.start();
06597     connFlashTimer.start();
06598     while (!js.configured())
06599     {
06600         // show one short yellow flash at 2-second intervals
06601         if (connFlashTimer.read_us() > 2000000)
06602         {
06603             // short yellow flash
06604             diagLED(1, 1, 0);
06605             wait_us(50000);
06606             diagLED(0, 0, 0);
06607             
06608             // reset the flash timer
06609             connFlashTimer.reset();
06610         }
06611 
06612         // If we've been disconnected for more than the reboot timeout,
06613         // reboot.  Some PCs won't reconnect if we were left plugged in
06614         // during a power cycle on the PC, but fortunately a reboot on
06615         // the KL25Z will make the host notice us and trigger a reconnect.
06616         // Don't do this if we're in a non-recoverable PSU2 power state.
06617         if (cfg.disconnectRebootTimeout != 0 
06618             && connTimeoutTimer.read() > cfg.disconnectRebootTimeout
06619             && powerStatusAllowsReboot())
06620             reboot(js, false, 0);
06621             
06622         // update the PSU2 power sensing status
06623         powerStatusUpdate(cfg);
06624     }
06625     
06626     // we're now connected to the host
06627     connected = true;
06628     
06629     // Set up a timer for keeping track of how long it's been since we
06630     // sent the last joystick report.  We use this to determine when it's
06631     // time to send the next joystick report.  
06632     //
06633     // We have to use a timer for two reasons.  The first is that our main
06634     // loop runs too fast (about .25ms to 2.5ms per loop, depending on the
06635     // type of plunger sensor attached and other factors) for us to send
06636     // joystick reports on every iteration.  We *could*, but the PC couldn't
06637     // digest them at that pace.  So we need to slow down the reports to a
06638     // reasonable pace.  The second is that VP has some complicated timing
06639     // issues of its own, so we not only need to slow down the reports from
06640     // our "natural" pace, but also time them to sync up with VP's input
06641     // sampling rate as best we can.
06642     Timer jsReportTimer;
06643     jsReportTimer.start();
06644     
06645     // Accelerometer sample "stutter" counter.  Each time we send a joystick
06646     // report, we increment this counter, and check to see if it has reached 
06647     // the threshold set in the configuration.  If so, we take a new 
06648     // accelerometer sample and send it with the new joystick report.  It
06649     // not, we don't take a new sample, but simply repeat the last sample.
06650     //
06651     // This lets us send joystick reports more frequently than accelerometer
06652     // samples.  The point is to let us slow down accelerometer reports to
06653     // a pace that matches VP's input sampling frequency, while still sending
06654     // joystick button updates more frequently, so that other programs that
06655     // can read input faster will see button changes with less latency.
06656     int jsAccelStutterCounter = 0;
06657     
06658     // Last accelerometer report, in joystick units.  We normally report the 
06659     // acceleromter reading via the joystick X and Y axes, per the VP 
06660     // convention.  We can alternatively report in the RX and RY axes; this
06661     // can be set in the configuration.
06662     int x = 0, y = 0;
06663     
06664     // Time since we successfully sent a USB report.  This is a hacky 
06665     // workaround to deal with any remaining sporadic problems in the USB 
06666     // stack.  I've been trying to bulletproof the USB code over time to 
06667     // remove all such problems at their source, but it seems unlikely that
06668     // we'll ever get them all.  Thus this hack.  The idea here is that if
06669     // we go too long without successfully sending a USB report, we'll
06670     // assume that the connection is broken (and the KL25Z USB hardware
06671     // hasn't noticed this), and we'll try taking measures to recover.
06672     Timer jsOKTimer;
06673     jsOKTimer.start();
06674     
06675     // Initialize the calibration button and lamp, if enabled.  To be enabled,
06676     // the pin has to be assigned to something other than NC (0xFF), AND the
06677     // corresponding feature enable flag has to be set.
06678     DigitalIn *calBtn = 0;
06679     DigitalOut *calBtnLed = 0;
06680     
06681     // calibration button input - feature flag 0x01
06682     if ((cfg.plunger.cal.features & 0x01) && cfg.plunger.cal.btn != 0xFF)
06683         calBtn = new DigitalIn(wirePinName(cfg.plunger.cal.btn));
06684         
06685     // calibration button indicator lamp output - feature flag 0x02
06686     if ((cfg.plunger.cal.features & 0x02) && cfg.plunger.cal.led != 0xFF)
06687         calBtnLed = new DigitalOut(wirePinName(cfg.plunger.cal.led));
06688 
06689     // initialize the calibration button 
06690     calBtnTimer.start();
06691     calBtnState = 0;
06692     
06693     // set up a timer for our heartbeat indicator
06694     Timer hbTimer;
06695     hbTimer.start();
06696     int hb = 0;
06697     uint16_t hbcnt = 0;
06698     
06699     // set a timer for accelerometer auto-centering
06700     Timer acTimer;
06701     acTimer.start();
06702     
06703     // create the accelerometer object
06704     Accel accel(MMA8451_SCL_PIN, MMA8451_SDA_PIN, MMA8451_I2C_ADDRESS, 
06705         MMA8451_INT_PIN, cfg.accel.range, cfg.accel.autoCenterTime);
06706        
06707     // initialize the plunger sensor
06708     plungerSensor->init();
06709     
06710     // set up the ZB Launch Ball monitor
06711     ZBLaunchBall zbLaunchBall;
06712     
06713     // enable the peripheral chips
06714     if (tlc5940 != 0)
06715         tlc5940->enable(true);
06716     if (hc595 != 0)
06717         hc595->enable(true);
06718     if (tlc59116 != 0)
06719         tlc59116->enable(true);
06720         
06721     // start the LedWiz flash cycle timer
06722     wizCycleTimer.start();
06723     
06724     // start the PWM update polling timer
06725     polledPwmTimer.start();
06726     
06727     // we're all set up - now just loop, processing sensor reports and 
06728     // host requests
06729     for (;;)
06730     {
06731         // start the main loop timer for diagnostic data collection
06732         IF_DIAG(mainLoopTimer.reset(); mainLoopTimer.start();)
06733         
06734         // Process incoming reports on the joystick interface.  The joystick
06735         // "out" (receive) endpoint is used for LedWiz commands and our 
06736         // extended protocol commands.  Limit processing time to 5ms to
06737         // ensure we don't starve the input side.
06738         LedWizMsg lwm;
06739         Timer lwt;
06740         lwt.start();
06741         IF_DIAG(int msgCount = 0;) 
06742         while (js.readLedWizMsg(lwm) && lwt.read_us() < 5000)
06743         {
06744             handleInputMsg(lwm, js, accel);
06745             IF_DIAG(++msgCount;)
06746         }
06747         
06748         // collect performance statistics on the message reader, if desired
06749         IF_DIAG(
06750             if (msgCount != 0)
06751             {
06752                 mainLoopMsgTime += lwt.read_us();
06753                 mainLoopMsgCount++;
06754             }
06755         )
06756         
06757         // process IR input
06758         process_IR(cfg, js);
06759     
06760         // update the PSU2 power sensing status
06761         powerStatusUpdate(cfg);
06762 
06763         // update flashing LedWiz outputs periodically
06764         wizPulse();
06765         
06766         // update PWM outputs
06767         pollPwmUpdates();
06768         
06769         // update Flipper Logic and Chime Logic outputs
06770         LwFlipperLogicOut::poll();
06771         LwChimeLogicOut::poll();
06772         
06773         // poll the accelerometer
06774         accel.poll();
06775             
06776         // Note the "effective" plunger enabled status.  This has two
06777         // components: the explicit "enabled" bit, and the plunger sensor
06778         // type setting.  For most purposes, a plunger type of NONE is
06779         // equivalent to disabled.  Set this to explicit 0x01 or 0x00
06780         // so that we can OR the bit into status reports.
06781         uint8_t effectivePlungerEnabled = (cfg.plunger.enabled
06782             && cfg.plunger.sensorType != PlungerType_None) ? 0x01 : 0x00;
06783             
06784         // collect diagnostic statistics, checkpoint 0
06785         IF_DIAG(mainLoopIterCheckpt[0] += mainLoopTimer.read_us();)
06786 
06787         // send TLC5940 data updates if applicable
06788         if (tlc5940 != 0)
06789             tlc5940->send();
06790             
06791         // send TLC59116 data updates
06792         if (tlc59116 != 0)
06793             tlc59116->send();
06794        
06795         // collect diagnostic statistics, checkpoint 1
06796         IF_DIAG(mainLoopIterCheckpt[1] += mainLoopTimer.read_us();)
06797         
06798         // check for plunger calibration
06799         if (calBtn != 0 && !calBtn->read())
06800         {
06801             // check the state
06802             switch (calBtnState)
06803             {
06804             case 0: 
06805                 // button not yet pushed - start debouncing
06806                 calBtnTimer.reset();
06807                 calBtnState = 1;
06808                 break;
06809                 
06810             case 1:
06811                 // pushed, not yet debounced - if the debounce time has
06812                 // passed, start the hold period
06813                 if (calBtnTimer.read_us() > 50000)
06814                     calBtnState = 2;
06815                 break;
06816                 
06817             case 2:
06818                 // in the hold period - if the button has been held down
06819                 // for the entire hold period, move to calibration mode
06820                 if (calBtnTimer.read_us() > 2050000)
06821                 {
06822                     // enter calibration mode
06823                     calBtnState = 3;
06824                     calBtnTimer.reset();
06825                     
06826                     // begin the plunger calibration limits
06827                     plungerReader.setCalMode(true);
06828                 }
06829                 break;
06830                 
06831             case 3:
06832                 // Already in calibration mode - pushing the button here
06833                 // doesn't change the current state, but we won't leave this
06834                 // state as long as it's held down.  So nothing changes here.
06835                 break;
06836             }
06837         }
06838         else
06839         {
06840             // Button released.  If we're in calibration mode, and
06841             // the calibration time has elapsed, end the calibration
06842             // and save the results to flash.
06843             //
06844             // Otherwise, return to the base state without saving anything.
06845             // If the button is released before we make it to calibration
06846             // mode, it simply cancels the attempt.
06847             if (calBtnState == 3 && calBtnTimer.read_us() > 15000000)
06848             {
06849                 // exit calibration mode
06850                 calBtnState = 0;
06851                 plungerReader.setCalMode(false);
06852                 
06853                 // save the updated configuration
06854                 cfg.plunger.cal.calibrated = 1;
06855                 saveConfigToFlash(0, false);
06856             }
06857             else if (calBtnState != 3)
06858             {
06859                 // didn't make it to calibration mode - cancel the operation
06860                 calBtnState = 0;
06861             }
06862         }       
06863         
06864         // light/flash the calibration button light, if applicable
06865         int newCalBtnLit = calBtnLit;
06866         switch (calBtnState)
06867         {
06868         case 2:
06869             // in the hold period - flash the light
06870             newCalBtnLit = ((calBtnTimer.read_us()/250000) & 1);
06871             break;
06872             
06873         case 3:
06874             // calibration mode - show steady on
06875             newCalBtnLit = true;
06876             break;
06877             
06878         default:
06879             // not calibrating/holding - show steady off
06880             newCalBtnLit = false;
06881             break;
06882         }
06883         
06884         // light or flash the external calibration button LED, and 
06885         // do the same with the on-board blue LED
06886         if (calBtnLit != newCalBtnLit)
06887         {
06888             calBtnLit = newCalBtnLit;
06889             if (calBtnLit) {
06890                 if (calBtnLed != 0)
06891                     calBtnLed->write(1);
06892                 diagLED(0, 0, 1);       // blue
06893             }
06894             else {
06895                 if (calBtnLed != 0)
06896                     calBtnLed->write(0);
06897                 diagLED(0, 0, 0);       // off
06898             }
06899         }
06900         
06901         // collect diagnostic statistics, checkpoint 2
06902         IF_DIAG(mainLoopIterCheckpt[2] += mainLoopTimer.read_us();)
06903 
06904         // read the plunger sensor
06905         plungerReader.read();
06906         
06907         // collect diagnostic statistics, checkpoint 3
06908         IF_DIAG(mainLoopIterCheckpt[3] += mainLoopTimer.read_us();)
06909 
06910         // update the ZB Launch Ball status
06911         zbLaunchBall.update();
06912         
06913         // collect diagnostic statistics, checkpoint 4
06914         IF_DIAG(mainLoopIterCheckpt[4] += mainLoopTimer.read_us();)
06915 
06916         // process button updates
06917         processButtons(cfg);
06918         
06919         // collect diagnostic statistics, checkpoint 5
06920         IF_DIAG(mainLoopIterCheckpt[5] += mainLoopTimer.read_us();)
06921 
06922         // send a keyboard report if we have new data
06923         if (kbState.changed)
06924         {
06925             // send a keyboard report
06926             js.kbUpdate(kbState.data);
06927             kbState.changed = false;
06928         }
06929         
06930         // likewise for the media controller
06931         if (mediaState.changed)
06932         {
06933             // send a media report
06934             js.mediaUpdate(mediaState.data);
06935             mediaState.changed = false;
06936         }
06937         
06938         // collect diagnostic statistics, checkpoint 6
06939         IF_DIAG(mainLoopIterCheckpt[6] += mainLoopTimer.read_us();)
06940 
06941         // flag:  did we successfully send a joystick report on this round?
06942         bool jsOK = false;
06943         
06944         // figure the current status flags for joystick reports
06945         uint16_t statusFlags = 
06946             effectivePlungerEnabled         // 0x01
06947             | nightMode                     // 0x02
06948             | ((psu2_state & 0x07) << 2)    // 0x04 0x08 0x10
06949             | saveConfigSucceededFlag;      // 0x40
06950         if (IRLearningMode != 0)
06951             statusFlags |= 0x20;
06952 
06953         // If it's been long enough since our last USB status report, send
06954         // the new report.  VP only polls for input in 10ms intervals, so
06955         // there's no benefit in sending reports more frequently than this.
06956         // More frequent reporting would only add USB I/O overhead.
06957         if (cfg.joystickEnabled && jsReportTimer.read_us() > cfg.jsReportInterval_us)
06958         {
06959             // Increment the "stutter" counter.  If it has reached the
06960             // stutter threshold, read a new accelerometer sample.  If 
06961             // not, repeat the last sample.
06962             if (++jsAccelStutterCounter >= cfg.accel.stutter)
06963             {
06964                 // read the accelerometer
06965                 int xa, ya;
06966                 accel.get(xa, ya);
06967             
06968                 // confine the results to our joystick axis range
06969                 if (xa < -JOYMAX) xa = -JOYMAX;
06970                 if (xa > JOYMAX) xa = JOYMAX;
06971                 if (ya < -JOYMAX) ya = -JOYMAX;
06972                 if (ya > JOYMAX) ya = JOYMAX;
06973                 
06974                 // store the updated accelerometer coordinates
06975                 x = xa;
06976                 y = ya;
06977                 
06978                 // rotate X and Y according to the device orientation in the cabinet
06979                 accelRotate(x, y);
06980 
06981                 // reset the stutter counter
06982                 jsAccelStutterCounter = 0;
06983             }
06984             
06985             // Report the current plunger position unless the plunger is
06986             // disabled, or the ZB Launch Ball signal is on.  In either of
06987             // those cases, just report a constant 0 value.  ZB Launch Ball 
06988             // temporarily disables mechanical plunger reporting because it 
06989             // tells us that the table has a Launch Ball button instead of
06990             // a traditional plunger, so we don't want to confuse VP with
06991             // regular plunger inputs.
06992             int zActual = plungerReader.getPosition();
06993             int zReported = (!effectivePlungerEnabled || zbLaunchOn ? 0 : zActual);
06994             
06995             // send the joystick report
06996             jsOK = js.update(x, y, zReported, jsButtons, statusFlags);
06997             
06998             // we've just started a new report interval, so reset the timer
06999             jsReportTimer.reset();
07000         }
07001 
07002         // If we're in sensor status mode, report all pixel exposure values
07003         if (reportPlungerStat && plungerSensor->ready())
07004         {
07005             // send the report            
07006             plungerSensor->sendStatusReport(js, reportPlungerStatFlags);
07007 
07008             // we have satisfied this request
07009             reportPlungerStat = false;
07010         }
07011         
07012         // Reset the plunger status report extra timer after enough time has
07013         // elapsed to satisfy the request.  We don't just do this immediately
07014         // because of the complexities of the pixel frame buffer pipelines in
07015         // most of the image sensors.  The pipelines delay the effect of the
07016         // exposure time request by a couple of frames, so we can't be sure
07017         // exactly when they're applied - meaning we can't consider the
07018         // delay time to be consumed after a fixed number of frames.  Instead,
07019         // we'll consider it consumed after a long enough time to be sure
07020         // we've sent a few frames.  The extra time value is meant to be an
07021         // interactive tool for debugging, so it's not important to reset it
07022         // immediately - the user will probably want to see the effect over
07023         // many frames, so they're likely to keep sending requests with the
07024         // time value over and over.  They'll eventually shut down the frame
07025         // viewer and return to normal operation, at which point the requests
07026         // will stop.  So we just have to clear things out after we haven't
07027         // seen a request with extra time for a little while.
07028         if (reportPlungerStatTime != 0 
07029             && static_cast<uint32_t>(requestTimestamper.read_us() - tReportPlungerStat) > 1000000)
07030         {
07031             reportPlungerStatTime = 0;
07032             plungerSensor->setExtraIntegrationTime(0);
07033         }
07034         
07035         // If joystick reports are turned off, send a generic status report
07036         // periodically for the sake of the Windows config tool.
07037         if (!cfg.joystickEnabled && jsReportTimer.read_us() > 10000UL)
07038         {
07039             jsOK = js.updateStatus(statusFlags);
07040             jsReportTimer.reset();
07041         }
07042 
07043         // if we successfully sent a joystick report, reset the watchdog timer
07044         if (jsOK) 
07045         {
07046             jsOKTimer.reset();
07047             jsOKTimer.start();
07048         }
07049 
07050         // collect diagnostic statistics, checkpoint 7
07051         IF_DIAG(mainLoopIterCheckpt[7] += mainLoopTimer.read_us();)
07052 
07053 #ifdef DEBUG_PRINTF
07054         if (x != 0 || y != 0)
07055             printf("%d,%d\r\n", x, y);
07056 #endif
07057 
07058         // check for connection status changes
07059         bool newConnected = js.isConnected() && !js.isSleeping();
07060         if (newConnected != connected)
07061         {
07062             // give it a moment to stabilize
07063             connectChangeTimer.start();
07064             if (connectChangeTimer.read_us() > 1000000)
07065             {
07066                 // note the new status
07067                 connected = newConnected;
07068                 
07069                 // done with the change timer for this round - reset it for next time
07070                 connectChangeTimer.stop();
07071                 connectChangeTimer.reset();
07072                 
07073                 // if we're newly disconnected, clean up for PC suspend mode or power off
07074                 if (!connected)
07075                 {
07076                     // turn off all outputs
07077                     allOutputsOff();
07078                     
07079                     // The KL25Z runs off of USB power, so we might (depending on the PC
07080                     // and OS configuration) continue to receive power even when the main
07081                     // PC power supply is turned off, such as in soft-off or suspend/sleep
07082                     // mode.  Any external output controller chips (TLC5940, 74HC595) might
07083                     // be powered from the PC power supply directly rather than from our
07084                     // USB power, so they might be powered off even when we're still running.
07085                     // To ensure cleaner startup when the power comes back on, globally
07086                     // disable the outputs.  The global disable signals come from GPIO lines
07087                     // that remain powered as long as the KL25Z is powered, so these modes
07088                     // will apply smoothly across power state transitions in the external
07089                     // hardware.  That is, when the external chips are powered up, they'll
07090                     // see the global disable signals as stable voltage inputs immediately,
07091                     // which will cause them to suppress any output triggering.  This ensures
07092                     // that we don't fire any solenoids or flash any lights spuriously when
07093                     // the power first comes on.
07094                     if (tlc5940 != 0)
07095                         tlc5940->enable(false);
07096                     if (tlc59116 != 0)
07097                         tlc59116->enable(false);
07098                     if (hc595 != 0)
07099                         hc595->enable(false);
07100                 }
07101             }
07102         }
07103         
07104         // if we have a reboot timer pending, check for completion
07105         if (saveConfigFollowupTimer.isRunning() 
07106             && saveConfigFollowupTimer.read_us() > saveConfigFollowupTime*1000000UL)
07107         {
07108             // if a reboot is pending, execute it now
07109             if (saveConfigRebootPending)
07110             {
07111                 // Only reboot if the PSU2 power state allows it.  If it 
07112                 // doesn't, suppress the reboot for now, but leave the boot
07113                 // flags set so that we keep checking on future rounds.
07114                 // That way we should eventually reboot when the power
07115                 // status allows it.
07116                 if (powerStatusAllowsReboot())
07117                     reboot(js);
07118             }
07119             else
07120             {
07121                 // No reboot required.  Exit the timed post-save state.
07122                 
07123                 // stop and reset the post-save timer
07124                 saveConfigFollowupTimer.stop();
07125                 saveConfigFollowupTimer.reset();
07126                 
07127                 // clear the post-save success flag
07128                 saveConfigSucceededFlag = 0;
07129             }
07130         }
07131                         
07132         // if we're disconnected, initiate a new connection
07133         if (!connected)
07134         {
07135             // show USB HAL debug events
07136             extern void HAL_DEBUG_PRINTEVENTS(const char *prefix);
07137             HAL_DEBUG_PRINTEVENTS(">DISC");
07138             
07139             // show immediate diagnostic feedback
07140             js.diagFlash();
07141             
07142             // clear any previous diagnostic LED display
07143             diagLED(0, 0, 0);
07144             
07145             // set up a timer to monitor the reboot timeout
07146             Timer reconnTimeoutTimer;
07147             reconnTimeoutTimer.start();
07148             
07149             // set up a timer for diagnostic displays
07150             Timer diagTimer;
07151             diagTimer.reset();
07152             diagTimer.start();
07153             
07154             // turn off the main loop timer while spinning
07155             IF_DIAG(mainLoopTimer.stop();)
07156 
07157             // loop until we get our connection back            
07158             while (!js.isConnected() || js.isSleeping())
07159             {
07160                 // try to recover the connection
07161                 js.recoverConnection();
07162                 
07163                 // update Flipper Logic and Chime Logic outputs
07164                 LwFlipperLogicOut::poll();
07165                 LwChimeLogicOut::poll();
07166 
07167                 // send TLC5940 data if necessary
07168                 if (tlc5940 != 0)
07169                     tlc5940->send();
07170                     
07171                 // update TLC59116 outputs
07172                 if (tlc59116 != 0)
07173                     tlc59116->send();
07174                 
07175                 // show a diagnostic flash every couple of seconds
07176                 if (diagTimer.read_us() > 2000000)
07177                 {
07178                     // flush the USB HAL debug events, if in debug mode
07179                     HAL_DEBUG_PRINTEVENTS(">NC");
07180                     
07181                     // show diagnostic feedback
07182                     js.diagFlash();
07183                     
07184                     // reset the flash timer
07185                     diagTimer.reset();
07186                 }
07187                 
07188                 // If the disconnect reboot timeout has expired, reboot.
07189                 // Some PC hosts won't reconnect to a device that's left
07190                 // plugged in through various events on the PC side, such as 
07191                 // rebooting Windows, cycling power on the PC, or just a lost
07192                 // USB connection.  Rebooting the KL25Z seems to be the most
07193                 // reliable way to get Windows to notice us again after one
07194                 // of these events and make it reconnect.  Only reboot if
07195                 // the PSU2 power status allows it - if not, skip it on this
07196                 // round and keep waiting.
07197                 if (cfg.disconnectRebootTimeout != 0 
07198                     && reconnTimeoutTimer.read() > cfg.disconnectRebootTimeout
07199                     && powerStatusAllowsReboot())
07200                     reboot(js, false, 0);
07201 
07202                 // update the PSU2 power sensing status
07203                 powerStatusUpdate(cfg);
07204             }
07205             
07206             // resume the main loop timer
07207             IF_DIAG(mainLoopTimer.start();)
07208             
07209             // if we made it out of that loop alive, we're connected again!
07210             connected = true;
07211             HAL_DEBUG_PRINTEVENTS(">C");
07212 
07213             // Enable peripheral chips and update them with current output data
07214             if (tlc5940 != 0)
07215                 tlc5940->enable(true);
07216             if (tlc59116 != 0)
07217                 tlc59116->enable(true);
07218             if (hc595 != 0)
07219             {
07220                 hc595->enable(true);
07221                 hc595->update(true);
07222             }
07223         }
07224 
07225         // provide a visual status indication on the on-board LED
07226         if (calBtnState < 2 && hbTimer.read_us() > 1000000) 
07227         {
07228             if (jsOKTimer.read_us() > 1000000)
07229             {
07230                 // USB freeze - show red/yellow.
07231                 //
07232                 // It's been more than a second since we successfully sent a joystick
07233                 // update message.  This must mean that something's wrong on the USB
07234                 // connection, even though we haven't detected an outright disconnect.
07235                 // Show a distinctive diagnostic LED pattern when this occurs.
07236                 hb = !hb;
07237                 diagLED(1, hb, 0);
07238                 
07239                 // If the reboot-on-disconnect option is in effect, treat this condition
07240                 // as equivalent to a disconnect, since something is obviously wrong
07241                 // with the USB connection.  
07242                 if (cfg.disconnectRebootTimeout != 0)
07243                 {
07244                     // The reboot timeout is in effect.  If we've been incommunicado for
07245                     // longer than the timeout, reboot.  If we haven't reached the time
07246                     // limit, keep running for now, and leave the OK timer running so 
07247                     // that we can continue to monitor this.  Only reboot if the PSU2
07248                     // power status allows it.
07249                     if (jsOKTimer.read() > cfg.disconnectRebootTimeout
07250                         && powerStatusAllowsReboot())
07251                         reboot(js, false, 0);
07252                 }
07253                 else
07254                 {
07255                     // There's no reboot timer, so just keep running with the diagnostic
07256                     // pattern displayed.  Since we're not waiting for any other timed
07257                     // conditions in this state, stop the timer so that it doesn't 
07258                     // overflow if this condition persists for a long time.
07259                     jsOKTimer.stop();
07260                 }
07261             }
07262             else if (psu2_state >= 4)
07263             {
07264                 // We're in the TV timer countdown.  Skip the normal heartbeat
07265                 // flashes and show the TV timer flashes instead.
07266                 diagLED(0, 0, 0);
07267             }
07268             else if (effectivePlungerEnabled && !cfg.plunger.cal.calibrated)
07269             {
07270                 // connected, plunger calibration needed - flash yellow/green
07271                 hb = !hb;
07272                 diagLED(hb, 1, 0);
07273             }
07274             else
07275             {
07276                 // connected - flash blue/green
07277                 hb = !hb;
07278                 diagLED(0, hb, !hb);
07279             }
07280             
07281             // reset the heartbeat timer
07282             hbTimer.reset();
07283             ++hbcnt;
07284         }
07285         
07286         // collect statistics on the main loop time, if desired
07287         IF_DIAG(
07288             mainLoopIterTime += mainLoopTimer.read_us();
07289             mainLoopIterCount++;
07290         )
07291     }
07292 }