Pinscape Controller version 1 fork. This is a fork to allow for ongoing bug fixes to the original controller version, from before the major changes for the expansion board project.
Dependencies: FastIO FastPWM SimpleDMA mbed
Fork of Pinscape_Controller by
Revision 29:582472d0bc57, committed 2015-09-25
- Comitter:
- mjr
- Date:
- Fri Sep 25 18:49:53 2015 +0000
- Parent:
- 28:2097c6f8f2db
- Child:
- 30:6e9902f06f48
- Commit message:
- Test of direct bit writes instead of SPI.
Changed in this revision
--- a/FastIO.lib Wed Sep 23 05:38:27 2015 +0000 +++ b/FastIO.lib Fri Sep 25 18:49:53 2015 +0000 @@ -1,1 +1,1 @@ -http://developer.mbed.org/users/Sissors/code/FastIO/#327ae1d5fecb +http://developer.mbed.org/users/Sissors/code/FastIO/#199aca52ac42
--- a/TLC5940/TLC5940.h Wed Sep 23 05:38:27 2015 +0000 +++ b/TLC5940/TLC5940.h Fri Sep 25 18:49:53 2015 +0000 @@ -48,6 +48,7 @@ * isn't a factor. E.g., at SPI=30MHz and GSCLK=500kHz, * t(blank) is 8192us and t(refresh) is 25us. */ +#define USE_SPI 1 #define SPI_SPEED 3000000 /** @@ -111,7 +112,11 @@ * @param nchips - The number of TLC5940s (if you are daisy chaining) */ TLC5940(PinName SCLK, PinName MOSI, PinName GSCLK, PinName BLANK, PinName XLAT, int nchips) +#if USE_SPI : spi(MOSI, NC, SCLK), +#else + : sin(MOSI), sclk(SCLK), +#endif gsclk(GSCLK), blank(BLANK), xlat(XLAT), @@ -122,6 +127,7 @@ gs = new unsigned short[nchips*16]; memset(gs, 0, nchips*16*sizeof(gs[0])); +#if USE_SPI // Configure SPI format and speed. Note that KL25Z ONLY supports 8-bit // mode. The TLC5940 nominally requires 12-bit data blocks for the // grayscale levels, but SPI is ultimately just a bit-level serial format, @@ -131,7 +137,10 @@ // format 0. spi.format(8, 0); spi.frequency(SPI_SPEED); - +#else + sclk = 1; +#endif + // Set output pin states xlat = 0; blank = 1; @@ -140,7 +149,11 @@ gsclk.period(1.0/GSCLK_SPEED); gsclk.write(.5); blank = 0; - + } + + // start the clock running + void start() + { // Set up the first call to the reset function, which asserts BLANK to // end the PWM cycle and handles new grayscale data output and latching. // The original version of this library uses a timer to call reset @@ -176,15 +189,20 @@ { // store the data, and flag the pending update for the interrupt handler to carry out gs[idx] = data; - newGSData = true; +// newGSData = true; } private: // current level for each output unsigned short *gs; +#if USE_SPI // SPI port - only MOSI and SCK are used SPI spi; +#else + DigitalOut sin; + DigitalOut sclk; +#endif // use a PWM out for the grayscale clock - this provides a stable // square wave signal without consuming CPU @@ -212,7 +230,7 @@ blank = 1; // If we have new GS data, send it now - if (newGSData) + if (true) // (newGSData) { // Send the new grayscale data. // @@ -267,6 +285,7 @@ void update() { +#if USE_SPI // Send GS data. The serial format orders the outputs from last to first // (output #15 on the last chip in the daisy-chain to output #0 on the // first chip). For each output, we send 12 bits containing the grayscale @@ -281,7 +300,7 @@ // [ element i+1 bits ] [ element i bits ] // 11 10 9 8 7 6 5 4 3 2 1 0 11 10 9 8 7 6 5 4 3 2 1 0 // [ first byte ] [ second byte ] [ third byte ] - for (int i = (16 * nchips) - 2 ; i >= 0 ; i -= 2) + for (int i = 61 /* (16 * nchips) - 2 */ ; i >= 0 ; i -= 2) { // first byte - element i+1 bits 4-11 spi.write(((gs[i+1] & 0xFF0) >> 4) & 0xff); @@ -292,6 +311,20 @@ // third byte - element i bits 0-7 spi.write(gs[i] & 0x0FF); } +#else + // Send GS data, from last output to first output, 12 bits per output, + // most significant bit first. + for (int i = 16*3 - 1 ; i >= 0 ; --i) + { + unsigned data = gs[i]; + for (unsigned int mask = 1 << 11, bit = 0 ; bit < 12 ; ++bit, mask >>= 1) + { + sclk = 0; + sin = (data & mask) ? 1 : 0; + sclk = 1; + } + } +#endif } };
--- a/USBJoystick/USBJoystick.cpp Wed Sep 23 05:38:27 2015 +0000 +++ b/USBJoystick/USBJoystick.cpp Fri Sep 25 18:49:53 2015 +0000 @@ -150,30 +150,8 @@ { USAGE_PAGE(1), 0x01, // Generic desktop USAGE(1), 0x04, // Joystick - COLLECTION(1), 0x01, // Application - // NB - the canonical joystick has a nested collection at this - // point. We remove the inner collection to enable the LedWiz - // emulation. The LedWiz API implementation on the PC side - // appears to use the collection structure as part of the - // device signature, and the real LedWiz descriptor has just - // one top-level collection. The built-in Windows HID drivers - // don't appear to care whether this collection is present or - // not for the purposes of recognizing a joystick, so it seems - // to make everyone happy to leave it out. - // - // All of the reference material for USB joystick device builders - // does use the inner collection, so it's possible that omitting - // it will create an incompatibility with some non-Windows hosts. - // But that seems largely moot in that VP only runs on Windows. - // If you're you're trying to adapt this code for a different - // device and run into problems connecting to a non-Windows host, - // try restoring the inner collection. You probably won't - // care about LedWiz compatibility in such a situation so there - // should be no reason not to return to the standard structure. - // COLLECTION(1), 0x00, // Physical - // input report (device to host) USAGE_PAGE(1), 0x06, // generic device controls - for config status @@ -207,12 +185,12 @@ // output report (host to device) REPORT_SIZE(1), 0x08, // 8 bits per report - REPORT_COUNT(1), 0x08, // output report count (LEDWiz messages) + REPORT_COUNT(1), 0x08, // output report count - 8-byte LedWiz format 0x09, 0x01, // usage 0x91, 0x01, // Output (array) - // END_COLLECTION(0), END_COLLECTION(0) + }; #else /* defined(ENABLE_JOYSTICK) */
--- a/USBJoystick/USBJoystick.h Wed Sep 23 05:38:27 2015 +0000 +++ b/USBJoystick/USBJoystick.h Fri Sep 25 18:49:53 2015 +0000 @@ -91,7 +91,7 @@ * @param product_release Your product_release (default: 0x0001) */ USBJoystick(uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0100, uint16_t product_release = 0x0001, int waitForConnect = true): - USBHID(16, 8, vendor_id, product_id, product_release, false) + USBHID(16, 64, vendor_id, product_id, product_release, false) { _init(); connect(waitForConnect);
--- a/config.h Wed Sep 23 05:38:27 2015 +0000 +++ b/config.h Fri Sep 25 18:49:53 2015 +0000 @@ -74,13 +74,15 @@ // // The reason we start at unit #8 is that we want to avoid conflicting with // any real LedWiz devices you have in your system. If you have a real -// LedWiz, it's probably unit #1, since that's the standard factor setting. -// If you have two real LedWiz's, they're probably units #1 and #2. If you -// have three... well, I don't think anyone actually has three, but if you -// did it would probably be unit #3. And so on. That's why we start at #8 - -// it seems really unlikely that this will conflict with anybody's existing -// setup. On the off chance it does, simply change the setting here to a -// different unit number that's not already used in your system. +// LedWiz, it's probably unit #1, since that's the default factory setting +// that they'll give you if you didn't specifically ask for something else +// when you ordered it. If you have two real LedWiz's, they're probably +// units #1 and #2. If you have three... well, I don't think anyone +// actually has three, but if you did it would probably be unit #3. And so +// on. That's why we start at #8: it seems really unlikely that anyone +// with a pin cab has a real LedWiz unit #8. On the off chance that you +// do, simply change the setting here to a different unit number that's not +// already used in your system. // // Note 1: the unit number here is the *user visible* unit number that // you use on the PC side. It's the number you specify in your DOF @@ -90,67 +92,22 @@ // are all off by one from the unit number you select here, that's why. // // Note 2: the DOF Configtool (google it) knows about the Pinscape -// controller (it's known there as just a "KL25Z" rather than Pinscape). -// And the DOF tool knows that it uses #8 as its default unit number, so -// it names the .ini file for this controller xxx8.ini. If you change the -// unit number here, remember to rename the DOF-generated .ini file to -// match, by changing the "8" at the end of the filename to the new number -// you set here. +// controller. There it's referred to as simply "KL25Z" rather than +// Pinscape Controller, but that's what they're talking about. The DOF +// tool knows that it uses #8 as its default unit number, so it names the +// .ini file for this controller xxx8.ini. If you change the unit number +// here, remember to rename the DOF-generated .ini file to match, by +// changing the "8" at the end of the filename to the new number you set +// here. const uint8_t DEFAULT_LEDWIZ_UNIT_NUMBER = #ifdef ENABLE_JOYSTICK - 0x08; // joystick enabled - assume we're the primary KL25Z, so use unit #8 + 0x01; // joystick enabled - assume we're the primary KL25Z, so use unit #8 #else 0x09; // joystick disabled - assume we're a secondary, output-only KL25Z, so use #9 #endif // -------------------------------------------------------------------------- // -// TLC5940 PWM controller chip setup - Enhanced LedWiz emulation -// -// By default, the Pinscape Controller software can provide limited LedWiz -// emulation through the KL25Z's on-board GPIO ports. This lets you hook -// up external devices, such as LED flashers or solenoids, to the KL25Z -// outputs (using external circuitry to boost power - KL25Z GPIO ports -// are limited to a meager 4mA per port). This capability is limited by -// the number of available GPIO ports on the KL25Z, and even smaller limit -// of 10 PWM-capable GPIO ports. -// -// As an alternative, the controller software lets you use external PWM -// controller chips to control essentially unlimited channels with full -// PWM control on all channels. This requires building external circuitry -// using TLC5940 chips. Each TLC5940 chip provides 16 full PWM channels, -// and you can daisy-chain multiple TLC5940 chips together to set up 32, -// 48, 64, or more channels. -// -// If you do add TLC5940 circuits to your controller hardware, use this -// section to configure the connection to the KL25Z. -// -// Note that if you're using TLC5940 outputs, ALL of the outputs must go -// through the TLC5940s - you can't mix TLC5940s and the default GPIO -// device outputs. This lets us take GPIO ports that we'd normally use -// for device outputs and reassign them to control the TLC5940 hardware. - -// Uncomment this line if using TLC5940 chips -//#define ENABLE_TLC5940 - -// Number of TLC5940 chips you're using. For a full LedWiz-compatible -// setup, you need two of these chips, for 32 outputs. -#define TLC5940_NCHIPS 2 - -// If you're using TLC5940s, change any of these as needed to match the -// GPIO pins that you connected to the TLC5940 control pins. Note that -// SIN and SCLK *must* be connected to the KL25Z SPI0 MOSI and SCLK -// outputs, respectively, which effectively limits them to the default -// selections, and that the GSCLK pin must be PWM-capable. -#define TLC5940_SIN PTC6 // Must connect to SPI0 MOSI -> PTC6 or PTD2 -#define TLC5940_SCLK PTC5 // Must connect to SPI0 SCLK -> PTC5 or PTD1; however, PTD1 isn't - // recommended because it's hard-wired to the on-board blue LED -#define TLC5940_XLAT PTC10 // Any GPIO pin can be used -#define TLC5940_BLANK PTC0 // Any GPIO pin can be used -#define TLC5940_GSCLK PTD4 // Must be a PWM-capable pin - -// -------------------------------------------------------------------------- -// // Plunger CCD sensor. // // If you're NOT using the CCD sensor, comment out the next line (by adding @@ -344,10 +301,59 @@ // push mode. const float LaunchBallPushDistance = .08; -#endif // CONFIG_H + +// -------------------------------------------------------------------------- +// +// TLC5940 PWM controller chip setup - Enhanced LedWiz emulation +// +// By default, the Pinscape Controller software can provide limited LedWiz +// emulation through the KL25Z's on-board GPIO ports. This lets you hook +// up external devices, such as LED flashers or solenoids, to the KL25Z +// outputs (using external circuitry to boost power - KL25Z GPIO ports +// are limited to a meager 4mA per port). This capability is limited by +// the number of available GPIO ports on the KL25Z, and even smaller limit +// of 10 PWM-capable GPIO ports. +// +// As an alternative, the controller software lets you use external PWM +// controller chips to control essentially unlimited channels with full +// PWM control on all channels. This requires building external circuitry +// using TLC5940 chips. Each TLC5940 chip provides 16 full PWM channels, +// and you can daisy-chain multiple TLC5940 chips together to set up 32, +// 48, 64, or more channels. +// +// If you do add TLC5940 circuits to your controller hardware, use this +// section to configure the connection to the KL25Z. +// +// Note that if you're using TLC5940 outputs, ALL of the outputs must go +// through the TLC5940s - you can't mix TLC5940s and the default GPIO +// device outputs. This lets us take GPIO ports that we'd normally use +// for device outputs and reassign them to control the TLC5940 hardware. + +// Uncomment this line if using TLC5940 chips +#define ENABLE_TLC5940 + +// Number of TLC5940 chips you're using. For a full LedWiz-compatible +// setup, you need two of these chips, for 32 outputs. +#define TLC5940_NCHIPS 4 + +// If you're using TLC5940s, change any of these as needed to match the +// GPIO pins that you connected to the TLC5940 control pins. Note that +// SIN and SCLK *must* be connected to the KL25Z SPI0 MOSI and SCLK +// outputs, respectively, which effectively limits them to the default +// selections, and that the GSCLK pin must be PWM-capable. +#define TLC5940_SIN PTC6 // Must connect to SPI0 MOSI -> PTC6 or PTD2 +#define TLC5940_SCLK PTC5 // Must connect to SPI0 SCLK -> PTC5 or PTD1; however, PTD1 isn't + // recommended because it's hard-wired to the on-board blue LED +#define TLC5940_XLAT PTC10 // Any GPIO pin can be used +#define TLC5940_BLANK PTC0 // Any GPIO pin can be used +#define TLC5940_GSCLK PTD4 // Must be a PWM-capable pin -#ifdef DECL_EXTERNS +#endif // CONFIG_H - end of include-once section (code below this point can be multiply included) + + +#ifdef DECL_EXTERNS // this section defines global variables, only if this macro is set + // -------------------------------------------------------------------------- // @@ -421,7 +427,7 @@ // -------------------------------------------------------------------------- // -// LED-Wiz emulation output pin assignments. +// LED-Wiz emulation output pin assignments - GPIO mode // // NOTE! This section isn't used if you have TLC5940 outputs - ALL // device outputs will be through the 5940s if you're using them.
--- a/main.cpp Wed Sep 23 05:38:27 2015 +0000 +++ b/main.cpp Fri Sep 25 18:49:53 2015 +0000 @@ -337,7 +337,7 @@ // --------------------------------------------------------------------------- // -// LedWiz emulation +// LedWiz emulation, and enhanced TLC5940 output controller // // There are two modes for this feature. The default mode uses the on-board // GPIO ports to implement device outputs - each LedWiz software port is @@ -357,6 +357,17 @@ // for 32 outputs). Every port in this mode has full PWM support. // +// Figure the number of outputs. If we're in the default LedWiz mode, +// we have a fixed set of 32 outputs. If we're in TLC5940 enhanced mode, +// we have 16 outputs per chip. To simplify the LedWiz compatibility code, +// always use a minimum of 32 outputs even if we have fewer than two of the +// TLC5940 chips. +#if !defined(ENABLE_TLC5940) || (TLC_NCHIPS) < 2 +# define NUM_OUTPUTS 32 +#else +# define NUM_OUTPUTS ((TLC5940_NCHIPS)*16) +#endif + // Current starting output index for "PBA" messages from the PC (using // the LedWiz USB protocol). Each PBA message implicitly uses the // current index as the starting point for the ports referenced in @@ -393,7 +404,7 @@ virtual void set(float val) { if (val != prv) - tlc5940.set(idx, (int)(val * 4095)); + tlc5940.set(idx, (int)(val * 4095)); } int idx; float prv; @@ -454,12 +465,14 @@ virtual void set(float val) { } }; -// Array of output assignments. This array is indexed by the LedWiz -// output port number; that protocol is hardwired for 32 ports, so we -// need 32 elements in the array. Each element is an LwOut object -// that provides the mapping to the physical output corresponding to -// the software port. -static LwOut *lwPin[32]; +// Array of output physical pin assignments. This array is indexed +// by LedWiz logical port number - lwPin[n] is the maping for LedWiz +// port n (0-based). If we're using GPIO ports to implement outputs, +// we initialize the array at start-up to map each logical port to the +// physical GPIO pin for the port specified in the ledWizPortMap[] +// array in config.h. If we're using TLC5940 chips for the outputs, +// we map each logical port to the corresponding TLC5940 output. +static LwOut *lwPin[NUM_OUTPUTS]; // initialize the output pin array void initLwOut() @@ -470,14 +483,16 @@ // Set up a TLC5940 output. If the output is within range of // the connected number of chips (16 outputs per chip), assign it // to the current index, otherwise leave it unattached. - if (i < TLC5940_NCHIPS*16) + if (i < (TLC5940_NCHIPS)*16) lwPin[i] = new Lw5940Out(i); else lwPin[i] = new LwUnusedOut(); #else // ENABLE_TLC5940 - // Set up the GPIO pin, according to whether it's PWM-capable or - // digital-only, and whether or not it's assigned at all. + // Set up the GPIO pin. If the pin is not connected ("NC" in the + // pin map), set up a dummy "unused" output for it. If it's a + // real pin, set up a PWM-capable or Digital-Only output handler + // object, according to the pin type in the map. PinName p = (i < countof(ledWizPortMap) ? ledWizPortMap[i].pin : NC); if (p == NC) lwPin[i] = new LwUnusedOut(); @@ -491,10 +506,44 @@ } } +// Current absolute brightness level for an output. This is a float +// value from 0.0 for fully off to 1.0 for fully on. This is the final +// derived value for the port. For outputs set by LedWiz messages, +// this is derived from te LedWiz state, and is updated on each pulse +// timer interrupt for lights in flashing states. For outputs set by +// extended protocol messages, this is simply the brightness last set. +static float outLevel[NUM_OUTPUTS]; + +// LedWiz output states. +// +// The LedWiz protocol has two separate control axes for each output. +// One axis is its on/off state; the other is its "profile" state, which +// is either a fixed brightness or a blinking pattern for the light. +// The two axes are independent. +// +// Note that the LedWiz protocol can only address 32 outputs, so the +// wizOn and wizVal arrays have fixed sizes of 32 elements no matter +// how many physical outputs we're using. + // on/off state for each LedWiz output static uint8_t wizOn[32]; -// profile (brightness/blink) state for each LedWiz output +// Profile (brightness/blink) state for each LedWiz output. If the +// output was last updated through an LedWiz protocol message, it +// will have one of these values: +// +// 0-48 = fixed brightness 0% to 100% +// 129 = ramp up / ramp down +// 130 = flash on / off +// 131 = on / ramp down +// 132 = ramp up / on +// +// Special value 255: If the output was updated through the +// extended protocol, we'll set the wizVal entry to 255, which has +// no meaning in the LedWiz protocol. This tells us that the value +// in outLevel[] was set directly from the extended protocol, so it +// shouldn't be derived from wizVal[]. +// static uint8_t wizVal[32] = { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, @@ -502,87 +551,156 @@ 48, 48, 48, 48, 48, 48, 48, 48 }; +// LedWiz flash speed. This is a value from 1 to 7 giving the pulse +// rate for lights in blinking states. +static uint8_t wizSpeed = 2; + +// Current LedWiz flash cycle counter. +static uint8_t wizFlashCounter = 0; + +// Get the current brightness level for an LedWiz output. static float wizState(int idx) { - if (wizOn[idx]) + // if the output was last set with an extended protocol message, + // use the value set there, ignoring the output's LedWiz state + if (wizVal[idx] == 255) + return outLevel[idx]; + + // if it's off, show at zero intensity + if (!wizOn[idx]) + return 0; + + // check the state + uint8_t val = wizVal[idx]; + if (val <= 48) + { + // PWM brightness/intensity level. Rescale from the LedWiz + // 0..48 integer range to our internal PwmOut 0..1 float range. + // Note that on the actual LedWiz, level 48 is actually about + // 98% on - contrary to the LedWiz documentation, level 49 is + // the true 100% level. (In the documentation, level 49 is + // simply not a valid setting.) Even so, we treat level 48 as + // 100% on to match the documentation. This won't be perfectly + // ocmpatible with the actual LedWiz, but it makes for such a + // small difference in brightness (if the output device is an + // LED, say) that no one should notice. It seems better to + // err in this direction, because while the difference in + // brightness when attached to an LED won't be noticeable, the + // difference in duty cycle when attached to something like a + // contactor *can* be noticeable - anything less than 100% + // can cause a contactor or relay to chatter. There's almost + // never a situation where you'd want values other than 0% and + // 100% for a contactor or relay, so treating level 48 as 100% + // makes us work properly with software that's expecting the + // documented LedWiz behavior and therefore uses level 48 to + // turn a contactor or relay fully on. + return val/48.0; + } + else if (val == 49) { - // on - map profile brightness state to PWM level - uint8_t val = wizVal[idx]; - if (val <= 48) - { - // PWM brightness/intensity level. Rescale from the LedWiz - // 0..48 integer range to our internal PwmOut 0..1 float range. - // Note that on the actual LedWiz, level 48 is actually about - // 98% on - contrary to the LedWiz documentation, level 49 is - // the true 100% level. (In the documentation, level 49 is - // simply not a valid setting.) Even so, we treat level 48 as - // 100% on to match the documentation. This won't be perfectly - // ocmpatible with the actual LedWiz, but it makes for such a - // small difference in brightness (if the output device is an - // LED, say) that no one should notice. It seems better to - // err in this direction, because while the difference in - // brightness when attached to an LED won't be noticeable, the - // difference in duty cycle when attached to something like a - // contactor *can* be noticeable - anything less than 100% - // can cause a contactor or relay to chatter. There's almost - // never a situation where you'd want values other than 0% and - // 100% for a contactor or relay, so treating level 48 as 100% - // makes us work properly with software that's expecting the - // documented LedWiz behavior and therefore uses level 48 to - // turn a contactor or relay fully on. - return val/48.0; - } - else if (val == 49) - { - // 49 is undefined in the LedWiz documentation, but actually - // means 100% on. The documentation says that levels 1-48 are - // the full PWM range, but empirically it appears that the real - // range implemented in the firmware is 1-49. Some software on - // the PC side (notably DOF) is aware of this and uses level 49 - // to mean "100% on". To ensure compatibility with existing - // PC-side software, we need to recognize level 49. - return 1.0; - } - else if (val >= 129 && val <= 132) - { - // Values of 129-132 select different flashing modes. We don't - // support any of these. Instead, simply treat them as fully on. - // Note that DOF doesn't ever use modes 129-132, as it implements - // all flashing modes itself on the host side, so this limitation - // won't have any effect on DOF users. You can observe it using - // LedBlinky, though. - return 1.0; - } + // 49 is undefined in the LedWiz documentation, but actually + // means 100% on. The documentation says that levels 1-48 are + // the full PWM range, but empirically it appears that the real + // range implemented in the firmware is 1-49. Some software on + // the PC side (notably DOF) is aware of this and uses level 49 + // to mean "100% on". To ensure compatibility with existing + // PC-side software, we need to recognize level 49. + return 1.0; + } + else if (val == 129) + { + // 129 = ramp up / ramp down + if (wizFlashCounter < 128) + return wizFlashCounter/127.0; else - { - // Other values are undefined in the LedWiz documentation. Hosts - // *should* never send undefined values, since whatever behavior an - // LedWiz unit exhibits in response is accidental and could change - // in a future version. We'll treat all undefined values as equivalent - // to 48 (fully on). - // - // NB: the 49 and 129-132 cases are broken out above for the sake - // of documentation. We end up using 1.0 as the return value for - // everything outside of the defined 0-48 range, so we could collapse - // this whole thing to a single 'else' branch, but I wanted to call - // out the specific reasons for handling the settings above as we do. - return 1.0; - } + return (255 - wizFlashCounter)/127.0; + } + else if (val == 130) + { + // 130 = flash on / off + return (wizFlashCounter < 128 ? 1.0 : 0.0); + } + else if (val == 131) + { + // 131 = on / ramp down + return (255 - wizFlashCounter)/255.0; } - else + else if (val == 132) + { + // 132 = ramp up / on + return wizFlashCounter/255.0; + } + else { - // off - show at 0 intensity - return 0.0; + // Other values are undefined in the LedWiz documentation. Hosts + // *should* never send undefined values, since whatever behavior an + // LedWiz unit exhibits in response is accidental and could change + // in a future version. We'll treat all undefined values as equivalent + // to 48 (fully on). + return 1.0; } } +// LedWiz flash timer pulse. This fires periodically to update +// LedWiz flashing outputs. At the slowest pulse speed set via +// the SBA command, each waveform cycle has 256 steps, so we +// choose the pulse time base so that the slowest cycle completes +// in 2 seconds. This seems to roughly match the real LedWiz +// behavior. We run the pulse timer at the same rate regardless +// of the pulse speed; at higher pulse speeds, we simply use +// larger steps through the cycle on each interrupt. Running +// every 1/127 of a second = 8ms seems to be a pretty light load. +Timeout wizPulseTimer; +#define WIZ_PULSE_TIME_BASE (1.0/127.0) +static void wizPulse() +{ + // increase the counter by the speed increment, and wrap at 256 + wizFlashCounter += wizSpeed; + wizFlashCounter &= 0xff; + + // if we have any flashing lights, update them + int ena = false; + for (int i = 0 ; i < 32 ; ++i) + { + if (wizOn[i]) + { + uint8_t s = wizVal[i]; + if (s >= 129 && s <= 132) + { + lwPin[i]->set(wizState(i)); + ena = true; + } + } + } + + // Set up the next timer pulse only if we found anything flashing. + // To minimize overhead from this feature, we only enable the interrupt + // when we need it. This eliminates any performance penalty to other + // features when the host software doesn't care about the flashing + // modes. For example, DOF never uses these modes, so there's no + // need for them when running Visual Pinball. + if (ena) + wizPulseTimer.attach(wizPulse, WIZ_PULSE_TIME_BASE); +} + +// Update the physical outputs connected to the LedWiz ports. This is +// called after any update from an LedWiz protocol message. static void updateWizOuts() { + // update each output + int pulse = false; for (int i = 0 ; i < 32 ; ++i) + { + pulse |= (wizVal[i] >= 129 && wizVal[i] <= 132); lwPin[i]->set(wizState(i)); + } + + // if any outputs are set to flashing mode, and the pulse timer + // isn't running, turn it on + if (pulse) + wizPulseTimer.attach(wizPulse, WIZ_PULSE_TIME_BASE); } - // --------------------------------------------------------------------------- // // Button input @@ -907,7 +1025,7 @@ printf("%f %f %d %d %f\r\n", vx, vy, x, y, dt); #endif } - + private: // adjust a raw acceleration figure to a usb report value int rawToReport(float v) @@ -1243,6 +1361,11 @@ bool reportPix = false; #endif +#ifdef ENABLE_TLC5940 + // start the TLC5940 clock + tlc5940.start(); +#endif + // create our plunger sensor object PlungerSensor plungerSensor; @@ -1368,7 +1491,7 @@ if (data[0] == 64) { // LWZ-SBA - first four bytes are bit-packed on/off flags - // for the outputs; 5th byte is the pulse speed (0-7) + // for the outputs; 5th byte is the pulse speed (1-7) //printf("LWZ-SBA %02x %02x %02x %02x ; %02x\r\n", // data[1], data[2], data[3], data[4], data[5]); @@ -1381,6 +1504,13 @@ } wizOn[i] = ((data[ri] & bit) != 0); } + + // set the flash speed - enforce the value range 1-7 + wizSpeed = data[5]; + if (wizSpeed < 1) + wizSpeed = 1; + else if (wizSpeed > 7) + wizSpeed = 7; // update the physical outputs updateWizOuts();
--- a/mbed.bld Wed Sep 23 05:38:27 2015 +0000 +++ b/mbed.bld Fri Sep 25 18:49:53 2015 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/6213f644d804 \ No newline at end of file +http://mbed.org/users/mbed_official/code/mbed/builds/4f6c30876dfa \ No newline at end of file