Chris, here are some code snippets relevant to the problem at hand...
class AnyController {private mTimerTransfer;private mTransferTimedOut = false;private void StopTimer(TimerID id){ switch (id) { case TimerID.TransferTimer: if (this.mTimerTransfer != null) { mTimerTransfer.Dispose(); mTimerTransfer = null; } break; : }}private void StartTimer(TimerID id, int msDelay, int msPeriod){ switch (id) { case TimerID.TransferTimer: mTimerTransfer = new Timer(new TimerCallback(ActionTimers_Tick), TimerID.TransferTimer, msDelay, msPeriod); mTransferTimedOut = false; break; : }}} // class
Then my Timer event handler on the above class looks like this. It handles several timers but they have the same mechanism so I show only one for brevity.
private void ActionTimers_Tick(object sender){ StopTimer((TimerID)sender); Signals timeoutSignal = Signals.NoOp; // an enumeration of all signals in the S.M. switch ((TimerID)sender) { case TimerID.TransferTimer: timeoutSignal = Signals.TimeoutTransfer; mTransferTimedOut = true; // used to discard the signal if necessary break; : } if (timeoutSignal != Signals.NoOp) { mSignalQueue.Enqueue(timeoutSignal); // of type Queue }}
Now the Program.cs class in my NETMF I have rigged it so that the TRANSFER button generates a GPIO interrupt on the leading (going low, pressed) and trailing (going high, released) edge. That works well. So there when the interrupt occurs on the leading edge I call the TransferPressed() method on the above class, and TransferReleased() when it is released. These methods are implemented like this in the above class:
class AnyController { : public void TransferPressed() { lock (this.mLock) { mSignalQueue.Enqueue(Signals.TransferPressed); } } public void TransferReleased() { lock (this.mLock) { StopTimer(TimerID.TransferTimer); // asap if (!this.mTransferTimedOut) mSignalQueue.Enqueue(Signals.TransferReleased); else this.mTransferTimedOut = false; // signal is discarded } }
And here is the processing of the main timer loop (not seen in the definitions) which executed every 200ms (even tried 75ms) and is in charge of processing the events and state transitions, a state machine... Here shown to the bare minimum for the purpose of clarity showing the mechanism I am using.
private void ControllerTimer_Tick(object sender){ Signals rxsignal = Signals.NoOp; if (mSignalQueue.Count > 0) rxsignal = (Signals)mSignalQueue.Dequeue(); switch (this.mState) { case States.SomeStateA: if (rxsignal == Signals.TransferPressed) { StartTimer(TimerID.TransferTimer, 0, 2000); // no delay, 2000ms period } else if (rxsignal == Signals.TransferReleased) { StopTimer(TimerID.TransferTimer); Swap(); // does action related to pressing less than 2 secs } else if (rxsignal == Signals.TimeoutReleased) { mState = States.OtherState; } break; } // switch state}
That may deserve some extra explanation. The Program NETMF class rigged the GPIO to cauase an interrupt on the leading (going low, pressed) and trailing (going high, released) edges of the GPIO and that is detected properly. When that detects a button pressed associated to that GPIO (The TRANSFER button) it invokes the TransferPressed() public method in the AnyController class. That in turn ALWAYS queues the TransferPressed signal into the signal queue.
Now, as I indicated in the OP, if the button is pressed less than 2 seconds it does the standard action, in this case Swap() and remains on the same state. If on the other hand the button is pressed more than 2 seconds the ActionTimers_Tick() event handler would STOP the one shot timer that times the button press AND raises the timeout by putting the TimeoutTransfer signal on the signal queue. It also flags that it has timed out on the mTimedoutTransfer.
So, why is that you may ask? well, when the button is pressed more than two seconds the TransferTimeout signal is on the queue, since the button was pressed for more than 2 seconds, the next time in the main processing loop (ControllerTimer_Tick()) it will initiate a state transition. That means that in that particular case the release of the Transfer button would happen AFTER the timeout, and I don't want that release event (related to the timeout) to be passed to the signal queue because it would cause the new state to pop back to the original state (not show) because it also processes the Transfer button. So, what I do in AnyController.TransferReleased() is that IF the mTimedoutTransfer boolean is set then the signal is discarded (note, there might be another press/release pair on the new state), if it is not set (not timed out) then the TransferRelased signal is put on the queue.
So, that is the scheme but the problem as I mentioned is that these timers seem to be working at a frequency of Terahertz or something like that because even if I set the timer to expire at a period of 2000 ms (2,000 ms == 2 secs) and have a time between press/release of MUCH LESS than 2 seconds (not even close, even less than a second) the timer times out first than the release and TransferTimeout signal is processed.