I've done it by checking the jiffies instead of the time. I think there's less overhead this way. However I'm still looking to improve the debouncing algorithm because I'm not fully satisfied with it: there are still bounces although they are less frequent than before.
#include <linux/jiffies.h>
extern unsigned long volatile jiffies;
static irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
static unsigned long old_jiffie = 0;
unsigned long diff = jiffies - old_jiffie;
if (diff < 20)
{
return IRQ_HANDLED;
}
printk(KERN_NOTICE "Interrupt [%d] for device %s was triggered, jiffies=%lu, diff=%lu, direction: %d \n",
irq, (char *) dev_id, jiffies, diff, gpio_get_value(GPIO_BUTTON1));
old_jiffie = jiffies;
return IRQ_HANDLED;
}
I've managed to further refine the debouncing algorithm. I now trigger the interrupt when the line reaches LOW instead of falling edge. In the interrupt handler I then change the interrupt to trigger on HIGH next time, then in LOW and so on. This combined with the jiffies method from above significantly reduces the bounces.
I think it's the best one can achieve in software. For further improvements I think this has to be done in hardware, indeed. When I have time I'll try with a capacitor and things like this.
The full code bellow:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#define DRIVER_AUTHOR ""
#define DRIVER_DESC "Button pad"
extern unsigned long volatile jiffies;
struct _Button
{
unsigned gpio;
int irq;
const char* description;
char last_value;
};
typedef struct _Button Button;
// we want GPIO's 17,27,22 (pin 11,13,15 on P5 pinout raspberry pi rev. 2 board) to generate interrupt
Button button1 = {17, 0, "Gpio for button 1", 1 };
Button button2 = {27, 0, "Gpio for button 2", 1 };
Button button3 = {22, 0, "Gpio for button 3", 1 };
/****************************************************************************/
/* IRQ handler - fired on interrupt */
/****************************************************************************/
static irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
Button* button = (Button*) dev_id;
char value = gpio_get_value(button->gpio);
if (value == button->last_value)
return IRQ_HANDLED;
if (0 == value)
{
static unsigned long old_jiffie = 0;
unsigned long diff = jiffies - old_jiffie;
if (diff < 23)
{
button->last_value = value;
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
// old_jiffie = jiffies;
return IRQ_HANDLED;
}
printk(KERN_NOTICE "Interrupt [%d] for device %s was triggered, jiffies=%lu, diff=%lu, direction: %d \n",
irq, button->description, jiffies, diff, value);
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
old_jiffie = jiffies;
}
else
{
irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
}
button->last_value = value;
return IRQ_HANDLED;
}
/****************************************************************************/
/* This function configures interrupts. */
/****************************************************************************/
void int_config(Button* button)
{
if (!button || gpio_request(button->gpio, button->description)) {
printk("GPIO request faiure: %s\n", button->description);
return;
}
gpio_direction_input(button->gpio);
if ( (button->irq = gpio_to_irq(button->gpio)) < 0 ) {
printk("GPIO to IRQ mapping faiure %s\n", button->description);
return;
}
printk(KERN_NOTICE "Mapped int %d\n", button->irq);
if (request_irq(button->irq,
(irq_handler_t ) irq_handler,
IRQF_TRIGGER_LOW,
button->description,
button)) {
printk("Irq Request failure\n");
return;
}
return;
}
/****************************************************************************/
/* This function releases interrupts. */
/****************************************************************************/
void int_release(Button* button) {
free_irq(button->irq, button);
gpio_free(button->gpio);
return;
}
int init_module(void)
{
printk(KERN_INFO "init_module() called\n");
int_config(&button1);
int_config(&button2);
int_config(&button3);
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "cleanup_module() called\n");
int_release(&button1);
int_release(&button2);
int_release(&button3);
}
/****************************************************************************/
/* Module licensing/description block. */
/****************************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
MODULE_DESCRIPTION("Driver for RaspeberryPi's GPIO");