GPIO Interrupts

Hi,

I’ve been looking for quite some time for a simple GPIO (hardware) interrupt example where I can press a button and an interrupt will be called in my application. I have come across a similar question (https://devtalk.nvidia.com/default/topic/940097/?comment=4896933) which pointed me in the direction of /sources/kernel/drivers/mmc/host/sdhci-tegra.c. I’m not quite sure if this is appropriate to what I’m trying to achieve(??).

I have the JetsonHacks GPIO interface example (http://www.jetsonhacks.com/2015/12/29/gpio-interfacing-nvidia-jetson-tx1/) set up and working fine, but I would like an interrupt to be called when the button is pressed, rather than the application constantly checking the status of the button. I have explored the possibility of implementing a kernel module but I’m confused if I’m gaining anything by doing that, rather than just calling /sys/class/gpio/gpioxxx (see example code below).

I have 2 questions, ultimately:

  1. Is below the de-facto way of communicating GPIO using Ubuntu? I'm struggling to find alternatives
  2. Is there a way of implementing some sort of interrupt handlers? (software/hardware/etc)

I’m struggling to migrate from the real-time embedded software development mindset to the Ubuntu application and Ubuntu Kernel development. This concept of reading/writing to a file to do “stuff” with GPIO is quite alien to me and I question if this is appropriate for real-time development. I would be very grateful for any clarity or advice.

Many thanks in advance!

// gpioGetValue
// Get the value of the requested GPIO pin ; value return is 0 or 1
// Return: Success = 0 ; otherwise open file error
int gpioGetValue ( jetsonGPIO gpio, unsigned int *value)
{
    int fileDescriptor;
    char commandBuffer[MAX_BUF];
    char ch;

    snprintf(commandBuffer, sizeof(commandBuffer), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

    fileDescriptor = open(commandBuffer, O_RDONLY);
    if (fileDescriptor < 0) {
        char errorBuffer[128] ;
        snprintf(errorBuffer,sizeof(errorBuffer), "gpioGetValue unable to open gpio%d",gpio) ;
        perror(errorBuffer);
        return fileDescriptor;
    }

    if (read(fileDescriptor, &ch, 1) != 1) {
        perror("gpioGetValue") ;
        return fileDescriptor ;
     }

    if (ch != '0') {
        *value = 1;
    } else {
        *value = 0;
    }

    close(fileDescriptor);
    return 0;
}

hello david_evans_g,

you could refer to gpio header, $TOP/kernel/include/linux/gpio.h
there’s gpio_get_value() and gpio_set_value() API in kernel driver to control gpio.
thanks

Hi, JerryChang.

Apologies for asking a stupid question (I’m having trouble moving from an embedded development mindset to Linux embedded development), but am I able to use the functions you’ve mentioned above in the ‘userspace’, or not? I’m having great difficulty trying to use the gpio.h header, my IDE claims it doesn’t exist, despite being able open it (I’ve checked the linkers too, it doesn’t appear to be an issue there).

hello david_evans_g,

sorry for late reply, functions in comment #2 were kernel layer APIs.
thanks

Thanks, Jerry!

To resolve any confusion for people searching through this thread. I’m of the opinion that you have to use the drivers Jerry mentioned above in the Kernel Space, and have a way of communicating to those drivers through something like a Kernel Module (or otherwise).

Found a good link introducing Kernel Module development:

David,

I am curious about this same idea. Did you manage to determine the latency with using Interrupt Driven GPIO? I did the derekmolloy tutorial, and it seems like it takes ~3ms to handle an interrupt trigger from a GPIO pin. This is much slower than my tolerances allow. Were you seeing similar results?

Thanks!

hello bryant.hayes,

did you observed ~3ms latency by controlling the GPIO pin with below function calls?

please refer to gpio header, $TOP/kernel/include/linux/gpio.h
there are gpio_get_value() and gpio_set_value() API in kernel driver to control gpio.

Hi, Bryant.

Can you elaborate a little further what you’re doing?

Assuming you’re doing the Derekmolloy tutorial (http://derekmolloy.ie/kernel-gpio-programming-buttons-and-leds/), is it determining how long it takes the pushed button to be pressed and turn on an LED? How are you determining the ~3ms timing? Are you evaluating this timing on an oscilloscope, or in the Userspace (through printfs), or in the Kernelspace (through prinntks)?

Thanks,

David.

Hi, Bryant.

Just something else for you to try out, regarding timing:

This application flashes an LED connected to the TX1:
http://www.jetsonhacks.com/2015/12/29/gpio-interfacing-nvidia-jetson-tx1/
https://github.com/jetsonhacks/jetsonTX1GPIO

In the following file:
exampleGPIOApp.cpp

Remove the ‘usleep(…)’ function (shown below) and put an oscilloscope on pins to see the delay, you should get a nice square wave with the time it takes to toggle a GPIO value from the userspace. Try removing the ‘cout’ too and see does that impact timing.

// Flash the LED 5 times
    for(int i=0; i<5; i++){
        cout << "Setting the LED on" << endl;
        gpioSetValue(redLED, on);
        //usleep(200000);         // on for 200ms
        cout << "Setting the LED off" << endl;
        gpioSetValue(redLED, off);
        //usleep(200000);         // off for 200ms
    }

David.

David,

I had the input pin hooked up to a function generator, and the output pin hooked up to an o-scope. I also had an o-scope channel looking at the input signal. I found the delta time between the rising edge of the input signal and the resulting edge from the output which was set during the input irq.

I found my problem though, I was issuing a printk() call inside my interrupt before setting the pin high. Once I removed that, I saw my time drop from 3ms to 28us.

Best,
Bryant

I try to use “GPIO8_ALS_PROX_INT” (on developer kit J21, pin 37) as an external interrupt. I followed the Derekmolloy tutorial which is a great tutorial and helps a lot.

Now I want to synchronize the time of interrupt in the user space, i.e. to notify application (C/C++ code) when external interrupt happens and being handled in the kernel space. I don’t want to use poll method, better interrupt. How can I generate an interrupt from kernel space to user space?

I have another concern is writing of kobject, because of more restriction of new kernel, writing of kobject seems not possible, any solution for this situation?

I also would like to add the module into kernel in application (C/C++ code) without doing it manually as “$sudo insmod button.ko”, any clue to do this?

Best,
Tao

David,

It is typically recommended to handle interrupt processing in kernel space, but if you really want to propagate it up to user space, one option is Userspace I/O https://www.kernel.org/doc/html/v4.12/driver-api/uio-howto.html. This basically uses a lightweight kernel module to create /dev/uiX, which you can then issue a blocking read() on, until your interrupt occurs.

Regarding adding a module to the kernel itself, you can look into building it as part of the kernel build. You can add a driver to “$KERNEL_DIR/drivers/button/button.c” and update the makefile / KConfig to build it into the kernel. Perhaps a useful link: https://stackoverflow.com/questions/11710022/adding-new-driver-code-to-linux-source-code

Hope this helps,
Bryant