4 Replies Latest reply on Oct 22, 2015 3:45 PM by CMata_Intel

    Configuring GPIO in a Linux module. Debouncing and pullup.

    Vincenze

      I want to connect a button to a GPIO.

       

      I can initialize a GPIO pin using mraa from the user space. Essentially, it writes some values into the files in /sys/class/gpio.

       

      But I prefer to run a kernel module that initializes the GPIO for the button and processes an interrupt when the button is clicked.

       

      I created a sample module. Here is an excerpt:

      //GPIO 46
      static unsigned int gpioButton = 46;
      
      // Set up the GPIO button
      gpio_request(gpioButton, "sysfs");
      // Set the button GPIO to be an input
      gpio_direction_input(gpioButton);
      // Debounce the button with a delay of 1000ms
      gpio_set_debounce(gpioButton, 1000);
      // Causes GPIO 46 to appear in /sys/class/gpio
      gpio_export(gpioButton, false);
      
      

      I tried to change the debounce time to 10000, 100000.

      But debouncing doesn't work. The system still detects secondary clicks.

       

      Does Edison support debouncing?

       

      I'm supposed to define pullups using pinctrl, but I haven't got to that part.

      I think a DTS file must be defined somehow which initializes all pins.

        • 1. Re: Configuring GPIO in a Linux module. Debouncing and pullup.
          CMata_Intel

          Hi Vincenze,

           

          Is that the entire code you are using?

          Could you post how are you loading it as a kernel module? Or are you just running the code in the board?

          Is your goal to accomplish something like the example in the Arduino IDE Digital > Debounce?

           

          In mraa, I haven’t seen functions to work with Debouncing but if you want to do something like the example I mentioned you can modify the library and include this feature or you can create your code and add all the timing routines required for this.

           

          Regards,

          Charlie

          • 2. Re: Configuring GPIO in a Linux module. Debouncing and pullup.
            Vincenze

            Hello, Charlie,

             

            Essentially, I want to know:

            1. Does Edison has built-in hardware debouncing support? Some Atmel chips do.

            2. How to set the pullup state of a GPIO pin from a Linux module?

            3. Is there a simple way of configuring all pins: direction, pullmode, pinmux, etc without writing into /sys/class/gpio? For example, with the help of Device Tree FDTWiki

             

            Edison creates a file in sysfs indicating that it supports debouncing. But I don't see any difference if I enable or disable it.

            cat /sys/kernel/debug/gpio_debug/gpio46/current_debounce

            enable

             

            Here is the full code of the test module.

            I load it using this command:

            insmod /lib/modules/3.10.17-yocto-standard/extra/hello.ko

             

            I created a kernel module with a software debouncer and it works. But a built-in solution would be better.

             

            #include <linux/init.h>
            #include <linux/module.h>
            #include <linux/kernel.h>
            #include <linux/gpio.h>                 // Required for the GPIO functions
            #include <linux/interrupt.h>            // Required for the IRQ code
            
            MODULE_LICENSE("GPL");
            MODULE_AUTHOR("Test");
            MODULE_DESCRIPTION("A Button test driver");
            MODULE_VERSION("0.1");
            
            static unsigned int gpioButton = 46;   ///< hard coding the button gpio for this example to P9_27 (GPIO115)
            static unsigned int irqNumber;          ///< Used to share the IRQ number within this file
            static unsigned int numberPresses = 0;  ///< For information, store the number of button presses
            
            /// Function prototype for the custom IRQ handler function -- see below for the implementation
            static irq_handler_t  test_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);
            
            /** @brief The LKM initialization function
            *  The static keyword restricts the visibility of the function to within this C file. The __init
            *  macro means that for a built-in driver (not a LKM) the function is only used at initialization
            *  time and that it can be discarded and its memory freed up after that point. In this example this
            *  function sets up the GPIOs and the IRQ
            *  @return returns 0 if successful
            */
            static int __init test_init(void){
               int result = 0;
               printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST\n");
            
               gpio_request(gpioButton, "sysfs");       // Set up the gpioButton
               gpio_direction_input(gpioButton);        // Set the button GPIO to be an input
               gpio_set_debounce(gpioButton, 1000);      // Debounce the button with a delay of 1000ms
               gpio_export(gpioButton, false);          // Causes gpio 46 to appear in /sys/class/gpio
                                            // the bool argument prevents the direction from being changed
               // Perform a quick test to see that the button is working as expected on LKM load
               printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpioButton));
            
               // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
               irqNumber = gpio_to_irq(gpioButton);
               printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d\n", irqNumber);
            
               // This next call requests an interrupt line
               result = request_irq(irqNumber,             // The interrupt number requested
                                    (irq_handler_t) test_irq_handler, // The pointer to the handler function below
                                    IRQF_TRIGGER_FALLING,   // Interrupt on rising edge (button press, not release)
                                    "ebb_gpio_handler",    // Used in /proc/interrupts to identify the owner
                                    NULL);                 // The *dev_id for shared interrupt lines, NULL is okay
            
               printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result);
               return result;
            }
            
            /** @brief The LKM cleanup function
            *  Similar to the initialization function, it is static. The __exit macro notifies that if this
            *  code is used for a built-in driver (not a LKM) that this function is not required. Used to release the
            *  GPIOs and display cleanup messages.
            */
            static void __exit test_exit(void){
               printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(gpioButton));
               printk(KERN_INFO "GPIO_TEST: The button was pressed %d times\n", numberPresses);
               free_irq(irqNumber, NULL);               // Free the IRQ number, no *dev_id required in this case
               gpio_unexport(gpioButton);               // Unexport the Button GPIO
               gpio_free(gpioButton);                   // Free the Button GPIO
               printk(KERN_INFO "GPIO_TEST: Goodbye!\n");
            }
            
            /** @brief The GPIO IRQ Handler function
            *  This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt
            *  handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete.
            *  This function is static as it should not be invoked directly from outside of this file.
            *  @param irq    the IRQ number that is associated with the GPIO -- useful for logging.
            *  @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt
            *  Not used in this example as NULL is passed.
            *  @param regs   h/w specific register values -- only really ever used for debugging.
            *  return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise.
            */
            static irq_handler_t test_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
               printk(KERN_INFO "GPIO_TEST: Interrupt! (button state is %d)\n", gpio_get_value(gpioButton));
               numberPresses++;                         // Global counter, will be outputted when the module is unloaded
               return (irq_handler_t) IRQ_HANDLED;      // Announce that the IRQ has been handled correctly
            }
            
            /// This next calls are  mandatory -- they identify the initialization function
            /// and the cleanup function (as above).
            module_init(test_init);
            module_exit(test_exit);
            
            
            
            • 3. Re: Configuring GPIO in a Linux module. Debouncing and pullup.
              CMata_Intel

              Hi Vincenze,

               

              In order to set the pull-up state of a GPIO from Linux side I suggest you to take a look at the Hardware Guide for the Edison with the Arduino Expansion Board. About the debouncing mode, I will investigate about it and I will let you know as soon as I get more information.

               

              Regards,

              Charlie

              • 4. Re: Configuring GPIO in a Linux module. Debouncing and pullup.
                CMata_Intel

                Hi Vincenze,

                 

                The Edison does not support debouncing, but there are some workarounds using debouncing switches, circuits or via software like the Arduino example I mentioned above (Arduino - Debounce)

                About a simple way of setting the properties for the GPIOs, there is no simple way to configure all pins, however you can create a script/program to set all the desired pin configurations.