Main Site Documentation

Guaranteed button press


#1

Hi again, I am wondering what the best way to ensure that I receive all button presses that is performed by a user.

Several timers, some SD-card writes and other time-consuming tasks are preventing my buttons presses to be triggered.

Is it possible to prioritize and make a kind of keyboard-buffer for button presses?


#2

You should get them all if use interrupt port.


#3

Hmm, I am using the GHI button module, assuming it uses the interrupt port.

Consider the case where you press the button really quickly 10 times, and every click triggers the button-press event code where there is a write to a log-file on SD-card with sleeps and all that stuff. I dont see all presses.


#4

Try to move heavy stuff into a separate thread.


#5

Hmm, this is really fundamental stuff. Making it even more interesting.

As I understand it, there is a time-slice used by the dispatcher, and within that timeslice, the interrupt need to fire.

IF there is a blocking operation in progress, the time-slice is extended, and the interrupt might be overseen.

If that is correct, it is evidently very interesting to know why Valentin suggests moving heavy stuff to a seperate thread.

Im confused!?


#6

@ njbuch - the quality of the button is important. the buttons on the button modules are not the best. hitting them fast can be unreliable.


#7

I have a GHI’s button module, and it indeed works unreliably. Thought it’s only for me, but it seems not…


#8

I think they queue hardware interrupts somewhere internally, and sooner or later, they will all fire. Even if the MCU core is stopped, hardware interrupts are saved — at least that’s what I observe with my CAN bus. If the CAN traffic is high and I stop the core for some debugging, later I have to wait until thousands of RXOVER events are raised…


#9

Interesting. So why move the heavy stuff into a separate thread. I don’t care about speed at all - as long as I press five times and there are five events…


#10

@ njbuch - I would have an interrupt handler that all it did was record the button event in a Queue. Then in a separate thread process the events. This way you wont loose any events and it doesn’t matter that much how long it takes to process each event. Asynchronous events are best handled using interrupts and queues. If not you always stand a chance of missing one.


#11

Thanks everybody. I have coded a seperate thread for the events, and a seperate worker thread. The events just enqueue a number, and the worker thread just takes a task from the queue. Works better, but not 100%

It works best with the event thread having highest priority.

I have realized that holding the button down for longer time, not only waiting for the click, but really holding it, ensures the button press. Users wont do that.

My guess is that I am catching about 90% of button presses.

So I am left with the following questions:

  • What is a good quality button? And where to buy it?
  • How can I ensure that I dont miss a single press?

#12

@ njbuch - Are you using an interrupt handler or are you polling the button?


#13

Standard event driven stuff…totally Gadgeteer standard code.


#14

@ njbuch -
So you have something like:


Int_in = new InterruptPort(cpupins[(int)pin], false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);
            Int_in.OnInterrupt += new NativeEventHandler(Int_in_OnInterrupt);

........


static void Int_in_OnInterrupt(uint data1, uint data2, DateTime time)
        {

........
 }


#15

Nope, look at https://www.ghielectronics.com/docs/61/button-module


#16

I don’t know for sure but I suspect the button handler is running as a thread and not an interrupt and therefore your losing some of the events because of the time slicing. I use an interrupt on an IR input which has a burst of 40 k hz and I don’t miss any. If I get some time I will code up a interrupt on a button and see if its any different.


#17

Buttons are hard. To help you really need to read up on this:

http://www.ganssle.com/debouncing.htm

…and if you open up the driver for the GHI button, you will find the handler.

        public Button(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            socket.EnsureTypeIsSupported(new char[] { 'X', 'Y' }, this);

            // These calls will throw GT.Socket.InvalidSocketException if a pin conflict or error is encountered
            this.input = new GTI.InterruptInput(socket, GT.Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullUp, GTI.InterruptMode.RisingAndFallingEdge, this);
            this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(this._input_Interrupt);
            this.led = new GTI.DigitalOutput(socket, GT.Socket.Pin.Four, false, this);

            LEDMode = LEDModes.Off;
        }

        private void _input_Interrupt(GTI.InterruptInput input, bool value)
        {
            ButtonState buttonState = input.Read() ? ButtonState.Released : ButtonState.Pressed;
            switch (buttonState)
            {
                case ButtonState.Released:
                    if (LEDMode == LEDModes.OnWhilePressed)
                        TurnLEDOff();
                    else if (LEDMode == LEDModes.OnWhileReleased)
                        TurnLEDOn();
                    else if (LEDMode == LEDModes.ToggleWhenReleased)
                        ToggleLED();
                    break;
                case ButtonState.Pressed:
                    if (LEDMode == LEDModes.OnWhilePressed)
                        TurnLEDOn();
                    else if (LEDMode == LEDModes.OnWhileReleased)
                        TurnLEDOff();
                    else if (LEDMode == LEDModes.ToggleWhenPressed)
                        ToggleLED();
                    break;
            }
            this.OnButtonEvent(this, buttonState);
        }



#18

Actually “Buttons” are easy if the button has form C contacts tied to a latch where the NO is tied to the set input and NC is tied to the reset of the latch. This takes care of the bounce. Then you just tie it to an interrupt and your done.