Прерывание RPi 4 GPIO при повышении и понижении триггера не работает в C

Новичок здесь учится программировать модули ядра Linux.

Я хочу написать модуль ядра для своего RPi 4 на языке C.

Я хочу использовать прерывания для включения светодиода, когда я нажимаю кнопку. Пока кнопка нажата, светодиод горит, а когда я ее отпускаю, светодиод должен погаснуть.

Я использую функцию request_irq(), чтобы моя функция, обрабатывающая прерывание, вызывалась по переднему фронту и по заднему фронту моей кнопки, указав IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING. Раньше я использовал функцию gpio_to_irq(BUTTON_PIN).

request_irq(button_irq, button_ih, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button_irq", NULL);

Функция вызывается, когда я нажимаю кнопку, но не когда я ее отпускаю. Он закодирован, как показано ниже:

static irqreturn_t button_ih(int irq, void *data)
{
    int state;
    
    state = gpio_get_value(BUTTON_PIN);

    printk(KERN_INFO "Actual state of the button : %d", state);
    
    //Debounce condition
    if(jiffies - last_interrupt_time > msecs_to_jiffies(200))
    {
        if(state)
        {
            gpio_set_value(LED_PIN, 1);
        }else
        {
            gpio_set_value(LED_PIN, 0);
        }
    }
    last_interrupt_time = jiffies;

    return IRQ_HANDLED;
}

Я проверяю, равно ли значение кнопки 1 (нажато) или 0 (отпущено), и хочу соответственно включить/выключить светодиод.

Когда я пробую разные GPIO, иногда значение моей кнопки равно 1, хотя я еще не нажимал ее (распечатывая сообщение из функции инициализации), а иногда это 0. Поэтому я не понимаю, делаю ли я это. Что-то не так. Я не думаю, что GPIO уже используются, потому что я могу запросить их с помощью функции gpio_request_one().

Поэтому, когда кнопка уже имеет значение 1, светодиод включается, но не выключается, когда я отпускаю кнопку.

И когда значение уже равно 0, значение кнопки не меняется на 1, когда я вхожу в функцию обработчика прерывания (и светодиод, очевидно, не включается).

Не могли бы вы сказать мне, что с ним не так, пожалуйста?


person Gomar    schedule 08.09.2020    source источник
comment
Я делаю это без прерываний, но с потоками, у меня есть функция для повышения_края, падения_края, вверх, вниз, longClick и мультиклики почти без задержки. Если прерывание не важно, я могу показать вам, как это сделать с помощью wireingPi.   -  person JB_DELR    schedule 08.09.2020
comment
Где происходит дебаунс в этом случае? Это делает водитель?   -  person Lundin    schedule 08.09.2020
comment
@JB_DELR Как вы думаете, лучше использовать потоки вместо прерываний? Конечно, мне интересно знать, что ты это сделаешь. Спасибо !   -  person Gomar    schedule 08.09.2020
comment
@Lundin Я просто проверяю, происходит ли прерывание за промежуток времени, превышающий 200 мс, когда я считаю, что больше не может произойти дребезг (условие if, не знаю, ясно ли мне или нет)   -  person Gomar    schedule 08.09.2020
comment
У меня есть программа на C, которая управляет моим домом с помощью платы Unipi 1.1, кнопок для освещения, управляемых событиями. Я не использую модуль ядра. Я читаю GPIO из wireingPi, активирую реле с помощью i2c, читаю значение АЦП и т. д.   -  person JB_DELR    schedule 08.09.2020
comment
Модуль ядра GPIO Это на французском языке, но код по-прежнему английский. blaess.fr/christophe/2012/11 /26/les-gpio-du-raspberry-pi   -  person JB_DELR    schedule 08.09.2020
comment
Вы включили внутренний резистор «pull-up» или «pull-down» в используемом вами выводе gpio? Для того, как вы хотите использовать gpio, предложите включить «вытягивающий» резистор, и нажатие кнопки подключает вывод gpio к соответствующему положительному напряжению.   -  person user3629249    schedule 08.09.2020


Ответы (2)


Сформируйте простую программу на C, а не модуль ядра. Это минимум, чтобы увидеть, как у меня дела. main.h

typedef struct s_Data {
    int exitFlag;
    struct {int value; int bcmPin;} In01; //<--- Stock actual GPIO Value
    struct {int value; int bcmPin;} In02;
    ....
    struct {int value; int bcmPin;} Out01;
    struct {int value; int bcmPin;} Out02;
    ....
} t_Data;

#define NO_Click 0
#define SimpleClick 1
#define LongClick 2
#define MultiClick 3

typedef struct s_Button {
    pthread_t button_thread;
    int *exitFlag;
    int * input; //Button
    int * output;
    int old_state;
    int new_state;
    struct timeval t0;
    struct timeval t1;
    struct timeval pressedTime;
    struct timeval releasedTime;
    int clickType;
    //float debounce = 0.020; // ms debounce period to prevent flickering when pressing or releasing the button
    // debounce not necessary while thread have time to take a coffee
    float DCgap;            // max ms between clicks for a double click event
    float holdTime;        // ms hold period: how long to wait for press+hold event
} t_Button;

main.c

#include "main.h"
#include <wiringPi.h>
#include <pthread.h>

// initial state
static t_Data data = {
  .exitFlag = 0,
  .In01.value = 0,  .In01.bcmPin = 4,
  .In02.value = 0,  .In02.bcmPin = 17,
  .Out01.value = 0, .Out01.bcmPin = 4,
  .Out02.value = 0, .Out02.bcmPin = 17,
}

static _Data data_prev;

static void *inputRead_threadFn(void *p_Data) {
   pinMode(Data.In01.bcmPin, INPUT);
   pullUpDnControl(Data.In01.bcmPin, PUD_UP);
   pinMode(Data.In02.bcmPin, INPUT);
   pullUpDnControl(Data.In02.bcmPin, PUD_UP);

   while (!Data.exitFlag) {
      Data.In01.value = !digitalRead(Data.In01.bcmPin);
      Data.In02.value = !digitalRead(Data.In02.bcmPin);
      if (Data.In01.value != Data_old.In01.value) {
        Data_old.In01.value = Data.In01.value;
      }
      if (Data.In02.value != Data_old.In02.value) {
        Data_old.In02.value = Data.In02.value;
      }
      usleep(50)
   }
}

static void *outputWrite_threadFn(void *p_Data) {
   pinMode(Data.In01.bcmPin, OUTPUT);
   pinMode(Data.In02.bcmPin, OUTPUT);

   while (!Data.exitFlag) {
      digitalWrite(Data.Out01.bcmPin, !Data.Out01.value);
      digitalWrite(Data.Out02.bcmPin, !Data.Out02.value);
   }
   usleep(50)
}

static void *button_threadFn(void *p_Data) {
  t_Button *button = (t_Button *)p_data;

  LOG("Button[%s] thread initialized\r\n", button->name);

  button->old_state = 0;
  button->new_state = 0;
  button->clickType = NO_Click;


  int clickCount = 0;
    
  while(*(button->exitFlag) == 0) {
    
    button->new_state = *(button->input) || *(button->web_input); //*((int *)

    if( button->old_state == 0 && button->new_state == 1 ) {
       //printf("front montant\r\n"); fflush(stdout);
        // *****************************   
        // traitement sur front montant
        // rising edge
        // ***************************** 

        button->old_state = 1;
        
        gettimeofday(&button->pressedTime, 0);
        
        //Button pressed
  

    } else if( (button->old_state == 1) && (button->new_state == 0) ) {
        //printf("front descendant\r\n"); fflush(stdout);
        // ***************************** 
        // traitement sur front descendant
        // falling edge
        // ***************************** 
        button->old_state = 0;
        gettimeofday(&button->releasedTime, 0);
        
        if (my_timedifference_msec(button->releasedTime, button->pressedTime ) < button->DCgap) {
            clickCount++;
            button->clickType = MultiClick;
        }
        
        
        //Button released
        
        
    } else if( (button->old_state == 0) && (button->new_state == 0) ) {
       // printf("front bas\r\n"); fflush(stdout);
        // ***************************** 
        // pas de changement d'état : front bas
        // no state change : edge down
        // ***************************** 
        gettimeofday(&button->t0, 0);
        

        *(button->output) = 0; //<--- here in your case

        //Attendre DC_Gap pour connaitre le nombre de click
        // Wait for DC_Gap to know click count
        if (my_timedifference_msec(button->t0, button->releasedTime) > button->DCgap) {
            if (clickCount == 1) {
                LOG("SimpleClick");
                //Simple Click

            } else if ( clickCount > 1 ) {
                //Multiclicks
            }
            button->clickType = NO_Click;
            clickCount = 0;
        }
        
    } else if( (button->old_state == 1) && (button->new_state == 1) ) {
       // printf("front haut\r\n"); fflush(stdout);
        // ***************************** 
        // pas de changement d'état : front haut
        // no state change : edge up
        // ***************************** 
        gettimeofday(&button->t1, 0);
        
        *(button->output) = 1; //<--- here in your case
       
        //long click
        if (my_timedifference_msec(button->t1, button->pressedTime) >= button->holdTime) {
            LOG("LongClick");
            button->clickType = LongClick;

            //do what you want while not released

            usleep(30*1000);
        }
    }
    usleep(100);   
  }
  printf("Light Loop::exiting...\r\n"); fflush(stdout);
}

int main(int argc, char** argv) {
   wiringPiSetup();
   wiringPiSetupGpio();

   data_prev = data;

   //start input thread
   //start output thread

   int DCGap = 250; //ms
   int HoldTime = 600;
   t_Button Buttons[] = {
    { //WC
        .exitFlag = &Data.exitFlag,
        .DCgap = DCGap,
        .holdTime = HoldTime,
        .input = &Data.In01.value,
        .output = &Data.Out01.value,
    },
    { //chambre
        .exitFlag = &Data.exitFlag,
        .DCgap = DCGap,
        .holdTime = HoldTime,
        .input = &Data.In02.value,
        .output= &Data.Out02.value,
    }
   }
   //start buttons threads
   for (i = 0; i < (sizeof(Buttons) / sizeof(t_Button)) ; i++) {
     ret = pthread_create (&Buttons[i].button_threadFn, NULL, fn_Button, &Buttons[i]); 
     if (ret) {
         fprintf (stderr, "%s", strerror (ret));
     }
   }


   //threads join

   return (EXIT_SUCCESS);
}
person JB_DELR    schedule 08.09.2020

Хорошо, спасибо за все комментарии/ответы.

Это было действительно потому, что я не использовал подтягивающий или подтягивающий резистор должным образом.

Поэтому я изменил свою схему следующим образом: Circuit

person Gomar    schedule 09.09.2020