poll() on a pin returns constantly even though there is no input


Hi,

I want to monitor a pin on BBB for interrupt. I chose pin 117 (pin 25 on P9 → GPIO3_21 = 32*3+21 = 117. I hope that’s correct?)
I came across this question on stackoverflow. And This is my question on stackoverflow
I am following this program from ridgerun and made minor changes since I want to monitor just one pin and not any other files. Changes are highlighted in the code below which I am trying to run. (I have pasted just the main. Rest is the same as the code from the site)
The problem is, when I run the program, it continuously outputs “poll() GPIO 117 interrupt occurred” even though I haven’t connected anything to that particular pin and I have to do ctrl+C to stop the execution. POLLPRI does not work and the program prints just … after every timeout and I think POLLIN is proper since it is not a priority data.

Where am I going wrong?

I read about potential problems and the program seems to take care of the below.

  1. The poll() doesn’t fail.
  2. read() is called after every poll() so data should be consumed. ( I just want the program to notify whenever there is a rising edge saying there is an event occured. I do not want to read data from pin. but included this to avoid potential pitfalls)
  3. struct pollfd is being reset inside while loop ( memset((void*)fdset, 0, sizeof(fdset)); )

`

int main(int argc, char **argv)
{
        **struct pollfd fdset[1];**
        **int nfds = 1;**
        int gpio_fd, timeout, rc;
        char *buf[MAX_BUF];
        unsigned int gpio;
        int len;
        if (argc < 2) {
                printf("Usage: gpio-int <gpio-pin>\n\n");
                printf("Waits for a change in the GPIO pin voltage level or input on stdin\n");
                exit(-1);
        }
        gpio = atoi(argv[1]);
        gpio_export(gpio);
        gpio_set_dir(gpio, 0);
        gpio_set_edge(gpio, "rising");
        gpio_fd = gpio_fd_open(gpio);
        timeout = POLL_TIMEOUT;
        while (1) {
                memset((void*)fdset, 0, sizeof(fdset));     
                **fdset[0].fd = gpio_fd;**
**             fdset[0].events = POLLIN;**
                rc = poll(fdset, nfds, timeout);      
                if (rc < 0) {
                        printf("\npoll() failed!\n");
                        return -1;
                }      
                if (rc == 0) {
                        printf(".");
                }  
                **if (fdset[0].revents & POLLIN) {**
**                   len = read(fdset[0].fd, buf, MAX_BUF);**
                        printf("\npoll() GPIO %d interrupt occurred\n", gpio);
                }
                fflush(stdout);
        }
        gpio_fd_close(gpio_fd);
        return 0;
}

`

Thanks,
sudhir

Sudhir,

If you don't object to using a library there is libsoc[1] which is made for this exact purpose.

https://github.com/jackmitch/libsoc

Cheers,
Jack.

Sudhir,

If you don't object to using a library there is libsoc[1] which is made for this exact purpose.

https://github.com/jackmitch/libsoc

Cheers,
Jack.

Hi Jack,

Thanks for the help!. I looked at your library and it appears that you are using poll(2) as well which is similar to the example I am trying to use in the original post.

There are two things I want to mention

  1. Latency : I came across somewhere that poll() by itself introduces 150ms latency which is not good for the system I am building. (But I want it to work first) And you have mentioned that the library is optimized for reliability rather than speed.
  2. Complexity : Interrupt capture is just a small part of the system I am building and I want the code to be as simple and little as possible. This Code appears to be simple enough and does just the functionality.

Since you have built the library, Can you please just point out where in the code I might be going wrong? The problem might be trivial but I am missing something. I am using POLLIN and it continuously outputs GPIO 117 interrupt occurred even though I have not connected any signals to GPIO 117. More details/analysis done in the original post.

Thank you!
sudhir.

Hi Sudhir,

If you are not well versed in Linux and C then the speed of your own code will be no faster than using my library with debugging disabled I imagine.

Without looking seriously at your code there are two things I want to say.

1) You should be using POLLPRI - look at my library for an example
2) Poll does not introduce 150ms latency

If you run the tests included with my libsoc library you will see that it can service 10,000 interrupts in a non-blocking context in roughly 3 seconds on a beaglebone white. That's per interrupt and simple ISR style routine. I would be suprised if you could get it much faster than that using standard read/write file techniques.

If you really want to roll your own, and are still struggling then just base it off my library. That way you have a fully working example to compare your code with.

Cheers,
Jack.

Hi Jack,

I apologize if my previous reply came across as rude. Let me be more humble and do proper homework before posting from now on :slight_smile:
I didn’t have the time to work on this till now. You were right in pointing out usage of POLLPRI instead of POLLIN. I had tried both earlier and it did not work. Turns out it was because of the faulty pin (assuming here…pin stayed high always regardless of settings). I am using a different pin and POLLPRI and it is working fine.
Although I did not understand the part about the interrupts. I am keeping a counter to count the interrupt event and using function generator, I am feeding pulses to BBB. It is a bare minimum code to to just increment counter upon every rising edge detected. I set the number to 10,000 initiallly and it takes wall clock time of 0.22 seconds to detect 10000 rising edges. I wasn’t sure if the number was correct so I increased the frequency of the pulses to 40,000 and even then the wall clock time is around ~0.2-0.3 seconds. Why might this be? How to check what’s the maximum number of edges which can be detected without losing any edges.

