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 Mike R

Files at this revision

API Documentation at this revision

Comitter:
mjr
Date:
Fri Feb 27 07:41:29 2015 +0000
Parent:
17:ab3cec0c8bf4
Child:
19:054f8af32fce
Commit message:
Old debounce about to be removed

Changed in this revision

TSL1410R/tsl1410r.h Show annotated file Show diff for this revision Revisions of this file
ccdSensor.h Show annotated file Show diff for this revision Revisions of this file
config.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/TSL1410R/tsl1410r.h	Fri Feb 27 04:14:04 2015 +0000
+++ b/TSL1410R/tsl1410r.h	Fri Feb 27 07:41:29 2015 +0000
@@ -63,11 +63,7 @@
     // If the caller has other work to tend to that takes longer than the
     // desired maximum integration time, it can call clear() to clock out
     // the current pixels and start a fresh integration cycle.
-    void read(uint16_t *pix, int n) { read(pix, n, 0, 0, 0); }
-    
-    // Read with interval callback.  We'll call the callback the given
-    // number of times per read cycle.
-    void read(uint16_t *pix, int n, void (*cb)(void *ctx), void *cbctx, int cbcnt)
+    void read(uint16_t *pix, int n)
     {
         // start the next integration cycle by pulsing SI and one clock
         si = 1;
@@ -78,42 +74,24 @@
         // figure how many pixels to skip on each read
         int skip = nPix/n - 1;
         
-        // figure the callback interval
-        int cbInterval = nPix;
-        if (cb != 0)
-            cbInterval = nPix/(cbcnt+1);
-    
         // read all of the pixels
-        for (int src = 0, dst = 0 ; src < nPix ; )
+        for (int src = 0, dst = 0 ; src < nPix ; ++src)
         {
-            // figure the end of this callback interval
-            int srcEnd = src + cbInterval;
-            if (srcEnd > nPix)
-                srcEnd = nPix;
-                
-            // read one callback chunk of pixels
-            for ( ; src < srcEnd ; ++src)
+            // clock in and read the next pixel
+            clock = 1;
+            ao.enable();
+            wait_us(1);
+            clock = 0;
+            wait_us(11);
+            pix[dst++] = ao.read_u16();
+            ao.disable();
+            
+            // clock skipped pixels
+            for (int i = 0 ; i < skip ; ++i, ++src) 
             {
-                // clock in and read the next pixel
                 clock = 1;
-                ao.enable();
-                wait_us(1);
                 clock = 0;
-                wait_us(11);
-                pix[dst++] = ao.read_u16();
-                ao.disable();
-                
-                // clock skipped pixels
-                for (int i = 0 ; i < skip ; ++i, ++src) 
-                {
-                    clock = 1;
-                    clock = 0;
-                }
             }
-            
-            // call the callback, if we're not at the last pixel
-            if (cb != 0 && src < nPix)
-                (*cb)(cbctx);
         }
         
         // clock out one extra pixel to leave A1 in the high-Z state
--- a/ccdSensor.h	Fri Feb 27 04:14:04 2015 +0000
+++ b/ccdSensor.h	Fri Feb 27 07:41:29 2015 +0000
@@ -92,21 +92,21 @@
     bool highResScan(int &pos)
     {
         // read the array
-        ccd.read(pix, npix, ccdReadCB, 0, 3);
+        ccd.read(pix, npix);
 
-        // get the average brightness at each end of the sensor
-        long avg1 = (long(pix[0]) + long(pix[1]) + long(pix[2]) + long(pix[3]) + long(pix[4]))/5;
-        long avg2 = (long(pix[npix-1]) + long(pix[npix-2]) + long(pix[npix-3]) + long(pix[npix-4]) + long(pix[npix-5]))/5;
+        // get the brightness at each end of the sensor
+        long b1 = pix[0];
+        long b2 = pix[npix-1];
         
         // Work from the bright end to the dark end.  VP interprets the
         // Z axis value as the amount the plunger is pulled: zero is the
         // rest position, and the axis maximum is fully pulled.  So we 
         // essentially want to report how much of the sensor is lit,
         // since this increases as the plunger is pulled back.
-        int si = 1, di = 1;
-        long avgHi = avg1;
-        if (avg1 < avg2)
-            si = npix - 2, di = -1, avgHi = avg2;
+        int si = 0, di = 1;
+        long hi = b1;
+        if (b1 < b2)
+            si = npix - 1, di = -1, hi = b2;
 
         // Figure the shadow threshold.  In practice, the portion of the
         // sensor that's not in shadow has all pixels consistently near
@@ -122,17 +122,9 @@
         //             = lo*1/3 + hi*2/3
         //             = (lo + hi*2)/3
         //
-        // Then multiply the whole thing by 3 to factor out the averaging
-        // of each three adjacent pixels that we do in the loop (to save a
-        // little time on a mulitply on each loop):
-        //
-        //    threshold' = lo + 2*hi
-        //
-        // Now, 'lo' is always one of avg1 or avg2, and 'hi' is the other
-        // one, so we can rewrite this as hi + avg1 + avg2.  We also already
-        // pulled out 'hi' as avgHi, so we finally come to the final
-        // simplified expression:
-        long midpt = avg1 + avg2 + avgHi;
+        // Now, 'lo' is always one of b1 or b2, and 'hi' is the other
+        // one, so we can rewrite this as:
+        long midpt = (b1 + b2 + hi)/3;
         
         // If we have enough contrast, proceed with the scan.
         //
@@ -141,13 +133,13 @@
         // or the sensor is misaligned and is either fully in or out of shadow
         // (it's supposed to be mounted such that the edge of the shadow always
         // falls within the sensor, for any possible plunger position).
-        if (labs(avg1 - avg2) > 0x1000)
+        if (labs(b1 - b2) > 0x1000)
         {
             uint16_t *pixp = pix + si;           
-            for (int n = 1 ; n < npix - 1 ; ++n, pixp += di)
+            for (int n = 0 ; n < npix ; ++n, pixp += di)
             {
                 // if we've crossed the midpoint, report this position
-                if (long(pixp[-1]) + long(pixp[0]) + long(pixp[1]) < midpt)
+                if (long(*pixp) < midpt)
                 {
                     // note the new position
                     pos = n;
@@ -166,7 +158,10 @@
         // send reports for all pixels
         int idx = 0;
         while (idx < npix)
+        {
             js.updateExposure(idx, npix, pix);
+            wait_ms(1);
+        }
             
         // The pixel dump requires many USB reports, since each report
         // can only send a few pixel values.  An integration cycle has
--- a/config.h	Fri Feb 27 04:14:04 2015 +0000
+++ b/config.h	Fri Feb 27 07:41:29 2015 +0000
@@ -165,6 +165,26 @@
 const int ZBLaunchBallPort = 32;
 const int LaunchBallButton = 24;
 
+// Distance necessary to push the plunger to activate the simulated 
+// launch ball button, in inches.  A standard pinball plunger can be 
+// pushed forward about 1/2".  However, the barrel spring is very
+// stiff, and anything more than about 1/8" requires quite a bit
+// of force.  Ideally the force required should be about the same as 
+// for any ordinary pushbutton.
+//
+// On my cabinet, empirically, a distance around 2mm (.08") seems
+// to work pretty well.  It's far enough that it doesn't trigger
+// spuriously, but short enough that it responds to a reasonably
+// light push.
+//
+// You might need to adjust this up or down to get the right feel.
+// Alternatively, if you don't like the "push" gesture at all and
+// would prefer to only make the plunger respond to a pull-and-release
+// motion, simply set this to, say, 2.0 - it's impossible to push a 
+// plunger forward that far, so that will effectively turn off the 
+// push mode.
+const float LaunchBallPushDistance = .08;
+
 
 // --------------------------------------------------------------------------
 //
--- a/main.cpp	Fri Feb 27 04:14:04 2015 +0000
+++ b/main.cpp	Fri Feb 27 07:41:29 2015 +0000
@@ -153,9 +153,10 @@
 //    long yellow/green = everything's working, but the plunger hasn't
 //        been calibrated; follow the calibration procedure described above.
 //        This flash mode won't appear if the CCD has been disabled.  Note
-//        that the device can't tell whether a CCD is physically attached,
-//        so you should use the config command to disable the CCD software 
-//        features if you won't be attaching a CCD.
+//        that the device can't tell whether a CCD is physically attached;
+//        if you don't have a CCD attached, you can set the appropriate option 
+//        in config.h or use the  Windows config tool to disable the CCD 
+//        software features.
 //
 //    alternating blue/green = everything's working
 //
@@ -442,6 +443,19 @@
 // button input map array
 DigitalIn *buttonDigIn[32];
 
+// button state
+struct ButtonState
+{
+    // current on/off state
+    int pressed;
+    
+    // Sticky time remaining for current state.  When a
+    // state transition occurs, we set this to a debounce
+    // period.  Future state transitions will be ignored
+    // until the debounce time elapses.
+    int t;
+} buttonState[32];
+
 // timer for button reports
 static Timer buttonTimer;
 
@@ -462,24 +476,67 @@
 }
 
 
-// read the raw button input state
-uint32_t readButtonsRaw()
+// read the button input state
+uint32_t readButtons()
 {
     // start with all buttons off
     uint32_t buttons = 0;
     
+    // figure the time elapsed since the last scan
+    int dt = buttonTimer.read_ms();
+    
+    // reset the timef for the next scan
+    buttonTimer.reset();
+    
     // scan the button list
     uint32_t bit = 1;
-    for (int i = 0 ; i < countof(buttonDigIn) ; ++i, bit <<= 1)
+    DigitalIn **di = buttonDigIn;
+    ButtonState *bs = buttonState;
+    for (int i = 0 ; i < countof(buttonDigIn) ; ++i, ++di, ++bs, bit <<= 1)
     {
-        if (buttonDigIn[i] != 0 && !buttonDigIn[i]->read())
-            buttons |= bit;
+        // read this button
+        if (*di != 0)
+        {
+            // deduct the elapsed time since the last update
+            // from the button's remaining sticky time
+            bs->t -= dt;
+            if (bs->t < 0)
+                bs->t = 0;
+            
+            // If the sticky time has elapsed, note the new physical
+            // state of the button.  If we still have sticky time
+            // remaining, ignore the physical state; the last state
+            // change persists until the sticky time elapses so that
+            // we smooth out any "bounce" (electrical transients that
+            // occur when the switch contact is opened or closed).
+            if (bs->t == 0)
+            {
+                // get the new physical state
+                int pressed = !(*di)->read();
+                
+                // update the button's logical state if this is a change
+                if (pressed != bs->pressed)
+                {
+                    // store the new state
+                    bs->pressed = pressed;
+                    
+                    // start a new sticky period for debouncing this
+                    // state change
+                    bs->t = 1000;
+                }
+            }
+            
+            // if it's pressed, OR its bit into the state
+            if (bs->pressed)
+                buttons |= bit;
+        }
     }
     
-    // return the button list
+    // return the new button list
     return buttons;
 }
 
+#if 0
 // Read buttons with debouncing.  
 //
 // Debouncing is the process of filtering out transients from button
@@ -505,7 +562,7 @@
         uint32_t b;
         
         // Change mask at this report.  This is a bit mask of the buttons
-        // that *didn't* change on this report.  AND this mask with a
+        // that changed on this report.  AND the NOT of this mask with a
         // new reading to filter buttons out of the new reading that
         // changed on this report.
         uint32_t m;
@@ -524,19 +581,20 @@
     // start timing the next interval
     buttonTimer.reset();
     
-    // mask out changes for any buttons that changed state within the
-    // past 50ms
-    for (int i = 1 ; i < countof(readings) && ms < 50 ; ++i)
+    // Mask out changes for any buttons that changed state within the
+    // past 50ms.  This ensures that each state change sticks for at
+    // least 50ms, which should be long enough to be sure that a change
+    // that reverses a prior change isn't just a transient.
+    for (int i = 1, j = ri - 1 ; i < countof(readings) && ms < 50 ; ++i, --j)
     {
         // find the next prior reading, wrapping in the circular buffer
-        int j = ri - i;
         if (j < 0) 
             j = countof(readings) - 1;
         reading *rj = &readings[j];
 
         // For any button that changed state in the prior reading 'rj',
         // remove any new change and restore it to its 'rj' state.
-        b &= rj->m;
+        b &= ~rj->m;
         b |= rj->b;
                 
         // add in the time to the next prior report
@@ -547,7 +605,7 @@
     uint32_t m = b ^ bPrv;
     
     // save the change mask and changed button vector in our history entry
-    r->m = ~m;
+    r->m = m;
     r->b = b & m;
     
     // save this as the prior report
@@ -561,6 +619,7 @@
     // return the debounced result
     return b;
 }
+#endif
 
 // ---------------------------------------------------------------------------
 //
@@ -917,21 +976,6 @@
  
 // ---------------------------------------------------------------------------
 //
-// CCD read interval callback.  When reading the CCD, we'll call this
-// several times over the course of the read loop to refresh the button
-// states.  This allows us to debounce the buttons while the long CCD
-// read cycle is taking place, so that we can reliably report button
-// states after each CCD read cycle.  (The read cycle takes about 30ms,
-// which should be enough time to reliably debounce the buttons.)
-//
-void ccdReadCB(void *)
-{
-    // read the keyboard
-    readButtonsDebounced();
-}
-
-// ---------------------------------------------------------------------------
-//
 // Include the appropriate plunger sensor definition.  This will define a
 // class called PlungerSensor, with a standard interface that we use in
 // the main loop below.  This is *kind of* like a virtual class interface,
@@ -1185,6 +1229,11 @@
     Timer lbTimer;
     lbTimer.start();
     
+    // Launch Ball simulated push timer.  We start this when we simulate
+    // the button push, and turn off the simulated button when enough time
+    // has elapsed.
+    Timer lbBtnTimer;
+    
     // Simulated button states.  This is a vector of button states
     // for the simulated buttons.  We combine this with the physical
     // button states on each USB joystick report, so we will report
@@ -1245,12 +1294,11 @@
     // host requests
     for (;;)
     {
-        // Look for an incoming report.  Continue processing input as
-        // long as there's anything pending - this ensures that we
-        // handle input in as timely a fashion as possible by deferring
-        // output tasks as long as there's input to process.
+        // Look for an incoming report.  Process a few input reports in
+        // a row, but stop after a few so that a barrage of inputs won't
+        // starve our output event processing.
         HID_REPORT report;
-        while (js.readNB(&report))
+        for (int rr = 0 ; rr < 4 && js.readNB(&report) ; ++rr)
         {
             // all Led-Wiz reports are 8 bytes exactly
             if (report.length == 8)
@@ -1559,8 +1607,10 @@
             }
             
             // Check for a simulated Launch Ball button press, if enabled
-            if (ZBLaunchBallPort != 0 && wizOn[ZBLaunchBallPort-1])
+            if (ZBLaunchBallPort != 0)
             {
+                const int cockThreshold = JOYMAX/3;
+                const int pushThreshold = int(-JOYMAX/3 * LaunchBallPushDistance);
                 int newState = lbState;
                 switch (lbState)
                 {
@@ -1568,9 +1618,9 @@
                     // Base state.  If the plunger is pulled back by an inch
                     // or more, go to "cocked" state.  If the plunger is pushed
                     // forward by 1/4" or more, go to "launch" state.
-                    if (znew >= JOYMAX/3)
+                    if (znew >= cockThreshold)
                         newState = 1;
-                    else if (znew < -JOYMAX/12)
+                    else if (znew <= pushThreshold)
                         newState = 3;
                     break;
                     
@@ -1582,7 +1632,7 @@
                     // to trigger a launch.
                     if (firing || znew <= 0)
                         newState = 3;
-                    else if (znew < JOYMAX/3)
+                    else if (znew < cockThreshold)
                         newState = 2;
                     break;
                     
@@ -1590,8 +1640,11 @@
                     // Uncocked state.  If the plunger is more than an inch
                     // retracted, return to cocked state.  If we've been in
                     // the uncocked state for more than half a second, return
-                    // to the base state.
-                    if (znew >= JOYMAX/3)
+                    // to the base state.  This allows the user to return the
+                    // plunger to rest without triggering a launch, by moving
+                    // it at manual speed to the rest position rather than
+                    // releasing it.
+                    if (znew >= cockThreshold)
                         newState = 1;
                     else if (lbTimer.read_ms() > 500)
                         newState = 0;
@@ -1600,7 +1653,7 @@
                 case 3:
                     // Launch state.  If the plunger is no longer pushed
                     // forward, switch to launch rest state.
-                    if (znew > -JOYMAX/24)
+                    if (znew >= 0)
                         newState = 4;
                     break;    
                     
@@ -1609,7 +1662,7 @@
                     // again, switch back to launch state.  If not, and we've
                     // been in this state for at least 200ms, return to the
                     // default state.
-                    if (znew < -JOYMAX/12)
+                    if (znew <= pushThreshold)
                         newState = 3;
                     else if (lbTimer.read_ms() > 200)
                         newState = 0;                    
@@ -1617,11 +1670,18 @@
                 }
                 
                 // change states if desired
+                const uint32_t lbButtonBit = (1 << (LaunchBallButton - 1));
                 if (newState != lbState)
                 {
-                    // if we're entering Launch state, press the Launch Ball button
-                    if (newState == 3 && lbState != 4)
-                        simButtons |= (1 << (LaunchBallButton - 1));
+                    // if we're entering Launch state, and the ZB Launch Ball
+                    // LedWiz signal is turned on, simulate a Launch Ball button
+                    // press
+                    if (newState == 3 && lbState != 4 && wizOn[ZBLaunchBallPort-1])
+                    {
+                        lbBtnTimer.reset();
+                        lbBtnTimer.start();
+                        simButtons |= lbButtonBit;
+                    }
                         
                     // if we're switching to state 0, release the button
                     if (newState == 0)
@@ -1633,6 +1693,17 @@
                     // start timing in the new state
                     lbTimer.reset();
                 }
+
+                // if the simulated Launch Ball button press is in effect,
+                // and either it's been in effect too long or the ZB Launch
+                // Ball signal is no longer active, turn off the button
+                if ((simButtons & lbButtonBit) != 0
+                    && (!wizOn[ZBLaunchBallPort-1] || lbBtnTimer.read_ms() > 250))
+                {
+                    lbBtnTimer.stop();
+                    simButtons &= ~lbButtonBit;
+                }
+
             }
                 
             // If a firing event is in progress, generate synthetic reports to 
@@ -1719,7 +1790,7 @@
         }
 
         // update the buttons
-        uint32_t buttons = readButtonsDebounced();
+        uint32_t buttons = readButtons();
 
         // If it's been long enough since our last USB status report,
         // send the new report.  We throttle the report rate because