Saturday, August 2, 2014

Button debounce, analogue VS digital

On the MIDI2VC, there is an unused button (named DEMO), which I want to use for an extra feature. For this new feature I need to debounce the button, one button press shall change state once, and only once. My first approach was to do this in software. After some fiddling I ended up with this code:
 //PIC16F1823 Button debounce using Interrupt On Change and timer overflow interrupts  
 /*@ignore@*/  
 #include <xc.h>  
 /*@end@*/  
 #include "header.h"  
 //Configuration bits  
 #pragma config WDTE=OFF, PWRTE = OFF, MCLRE=ON, BOREN=OFF, FCMEN=OFF, CLKOUTEN = OFF, IESO=OFF, FOSC=INTOSC, CPD=OFF, LVP = OFF, BORV = 0, PLLEN = OFF  
 #pragma switch speed  
 #define _XTAL_FREQ 32000000  
 void main() {  
   //TRISA  
   //TRISC  
   //RX(RC5), SCL(RC0) and SDA(RC1) as inputs  
   TRISA = 0b00010000;  
   TRISC = 0b00100011;  
      //Weak pull ups enabled on RA4  
   WPUA = 0b00010000;  
   //Set all I/O's to digital  
   ANSELA = 0x00;  
   ANSELC = 0x00;  
   PORTA = 0b00110000;  
   PORTC = 0x00; //Clear RC2 to unlight LED  
      //Enable Interrupt On Change on neagative slope on RA4  
   IOCAN = 0b00010000;  
      //No interrupt on positive slope  
   IOCAP = 0x00;  
      //Reset interrupt flag  
   IOCAF = 0x00;  
   //0 Internal oscillator, 3 <fosc> on, 6-4 31kHz  
   // OSCCON = 0b00000000;  
   OSCCON = 0b11110000;  
   //Interrupt controller  
   //5 Timer0 overflow interrupt enabled  
      //6 Peripheral interrupt enabled  
   //7 Global interrupt enabled  
      //3 Interrupt on change enabled  
   INTCON = 0b11101000;  
   //Timer0  
   TMR0 = 0x00;  
   //Option Register  
   //2-0 Prescaler 1:4  
   //3 prescaler assigned to Timer0  
   //4 timer edge, high to low  
   OPTION_REG = 0b01000101;  
   //Loop forever, interrupts when Timer0 overflows  
   while (1) {  
   }  
 }  
 //Interrupt routine  
 interrupt void isr(void) {  
   static char overFlow = 0;  
   if (TMR0IF == 1)  
   {  
     TMR0IF = 0;  
     overFlow++;  
     if (overFlow == 0)  
     {  
                //Reenable Interrupt On Change  
       IOCAN = 0b00010000;  
       INTCON = 0b11101000;  
     }  
   }  
   else if (IOCAF4 == 1)  
   {  
           //Disable Interrupt On Change  
     INTCON = 0b11100000;  
           //Toggle LED  
     LATC ^= 1 << 2;  
           //Reset Interrupt On Change flag  
     IOCAF4 = 0;  
   }  
   return;  
 }  

This is a very well controlled way to specify at which rate the button can be pressed without missing push-events, but as you can see, it adds quite a lot of code to the interrupt service routine. Since I have optimized my ISR quite a bit, I want to avoid adding code to it. I am also running out of data space in general, so that is also a reason not to add more code.
 So, I tried out debouncing the analog way, by adding a low pass filter between the button and the IO pin.
Analogue Switch Debouncer
The pull-up resistor in the MCU is of unknown value, but the data sheet calls it weak, so it's probably over 10kΩ.
 The analogue solution worked fine, but I need to do some strapping for it to work. I'll make a new version of the board that incorporates this.
Analogue Debounce strap