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
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 }
Generated on Wed Jul 13 2022 03:30:11 by 1.7.2