GPIO Interrupt Debounce

7

Ich habe diese Anleitung befolgt : Raspberry Pi GPIO-Interrupts im Kernelraum , um ein Kernelmodul zu erstellen, das GPIO-Interrupts verarbeitet. Das Problem ist, dass in diesem Code keine Software-Entprellung implementiert ist.

Können Sie mir bitte einen Rat geben, wie Software-Debouncing implementiert werden kann oder wie ein Hardware-Debouncing-System einfach implementiert werden kann?

Flavio Barisi
quelle
Sie können Ihren Code in einen Antwortbereich verschieben (Sie können Ihre eigenen Fragen beantworten). Auf diese Weise wäre es viel sauberer. Denken Sie daran, dies ist kein Forum.
Krzysztof Adamski

Antworten:

7

Ich habe noch nie versucht, dies ohne das RPi zu tun, aber ich habe es mehrmals auf der Arduino-Plattform getan, und ich habe etwas in Anlehnung an diesen Arduino-Code verwendet. Beachten Sie, dass Sie eine ähnliche Funktion für die von Ihnen verwendete Sprache neu schreiben müssen:

unsigned long last_interrupt_time = 0;

void my_interrupt_handler()
{
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 200ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 200) 
  {
    ... Interrupt okay, do something;
  }
  last_interrupt_time = interrupt_time;
}
Butter
quelle
ok, ich habe es geschafft ... millis () in keiner Himbeerfunktion, also musste ich es selbst implementieren. Hier ist der Quellcode geändert
Flavio Barisi
@FlavioBarisi Entschuldigung. Dies ist Code für das Arduino, der nur als Beispiel enthalten ist. Ich hatte gestern keine Zeit, es in Python von C umzuschreiben. Ich werde das in der Antwort vermerken.
Butters
1

Am einfachsten ist der Hardware-Debouncer, der aus einem RC-Filter gefolgt von einem Schmitt-Trigger oder nur einem RC-Filter besteht, wenn Sie faul sind. Teile sind billig, überall erhältlich und können problemlos in alle Schaltkreise eingebaut werden, die Sie an Ihren Raspberry Pi anschließen möchten.

lenik
quelle
1

Es gemacht!

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/time.h>

#define DRIVER_AUTHOR "Igor <[email protected]>"
#define DRIVER_DESC   "Tnterrupt Test"

// we want GPIO_17 (pin 11 on P5 pinout raspberry pi rev. 2 board)
// to generate interrupt
#define GPIO_ANY_GPIO                17

// text below will be seen in 'cat /proc/interrupt' command
#define GPIO_ANY_GPIO_DESC           "Some gpio pin description"

// below is optional, used in more complex code, in our case, this could be
// NULL
#define GPIO_ANY_GPIO_DEVICE_DESC    "some_device"


/****************************************************************************/
/* Interrupts variables block                                               */
/****************************************************************************/
short int irq_any_gpio    = 0;

unsigned int last_interrupt_time = 0;
static uint64_t epochMilli;

unsigned int millis (void)
{
  struct timeval tv ;
  uint64_t now ;

  do_gettimeofday(&tv) ;
  now  = (uint64_t)tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000) ;

  return (uint32_t)(now - epochMilli) ;
}

/****************************************************************************/
/* IRQ handler - fired on interrupt                                         */
/***************************************************************************/
static irqreturn_t r_irq_handler(int irq, void *dev_id, struct pt_regs *regs) {
   unsigned long flags;
   unsigned int interrupt_time = millis();

   if (interrupt_time - last_interrupt_time < 1000) 
   {
     printk(KERN_NOTICE "Ignored Interrupt!!!!! [%d]%s \n",
          irq, (char *) dev_id);
     return IRQ_HANDLED;
   }
   last_interrupt_time = interrupt_time;

   // disable hard interrupts (remember them in flag 'flags')
   local_irq_save(flags);

   printk(KERN_NOTICE "Interrupt [%d] for device %s was triggered !.\n",
          irq, (char *) dev_id);

   // restore hard interrupts
   local_irq_restore(flags);

   return IRQ_HANDLED;
}




