Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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
