Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Revision:
18:5e890ebd0023
Parent:
17:ab3cec0c8bf4
Child:
19:054f8af32fce
--- 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