Debouncing i2c attached (pushboutton) switch

Hello,

Can someone recommend an approach of debouncing a switch attached to an i2c i/o expender's input?

In my original scheme, a timer interrupt would, every ~5ms, interrupt the main program busy in some background work, and check that the (concerned) switch(s) had stabilized.

In this scheme,

  • an i2c interrupt notifies of a pushbutton press.
  • corresponding i2c input interrupt is masked.
  • that input is submitted to the timer interrupt ISR for debouncing (or added to debouncing queue if many concurrent such switches).
  • once debounced that input is interrupt enabled again.

The rational is to have all the communication tasks -which in this case include the debouncing- done autonomously by an "independent channel"
while the main program is occupied with processing tasks (as opposed to communications)

Unfortunately, it seems that within an ISR all calls to "wire" routines block the UNO.

All advices are most welcome

Thanks in advance for your help

Guy

If the wire code uses interrupts you are screwed because they are disabled in the ISR. If they do then set a flag and do the I2C polling outside the ISR, either way that's the preferred method as ISR's should not take much time.

Someone had a similar problem the other day and I gave a similar answer, so far nobody has said that wire doesn't use interrupts so I assume it does.

Anyway post your code.


Rob

guy_c:
Can someone recommend an approach of debouncing a switch attached to an i2c i/o expender's input?

Yep.

guy_c:
In my original scheme, a timer interrupt would, every ~5ms, interrupt the main program busy in some background work, and check that the (concerned) switch(s) had stabilized.

Fine so far.

guy_c:
In this scheme,

  • an i2c interrupt notifies of a pushbutton press.
  • corresponding i2c input interrupt is masked.
  • that input is submitted to the timer interrupt ISR for debouncing (or added to debouncing queue if many concurrent such switches).
  • once debounced that input is interrupt enabled again.
    ...
    Unfortunately, it seems that within an ISR all calls to "wire" routines block the UNO.

As you would expect if you thought about it. :slight_smile:

Nope, it doesn't work that way.

You badly misunderstand the purpose of interrupts.

Interrupts are there to quickly service important things, so you have it wrong on two counts; what you propose to do - using I2C is not quick, and pushbuttons are never, repeat never important! Apparently the I2C routines themselves use some sort of interrupts as well.

guy_c:
The rationale is to have all the communication tasks -which in this case include the debouncing- done autonomously by an "independent channel" while the main program is occupied with processing tasks (as opposed to communications)

Yes, all fine, as long as you realise this independent channel is not performed within an interrupt.

What you are looking for is a RTOS or task scheduler, but you do not (should not) need to go to all that trouble of using a formal "packaged" one (if you understand how it works).

You have to write the main program (which is a loop - always a loop) so that it alternates between two tasks. You may not "busy wait" - if either or any task has to wait at any point, it needs to invoke the other task or if you are aware that it will take an inordinate amount of processing time, it will need to regularly invoke the other task.

In practice, this is generally fairly easy because you would have broken up your task into modules called from a main sequence. Simply put the debounce code into that sequence. It is most certainly true that any marginal efficiency that you might achieve by having the expander generate an interrupt, will be offset by the complexity of having that interrupt set a flag (semaphore) which will then be checked in due course by the main loop.

You already have an efficiency insofar as the I2C routine polls the whole byte from the expander each time it is called on the 1 ms (or better, 5 ms) "tick" and you debounce the whole input byte as one by comparing it to the "previous" version of the input byte, so all of your switches are debounced as one.

Just a reminder - pushbuttons are not an important priority at MCU speed. Your debounce should be counting at least three or four, 5 ms "ticks".

Wire does use the interrupts. Here is what i intend to do

  1. My ISRs will start enabling interrupts
  2. Access to Wire libs will be exclusive
  3. Exclusiveness implemented with s/w test and set

It seems to me that this should ensure that access to Wire routines is always sequential

is there a subtle flow i'm missing?

Thanks for sharing you experience,

Guy

I don't know how to insert a rolling window of code so here are the essential pieces

...
static boolean i2cBusy=false;
...

boolean lockedI2c (){ //test-and-set
boolean reply;
cli();
reply=i2cBusy;
i2cBusy=true;
sei();
return reply;
}

ISR(TIMER0_COMPA_vect){ // example timer ISR but same for i2c chip generated ISRs
sei();
...
// need to access wire lib
while(lockedI2c());
// make calls to Wire ...
i2cBusy=false;
...
}