1 2 Previous Next 23 Replies Latest reply: Jan 30, 2013 11:13 AM by rdolson Go to original post RSS
  • 15. Re: Can GPIO pins generate interrupts?
    dkossman New Members

    Not sure I'm following your suggestion... My idea is to create a thread that does the poll(), ie is event-driven and wakes up with activity on the pin to increment a counter.  Thus the main program thread can handle the main control loop.

     

    the gpio input is set up for edge-triggered interrupts.

     

    Once i finish it (in a few days i hope, and assuming it actually works :-)) should I post my code here so you can review and clarify/make suggestions?

     

    Thx

    Don

  • 16. Re: Can GPIO pins generate interrupts?
    rdolson New Members

    What would your main program be doing while waiting for events from the thread? If it's only acting on external events, or on timeouts ("if no bubbles in 3 minutes do something"), you don't need a separate thread and the associated synchonization - you can use the poll mechanism to set up the timeouts.

     

    I'd be happy to take a look at the code.

  • 17. Re: Can GPIO pins generate interrupts?
    dkossman New Members

    Hi Robert,

     

    Here's the current code - no threads.  Note that the program doesn't do anything useful yet - I'm still just playing around to figure out the basic structure.

     

    My intent is to move the poll() into a thread, and change the timeout value so it waits forever.  Then the main program thread would have a separate loop (perhaps running once per 5 minutes - aquariums don't change very quickly) where I check the counts (i believe i will need to use semaphone to protect read/write of the counts) and do stuff based on a control algorithm.

     

    The main loop would be augmented to deal with the other sensors as well - temperature, ORP, Ph, and so on.

     

    It seems to me that if I leave the poll() and pulse counter in my main program, I can't easily control the timing of my main control loop.  Let me know if I'm missing something...

     

    regards

    Don

     

     

    /*

    * counts pulses on one or more gpio inputs using poll()

    * based on code pilfered from various places.  will add credits later...

    * uses wiringPi library, currently must run as root.  will deal with that later...

    */

     

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <errno.h>

    #include <unistd.h>

    #include <fcntl.h>

    #include <poll.h>

    #include <string.h>

    #include <pthread.h>

     

    extern int gpio_export(unsigned int gpio);

    extern int gpio_unexport(unsigned int gpio);

    extern int gpio_set_dir(unsigned int gpio, unsigned int out_flag);

    extern int gpio_set_value(unsigned int gpio, unsigned int value);

    extern int gpio_get_value(unsigned int gpio, unsigned int *value);

    extern int gpio_set_edge(unsigned int gpio, char *edge);

    extern int gpio_fd_open(unsigned int gpio);

    extern int gpio_fd_close(int fd);

     

    /****************************************************************

    * Constants

    ****************************************************************/

     

    #define POLL_TIMEOUT (3 * 1000) /* 3 seconds */

    #define MAX_BUF 64

    #define MAX_PINS 16

     

    /****************************************************************

    * Main

    ****************************************************************/

    int main(int argc, char **argv, char **envp)

    {

        struct pollfd fdset[MAX_PINS];

        int nfds = argc-1;

        int gpio_fd[MAX_PINS], timeout, rc;

        char *buf[MAX_BUF];

        unsigned int gpio[MAX_PINS];

        int i;

        int count[MAX_PINS];

     

        if (argc < 2) {

            printf("Usage: %s <gpio-pin> .. <gpio-pin>\n", argv[0]);

            printf("Waits for a rising edge on the specified GPIO pins\n");

            exit(-1);

        }

     

        // set up

        for (i=0; i < nfds; i++) {

            gpio[i] = atoi(argv[i+1]);

            gpio_export(gpio[i]);

            gpio_set_dir(gpio[i], 0);  // input

            gpio_set_edge(gpio[i], "falling");

            gpio_fd[i] = gpio_fd_open(gpio[i]);

            printf("set up gpio input %d\n", gpio[i]);

            count[i] = 0;

        }

     

        timeout = POLL_TIMEOUT;

     

        memset((void*)fdset, 0, sizeof(fdset));

     

        for (i=0; i < nfds; i++) {

            fdset[i].fd = gpio_fd[i];

            fdset[i].events = POLLPRI;

        }

     

        while (1) {

            // poll() returns with 0 after a timeout or > 1 for activity on any of the specified pins

            rc = poll(fdset, nfds, timeout);

     

            if (rc > 0) { // activity occurred on one or more pins

                for (i=0; i<nfds; i++) {  // check each file descriptor

                    if (fdset[i].revents & POLLPRI) {

                        // there was activity on this pin

                        read(fdset[i].fd, buf, MAX_BUF);  // this clears the interrupt, i think?

                        count[i]++;

                        printf("GPIO %d interrupt %d\n", gpio[i], count[i]);

                    } // if

                    fflush(stdout);

                } // for

            } else {

                if (rc < 0) {

                    printf("poll() failed with %d!\n", errno);

                    return -1;

                } // if

            } // else

        } // while

     

        // clean up

        for (i=0; i<nfds; i++) {

            gpio_fd_close(gpio_fd[i]);

        }

     

        return 0;

    }

  • 18. Re: Can GPIO pins generate interrupts?
    rdolson New Members

    Here's my code with some fairly crude high-level timer support. The idea here is that there are a number of timers that can be registered by the user code; you do this by calling

     

         add_timer(when, interval, oneshot, callback)

     

    when is a double denoting when the timer should trigger. The function get_now() returns a double representing the current time (in seconds).

     

    interval is a double denoting the time between invocations of interval timers - timers that trigger indefinitely until disabled.

     

    oneshot is an int that if true, says this is a one shot timer, and if false (0), says this is a repeating timer.

     

    callback is a pointer to a function that looks like

     

         int callback_function(ptimer_t *timer);

     

    the timer parameter is a pointer to the underlying timer object, which can safely be ignored. If the timer is a repeating timer, the return value from the callback affects if the timer is removed or not. Return 1 and the timer will keep getting called, return 0 and the timer is removed.

     

    In this example the poll will wake up the code to handle interrupts if there are interrupts, and will otherwise wake up to handle any timers that are registered.

     

    --bob

     

     

    #include <stdio.h>

    #include <poll.h>

    #include <stdlib.h>

    #include <fcntl.h>

    #include <string.h>

    #include <sys/time.h>

     

     

    #define GPIO_FN_MAXLEN          32

    #define POLL_TIMEOUT          1000

    #define RDBUF_LEN          5

     

     

    typedef struct ptimer ptimer_t;

     

     

    typedef int (*ptimer_cb)(ptimer_t *);

     

     

    struct ptimer

    {

        double when;

        double interval;

        int oneshot;

        ptimer_cb cb;

        ptimer_t *next;

    };

     

     

    static ptimer_t timers;

     

     

    double times[1000];

    int n;

     

     

    double get_now()

    {

        struct timeval tv;

        gettimeofday(&tv, 0);

        return (double) tv.tv_sec + (double) tv.tv_usec * 1e-6;

    }

     

     

    void add_timer(double when, double interval, int oneshot, ptimer_cb cb)

    {

        ptimer_t *t = (ptimer_t *) calloc(sizeof(ptimer_t), 1);

        t->when = when;

        t->interval = interval;

        t->oneshot = oneshot;

        t->cb = cb;

        t->next = timers.next;

        timers.next = t;

    }

     

     

    int hi(ptimer_t *t)

    {

        printf("Hi!\n");

        return 1;

    }

     

     

    int hi2(ptimer_t *t)

    {

        printf("Hi 2!\n");

        return 1;

    }

     

     

    int hi3(ptimer_t *t)

    {

        printf("Hi 3!\n");

        return 1;

    }

     

     

    double least_time()

    {

        ptimer_t *t = timers.next;

        /* we only invoke this routine if there is at least one timer. */

        double least = t->when;

        t = t->next;

     

     

        while (t)

        {

              if (t->when < least)

              {

                  least = t->when;

              }

              t = t->next;

        }

        return least;

    }

     

     

    void call_timers()

    {

        ptimer_t *t, *last_t;

        double now = get_now();

        last_t = &timers;

        while (last_t->next)

        {

              t = last_t->next;

              if (t->when <= now)

              {

                  int keep = (t->cb)(t);

                  if (t->oneshot || !keep)

                  {

                        t = t->next;

                        free(last_t->next);

                        last_t->next = t;

                  }

                  else if (!t->oneshot)

                  {

                        t->when += t->interval;

                  }

              }

              last_t = t;

        }

    }

     

     

    int main(int argc, char **argv) {

        char fn[GPIO_FN_MAXLEN];

        int fd,ret;

        struct pollfd pfd;

        char rdbuf[RDBUF_LEN];

     

     

        timers.next = 0;

        add_timer(get_now() + 1.0, 1.0, 0, hi);

        add_timer(get_now() + 2.0, 1.7, 0, hi2);

        add_timer(get_now() + 2.1, 0.0, 1, hi3);

     

     

        memset(rdbuf, 0x00, RDBUF_LEN);

        memset(fn, 0x00, GPIO_FN_MAXLEN);

     

     

        if(argc!=2) {

                  printf("Usage: %s <GPIO>\nGPIO must be exported to sysfs and have enabled edge detection\n", argv[0]);

                  return 1;

        }

        snprintf(fn, GPIO_FN_MAXLEN-1, "/sys/class/gpio/gpio%s/value", argv[1]);

        fd=open(fn, O_RDONLY);

        if(fd<0) {

                  perror(fn);

                  return 2;

        }

        pfd.fd=fd;

        pfd.events=POLLPRI;

       

        ret=read(fd, rdbuf, RDBUF_LEN-1);

        if(ret<0) {

                  perror("read()");

                  return 4;

        }

        printf("value is: %s\n", rdbuf);

       

        n = 0;

        while(1) {

                  memset(rdbuf, 0x00, RDBUF_LEN);

                  lseek(fd, 0, SEEK_SET);

                  struct timespec wait;

                  if (timers.next)

                  {

                        double l = least_time();

                        double lwait = l - get_now();

                        if (lwait < 0)

                        {

                            wait.tv_sec = 0L;

                            wait.tv_nsec = 1000;

                        }

                        else

                        {

                            wait.tv_sec = (long) lwait;

                            wait.tv_nsec = (long) ((lwait - (double) wait.tv_sec) * 1e9);

                        }

                  }

                  else

                  {

                        wait.tv_sec = 1L;

                        wait.tv_nsec = 0L;

                  }

     

     

     

     

                  ret = ppoll(&pfd, 1, &wait, 0);

                  if(ret<0) {

                            perror("poll()");

                            close(fd);

                            return 3;

                  }

                  if(ret==0) {

                            printf("timeout\n");

                            call_timers();

                            continue;

                  }

                  ret=read(fd, rdbuf, RDBUF_LEN-1);

                  if(ret<0) {

                            perror("read()");

                            return 4;

                  }

    /*

                  struct timeval tv;

                  gettimeofday(&tv, 0);

                  if (n < 999)

                  {

                            times[n++] = (double) tv.tv_sec + (double) tv.tv_usec * 1e-6;

                  }

                  else

                  {

                            int i;

                            for (i = 1; i < 1000; i++)

                            {

                                      printf("%lf\n", 1.0/ (times[i] - times[i-1]));

                            }

                            n = 0;

                  }

    */

                  printf("interrupt, value is: %s\n", rdbuf);

        }

        close(fd);

        return 0;

    }

  • 19. Re: Can GPIO pins generate interrupts?
    dkossman New Members

    Thanks for this - I'll probably end up incorporating elements of this - I'll need to set up handlers that are invoked at different intervals.  So, much appreciated.

     

    Actually, I forgot to mention a primary reason for using a separate thread to handle the poll() for the GPIO edge interrupt, which is that this will enable me to change (raise) the priority of this relative to the rest of the code.  The app will eventually be writing data to a mysql database, talking to an LCD display, driving relays, etc., and I don't want it to miss any interrupts when counting pulses.

     

    regards

    Don

  • 20. Re: Can GPIO pins generate interrupts?
    efflandt New Members

    I have done little programming lately, but seem to remember from PC Linux there may be a way to tell if you missed any interrupts from /proc/interrupts.

  • 21. Re: Can GPIO pins generate interrupts?
    rdolson New Members

    I'm not positive about this but as I understand the way the file descriptor logic works if a fd is signalled because of an interrupt, until you clear that status (by reading from the file descriptor) you'll still get notification that it happened. What you may lose is the number of interrupts that occurred. This is where the gpio-event code that's kicking around comes in - it will record and timestamp interrupts down at the linux kernel level, and then report those up to the user code.

     

    if you're using threads it's still possible you will miss interrupts, particularly if you are doing something I/O intensive in the other thread.

  • 22. Re: Can GPIO pins generate interrupts?
    dkossman New Members

    Sorry - but which "gpio-event code that's kicking around" are you referring to?

  • 23. Re: Can GPIO pins generate interrupts?
    rdolson New Members

    This one, from the gumstix folks:

     

    http://wiki.gumstix.org/index.php?title=GPIO_Event_Driver

     

    It will compile on the raspbian kernel, but I was getting kernel crashes with my first tests. I have not had a chance to debug it further.

1 2 Previous Next