how to detect a pin change of a GPIO on Linux board

Angs picture Angs · Sep 21, 2014 · Viewed 7.4k times · Source

I am using 3.12 kernel on an ARM based linux board (imx233 CPU). My purpose is to detect pin change of a GPIO (1 to 0).

I can read the pin value constantly calling the below function (in a while(1) loop)

int GPIO_read_value(int pin){
    int gpio_value = 0;
    char path[35] = {'\0'};
    FILE *fp;
    sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
    if ((fp = fopen(path,"rb+")) == NULL){ //echo in > direction
         //error
    }

    fscanf(fp, "%d", &gpio_value);
    fclose(fp);
    return gpio_value;
}

But it causes too much load to the CPU. I don't use usleep or nanosleep, because the pin change happens for a very short of a time that would cause me to miss the event.

As far as I find out, it is not possible to use poll(). Is there any poll() like function that I can use to detect a pin change of a GPIO?

EDIT: Just in case, if I am doing something wrong, here is my poll() usage that does not detect the pin change

struct pollfd pollfds;
    int fd;
    int nread, result;
    pollfds.fd = open("/sys/class/gpio/gpio51/value", O_RDWR);
    int timeout = 20000;           /* Timeout in msec. */
    char buffer[128];

    if( pollfds.fd < 0 ){
        printf(" failed to open gpio \n");
        exit (1);
    }

    pollfds.events = POLLIN;
    printf("fd opens..\n");
    while (1)
    {
            result = poll (&pollfds, 0, timeout);
            switch (result)
            {
                  case 0:
                    printf ("timeout\n");
                    break;
                  case -1:
                    printf ("poll error \n");
                    exit (1);

                   default:
                printf("something is happening..\n");
                    if (pollfds.revents & POLLIN)
                    {
                        nread = read (pollfds.fd, buffer, 8);
                        if (nread == 0) {
                            printf ("result:%d\n", nread);
                            exit (0);
                         } else {
                            buffer[nread] = 0;
                            printf ("read %d from gpio: %s", nread, buffer);
                         }
                     }
              }
     }
     close(fd);

EDIT2: the code on https://developer.ridgerun.com/wiki/index.php/Gpio-int-test.c works fine with poll() I needed to define the rising/falling edge for the interrupt and a little bit fix on the definition. It solves my problem, however, it might be good for me and some other people to hear/know the alternative methods.

Answer

wiesniak picture wiesniak · Dec 17, 2014

I have never seen this board before, however I guess PIC is fully implemented for this board (usually is like that) but you have to configure interrupt additionally in GPIO controller (usually is like that). Some part should be done as a kernel module, then you have to pass information about interrupt to you application.

Example way to do this is to implement following thing as a kernel module:

and a rest in your application:

  • a function that can coosomeoneperate with interrupt.

Simplest way of passing information about interrupt from kernel to app is by semaphore on kernel side. in module you can implement an ioctl that will sleep until interrupt happen. So application will call this ioctl and its thread will be blocked until interrupt happen.

Inside module, interrupt routine should check if application thread is now blocked, and if so up() semaphore.

EDIT*****

This CPU has SSP that has working mode for SPI. Why dont use it ??