Thanks,
sudhir.

How are you counting your pulses? If you are firing interrupts faster
than you can service them then you will be missing interrupts. There are
only 2 ways to get faster interrupts on the beaglebone than what your
are doing now and that is by either writing a kernel driver, to avoid
the latency in the context switches, or by using the PRU.

Sudhir, do you know what the problem was with the Pin? I have the same problem that poll is not blocking.
Therefor to know why would be great.

Web,

I’m not sure if setfd needs to be set each time in the while() loop. I have this from last year… it works. It ONLY prints the times after CTRL-C is pressed, since printf can sometimes throw off the timing.

int_p8_12.c:

`
// interrupt_gpio44.c
// catches interrupt on p8_12 (gpio44)

#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include <string.h>
#include <signal.h>
#include <sys/time.h>

#define MAX_BINARY_VALUE 8
#define MAX_PATH 64
#define MAX_VALUE 256
#define MAX_RECORDED_DATA 4096

int g_interrupted = 0;

void sig_handler(int signo)
{
if ( signo == SIGINT )
g_interrupted = 1;
}

//============================================================================
//============================================================================
// GPIO RELATED
//============================================================================
//============================================================================
//============================================================================

int set_value( char* sys_str, char* value )
{
int handle = 0;

if ( ( handle = open( sys_str, O_WRONLY ) ) < 0 )
return 0;
write( handle, value, sizeof(char) * strlen( value ) );
close( handle );
return 1;
}

//============================================================================

int get_value( char* sys_str, char* value, int max_length )
{
int handle = 0;

if ( ( handle = open( sys_str, O_RDONLY ) ) < 0 )
return 0;
read( handle, value, sizeof(char) * max_length - 1 );
close( handle );

return 1;
}

//============================================================================
// return microseconds

inline unsigned long get_micros( struct timeval time_value )
{
return ( time_value.tv_sec * 1000000 + time_value.tv_usec );
}

//============================================================================

int main( int argc, char* argv[] )
{
struct timeval curr_time, start_time;
char gpio_value[ MAX_BINARY_VALUE ];
int fd[ 1 ];
struct pollfd pfd[ 1 ];
int count = 0;
int ready;
static unsigned long recorded_time[ MAX_RECORDED_DATA ];
static unsigned char recorded_value[ MAX_RECORDED_DATA ];
int i;

memset( gpio_value, 0, MAX_BINARY_VALUE );

if ( signal( SIGINT, sig_handler ) == SIG_ERR )
printf( “\nCannot assign SIGINT.\n” );

set_value( “/sys/class/gpio/export”, “44” );
set_value( “/sys/class/gpio/gpio44/direction”, “in” );
set_value( “/sys/class/gpio/gpio44/edge”, “both” ); // none, rising, falling or both

fd[ 0 ] = open("/sys/class/gpio/gpio44/value", O_RDONLY );

pfd[ 0 ].fd = fd[ 0 ];
pfd[ 0 ].events = POLLPRI;
pfd[ 0 ].revents = 0;

gettimeofday( &start_time, 0 );
curr_time.tv_sec = start_time.tv_sec;
curr_time.tv_usec = start_time.tv_usec;

printf( “Press ctrl-C to terminate and print output.\n” );

// new POLL
while ( !g_interrupted && count < MAX_RECORDED_DATA )
{
ready = poll( pfd, 1, -1 );

if ( pfd[ 0 ].revents != 0 )
{
gettimeofday( &curr_time, 0 );

lseek( *fd, 0, 0 );
if ( read( *fd, gpio_value, MAX_BINARY_VALUE ) < 0 )
*gpio_value = ‘?’;

recorded_time[ count ] = get_micros( curr_time );
recorded_value[ count ] = *gpio_value;

count++;
}
}
printf( “\n” );
for ( i = 1; i < count; i++ )
printf( “%6d\t%lu\tp8_12: %c\n”, i, recorded_time[ i ] - recorded_time[ i - 1 ], recorded_value[ i ] );

return 0;
}

`

This is a bit off topic, but it is just about as easy to call mmap to get a pointer to the GPIO registers and then just read the pin value directly instead of using the file system. The file system pin write takes about 80 usec at best (maybe 10 msec at worst when something else wants to run). The direct write or read takes about 340 nsec. (Yes, over 200 times faster!)

But the bigger picture is seen when your user-space application drives a pin up and down continuously, and you watch that on a Logic Analyzer: A bit more than half the time your application runs, with 3-5 msec gaps every 5 msec or so. Unmodified Linux is not useful for working with hardware events that happen more often than about every 10 msec, unless you use interrupts or hardware like FIFOs in the SPI module. This is why the PRUs are there!

Thank you for the information - I noticed others used the mmap instead of sysfs and I wondered how much overhead was in the read/write/open functions. Can I ask what logic analyzer you’re using? I’ve been itching to get an oscilloscope, but even the cheapest is a few hundred.

Regards,
Rene

Have a look at the sigrok site. It’s a free signal analysis software suite and there’s a page showing all the logic analyzer hardware it works with. I bought a $10 Chinese equivalent to the Cypress CY7C68013A-56PVXC (FX2LP) board and it works well.