hardware debounce filter tutorial

Hi everyone

I just tested the hardware based debounce filter that is provided by the SAM3X8E. I found out it is quite easy to set up and I thought I would share my results with you. It is usually used to debounce mechanical shutters and things like that.

All you need to activate it, are the following 3 lines of code:

  // activate input filters
  REG_PIOx_IFER = yourPinMask;
  // choose debounce filter as input filter type
  REG_PIOx_DIFSR = yourPinMask;
  // set clock divider for slow clock -> rejects pulses shorter than (DIV+1)*31µs and accepts pulses longer than 2*(DIV+1)*31µs 
  REG_PIOx_SCDR = DIV;
}

replace "yourPinMask" by the bitmask of your input pin, the "x" in REG_PIOx_... by the corresponding PORT of your pin (A,B,C or D to be looked up in the due pinout diagramm http://arduino.cc/forum/index.php/topic,132130.0.html) and the slow clock divider DIV according to the pulse length you'd like to reject. DIV is a 14bit value, allowing for rejection times between 60µs and 500s.

For people who this code looks cryptic to, here an easy example how to use it:

// input pin to debounce, pin 24 is on PORT A
int pin1 = 24;  
// get bitmask for register manipulation
int mask_1 = digitalPinToBitMask(pin1); 

void setup() {
  pinMode(pin1, INPUT);
  
  // activate input filters for pin 24
  REG_PIOA_IFER = mask_1;
  // choose debounce filter as input filter for pin 24
  REG_PIOA_DIFSR = mask_1;
  // set clock divider for slow clock -> rejects pulses shorter than (DIV+1)*31µs and accepts pulses longer than 2*(DIV+1)*31µs 
  REG_PIOA_SCDR = 10;
}

void loop() {
 // do whatever you like to ;)
}

This code sets up a debounce filter for pin 24 (on PORT A) with a clock divider of DIV = 10, which means that pulses shorter than 341µs are rejected and pulses longer than 682µs are accepted (inbetween it might or might not be accepted, more or less randomly).

According to the SAM3X8E datasheet, the debounce filter influences the values read by REG_PIOx_PDSR (or digitalRead()) and the input CHANGE interrupt detection. Nothing else.. all peripheral functions (peripheral A or B), if used, are not affected.

Please mind that the activation of the debounce filter adds latencies to your digital input signals on this pin. The delays are on the order of the acceptance time set by DIV, since this is basically the time the SAM needs to check if your pulse exceeds the acceptance time.

I hope this might help someone someday :wink:

Thanks, that's really useful. I've had a go at coding it into a function using the g_APinDescription table:

void setDebounce(int pin, int usecs){  // reject spikes shorter than usecs on pin
  if(usecs){
    g_APinDescription[pin].pPort -> PIO_IFER = g_APinDescription[pin].ulPin;
    g_APinDescription[pin].pPort -> PIO_DIFSR |= g_APinDescription[pin].ulPin;
  }
  else {
    g_APinDescription[pin].pPort -> PIO_IFDR = g_APinDescription[pin].ulPin;
    g_APinDescription[pin].pPort -> PIO_DIFSR &=~ g_APinDescription[pin].ulPin;
    return;
  }


 int div=(usecs/31)-1; if(div<0)div=0; if(div > 16383) div=16383;
 g_APinDescription[pin].pPort -> PIO_SCDR = div;
  
}

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(13,OUTPUT);
  setDebounce(2,200000);
}

void loop() {
  digitalWrite(13,digitalRead(2));
}

Given that the slow clock defaults to an RC oscillator the timings won't be exact unless the crystal oscillator has been enabled.

I think you meant rejection time between 30us and 500ms :slight_smile:

Working with uS is pretty short, what is the longest time you can set?


Rob

Longest would be 500000 us. I imagine most people would either be debouncing switches (about 20000us) or filtering spikes on signals in a 'noisy' circuit (100us or less).

Thanks, that sounds nice. I had to write a custom debounce handler for the LPC, this is a lot simpler so I won't have to port that across; pity it's quite a clever algorithm :).


Rob

Thanks stimmer for correcting my little mistake with ms and s :blush: and for coding this nice function.. its very handy to use :wink:

As a comment to users of your function I would like to point out that the rejection time is set for the whole port, so if you want to use different rejection times, your pins have to be on different ports.

Considering the number of push-buttons and encoders that are unceremoniously tacked onto hardware, and the angst that non-debounced inputs can impart on a poor DUE, I humbly suggest that this thread get pinned.

Neat-tricks / feature utilization...

schwingkopf:
Thanks stimmer for correcting my little mistake with ms and s :blush: and for coding this nice function.. its very handy to use :wink:

As a comment to users of your function I would like to point out that the rejection time is set for the whole port, so if you want to use different rejection times, your pins have to be on different ports.

Thanks. I've modified the function so now if you use setDebounce(pin,0); to cancel the filter it doesn't affect the timing of the rest of the port.

Thanks. This works perfect, also on FALLING edge for the pin. Here is my working code:

#define Zero 25
pinMode(Zero, INPUT_PULLUP);
setDebounce(Zero, 10000); // Filters Input (pin, uSec)
PIOC->PIO_ISR; // Clear status register
NVIC_ClearPendingIRQ(PIOC_IRQn); // Clear pending ISR
attachInterrupt(Zero, ZeroJoystick, FALLING); // Calls at faling edge

void setDebounce(int pin, int usecs) { // reject spikes shorter than usecs on pin
if (usecs) {
g_APinDescription[pin].pPort -> PIO_IFER = g_APinDescription[pin].ulPin;
g_APinDescription[pin].pPort -> PIO_DIFSR |= g_APinDescription[pin].ulPin;
}
else {
g_APinDescription[pin].pPort -> PIO_IFDR = g_APinDescription[pin].ulPin;
g_APinDescription[pin].pPort -> PIO_DIFSR &= ~ g_APinDescription[pin].ulPin;
return;
}

int div = (usecs / 31) - 1; if (div < 0)div = 0; if (div > 16383) div = 16383;
g_APinDescription[pin].pPort -> PIO_SCDR = div;

}

Sorry for the format. First post here.