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