Hey everyone. I’m about to start a new software project which requires a human UI with switches.
This particular UI will require two buttons. Each button must capture a sense of “acceleration”, in that the longer the user presses the button, different stuff happens.
The MCU I was looking at, the JS16, only has one MTIM. I’ve already allocated all the I/O pins, so I can’t dedicate any to input capture or output compare (the latter feature, I was under the impression could only be implemented by tying to an I?O pin).
My thought was to run a state machine for each switch. When key #1 is pressed, it looks up the MTIM free running clock value as a reference point. Then keep track of the TOFs to see just how long it was pressed.
Does that sound reasonable? I’d rather to take advantage of the output compare, but can that feature be used internally instead of with a pin?
已解决! 转到解答。
Hello irob,
Another thought is that there is probably little point in executing each state machine any more frequently than every 10-20 milliseconds. On this basis, the decrement of the timing counters can be done within these functions, rather than within the overflow ISR.
The ISR would need only to set a flag that would be visible within the main loop or the state machine functions, to determine that the state machine code be executed, rather than skipped. Static local variables could now be used for the timing counters.
if (OF_flag) {
OF_flag = 0;
FSM_button1();
FSM_button2();
}
Regards,
Mac
Hello irob,
For detection of manually actuated switch closures, where closure periods are likely to extend to "hundreds of milliseconds", and generally timing periods are not particularly critical, I would typically use a polling approach with respect to the closure timing. This is usually consistent with the testing of other inputs required by a state machine. Here, I would simply use an overflow interrupt associated with a TPM module, maybe 10-20 milliseconds period, and base my timing intervals on multiples of this amount. Then, for each independent timing function required, to provide a suitable variable which would decrement within the overflow ISR, provided it is a non-zero value.
if (var1) var1--;
if (var2) var2--;
The state machine can then test for a zero value to determine the next state required for a timeout condition. With the debounce timing built into the state machine itself, it is possible to utilize the same variable, as for the other timing intervals required. In your case, you might possibly need only two variables, one for each pushbutton.
Following debounce timing, you would set the variable to correspond with the first timing threshold, and then subsequently test whether release occurred prior to timeout. If no release, then set the variable for the next timing threshold (minus the first threshold value), and so on.
Regards,
Mac
Great ideas, guys. Thanks for the input. Bigmac, all of my human UI projects with switches have been state machine driven, so I'm more inclined to steer toward your suggestions. Rocco, your idea sounds fairly sophisticated, but I wouldn't mind seeing your example code for reference.
Thanks!
Hello irob,
Another thought is that there is probably little point in executing each state machine any more frequently than every 10-20 milliseconds. On this basis, the decrement of the timing counters can be done within these functions, rather than within the overflow ISR.
The ISR would need only to set a flag that would be visible within the main loop or the state machine functions, to determine that the state machine code be executed, rather than skipped. Static local variables could now be used for the timing counters.
if (OF_flag) {
OF_flag = 0;
FSM_button1();
FSM_button2();
}
Regards,
Mac
Here is the scheduler. It's small, but does need one byte of ram to support eight tasks.
Each task is just a subroutine. You put the subroutine's name in the task list, highest priority task first. Everything is built at compile-time by a couple of macros, so priorities are static.
You can request a task to be run with the line:
QUE SubroutineName
Where "SubroutineName" is the same subroutine that was put in the task list with:
TASK SubroutineName
Each "TASK" line will use two byte of flash for a pointer, and assign one bit of ram for a semaphore. Each "QUE" line will generate one "BSET" instruction to set the semaphore.
Usually, an ISR or another task would QUE the task to run in order for it to do subsequent processing. Some examples might be the serial-port ISR requesting a command-line parser to run when a <CR> has been received, or a timer-interrupt scheduling a keypad to be scanned. The keypad-scanning task might then QUE a key-processing task to run once a key-press is detected.
I'm sure I'm forgetting important information, so just ask . . .
Hi Rob,
I have something that might help a little.
I have a module that scans two ports for button press/release (sixteen buttons). It uses an interrupt to "que" the scan "task" subroutine at some interval. I typically use 200 microseconds per scan, but sometimes piggie-back the scheduling of the scan off of some other periodic interrupt, like the ADC, to save a timer.
The scan task has a debounce counter that you can set. It will not register a change until the IO-input has been stable for N sample periods. It then sticks the button "events" in a queue, for another "task" to extract and process.
The button events are:
KeyPressed: the key was pressed down
KeyClicked: the key was pressed and released quickly
KeyHeld: the key has been pressed long enough to be 'Held'
KeyRepeat: the key has been down for an additional 1/4 second
KeyHoldEnd: the key was 'Held', but is now released
KeyReleased: the key was let back up
For your application, the "KeyHeld" and "KeyRepeat" code might be able to be used as is, or modified to suit your needs.
I know this might be more than you need, but let me know if you want to try it. I use a little pico-scheduler for my "tasks", and can include that if you would like.
Hi Rob and Dan,
Yet again, I forgot to mention that the code is written in assembly language, and not C (which is why we are still using HC08/S08).
Let me know if you still want it, and if you do, whether you want the pico-scheduler, which is macro-based.