/****************************************************************************/
/* This function configures interrupts.                                     */
/****************************************************************************/
void r_int_config(void) {

  struct timeval tv ;

  do_gettimeofday(&tv) ;
  epochMilli = (uint64_t)tv.tv_sec * (uint64_t)1000    + (uint64_t)(tv.tv_usec / 1000) ;

   if (gpio_request(GPIO_ANY_GPIO, GPIO_ANY_GPIO_DESC)) {
      printk("GPIO request faiure: %s\n", GPIO_ANY_GPIO_DESC);
      return;
   }

   if ( (irq_any_gpio = gpio_to_irq(GPIO_ANY_GPIO)) < 0 ) {
      printk("GPIO to IRQ mapping faiure %s\n", GPIO_ANY_GPIO_DESC);
      return;
   }

   printk(KERN_NOTICE "Mapped int %d\n", irq_any_gpio);

   if (request_irq(irq_any_gpio,
                   (irq_handler_t ) r_irq_handler,
                   IRQF_TRIGGER_FALLING,
                   GPIO_ANY_GPIO_DESC,
                   GPIO_ANY_GPIO_DEVICE_DESC)) {
      printk("Irq Request failure\n");
      return;
   }

   return;
}


/****************************************************************************/
/* This function releases interrupts.                                       */
/****************************************************************************/
void r_int_release(void) {

   free_irq(irq_any_gpio, GPIO_ANY_GPIO_DEVICE_DESC);
   gpio_free(GPIO_ANY_GPIO);

   return;
}


/****************************************************************************/
/* Module init / cleanup block.                                             */
/****************************************************************************/
int r_init(void) {

   printk(KERN_NOTICE "Hello !\n");
   r_int_config();

   return 0;
}

void r_cleanup(void) {
   printk(KERN_NOTICE "Goodbye\n");
   r_int_release();

   return;
}


module_init(r_init);
module_exit(r_cleanup);


/****************************************************************************/
/* Module licensing/description block.                                      */
/****************************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
Flavio Barisi
quelle
1

Was ist, wenn Sie den vorherigen Wert / Zustand des Pins / GPIO überwachen?

Ich musste usleep()in der while-Schleife verwenden, um CPU-Thrashing / Racing zu vermeiden. Siehe diese Antwort auf C, Fehler beim Kompilieren während der Verwendung der Funktion "usleep" .

Dies funktioniert für meinen Zweck:

#include <unistd.h>
uint8_t prevStatus;

void sendStatus (void) {
        uint8_t status = digitalRead(ISR_Pin);
        if (prevStatus != status) {
                if (status == 0)
                         falling();
                else
                         rising();
        }
        prevStatus = status;
}


int main (void) {
    /// Make sure WiringPi is installed and cofigured
    if ( wiringPiSetup() == -1 )
        exit( 1 );

    /// Set-up GPIO as INPUT, with Pull-down
    pinMode (ISR_Pin, INPUT);
    pullUpDnControl(TISR_Pin, PUD_DOWN); // If you need it or PUD_UP for Pull-up

    /// Set-up Hardware Interrupt for BOTH Falling & Rising Edge
    wiringPiISR(ISR_Pin, INT_EDGE_BOTH, &sendStatus);

    /// Wait for ISR_Pin pin change
    while (1) {
         usleep(50000);
    }

    return(0);
}
Electron1979
quelle
1

Ich habe es getan, indem ich die Sekunden anstatt der Zeit überprüft habe. Ich denke, auf diese Weise entsteht weniger Aufwand. Ich versuche jedoch immer noch, den Entprellungsalgorithmus zu verbessern, da ich damit nicht ganz zufrieden bin: Es gibt immer noch Bounces, obwohl sie weniger häufig sind als zuvor.

#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;
}

Ich habe es geschafft, den Entprellungsalgorithmus weiter zu verfeinern. Ich löse jetzt den Interrupt aus, wenn die Leitung LOW erreicht, anstatt die Flanke zu fallen. Im Interrupt-Handler ändere ich dann den Interrupt so, dass er beim nächsten Mal auf HIGH, dann auf LOW und so weiter ausgelöst wird. Dies in Kombination mit der Jiffies-Methode von oben reduziert die Bounces erheblich.

Ich denke, es ist das Beste, was man mit Software erreichen kann. Für weitere Verbesserungen denke ich, dass dies tatsächlich in der Hardware gemacht werden muss. Wenn ich Zeit habe, versuche ich es mit einem Kondensator und ähnlichen Dingen.

Der vollständige Code unten:

#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");
John D.
quelle