I have problems with consecutive interrupts (edge triggered) on two different GPIO pins. In my application, the second interrupt often appears about 5 µs after the first interrupt. When the 2nd IRQ (e.g GPIO6) arrives exactly when the 1st IRQ (e.g. GPIO5) is handled, the 2nd IRQ will sometimes not be raised (depending on the timing). If the 2nd IRQ appears earlier or later than 5 µs, everything is fine.
After some days of investigation I'm quite sure, that the GPEDS register of the GPIO controller is not implemented correctly (controller bug). Everytime the Linux driver (pinctrl-bcm2835.c) "acks" an interrupt by writing to GPEDS0, a simultaneous IRQ on another GPIO pin will be lost. Although the problematic "window" may be only one clock cycle long, I can easily reproduce this problem within a few minutes.
For demonstration purposes, I've written a small test case (only the RPi 3B and a piece of wire is required). In this test case, I try to activate simultaneous access (setting one event and reset another one) of the GPEDS0 register. Setting is done via external GPIO loop (GPIO output --> IRQ interrupt). Resetting is done via a C application.
Test setup:
1. Make sure that GPIO2 and GPIO3 are not used (probably disable I2C in /boot/firmware/config.txt and reboot)
2. Connect GPIO2 and GPIO3 with an external piece of wire
3. Configure GPIO2 and GPIO3 via sysfs
Code: Select all
cd /sys/class/gpio/
echo 2 > export
echo out > gpio2/direction
echo 3 > export
echo falling > gpio3/edge
Code: Select all
grep gpiolib /proc/interrupts
169: 0 0 0 0 pinctrl-bcm2835 3 Edge gpiolib
Code: Select all
I=0 && while [ $I -lt 100 ]; do echo 1 > gpio2/value && echo 0 > gpio2/value && I=$(($I+1)) && sleep 0.1; done
Code: Select all
grep gpiolib /proc/interrupts
169: 100 0 0 0 pinctrl-bcm2835 3 Edge gpiolib
Code: Select all
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
static const uint32_t gpioGpeds0 = 0x40 / sizeof(uint32_t);
int fd = open("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC);
if (fd < 0)
{
fprintf(stderr, "Cannot open /dev/gpiomem: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
uint32_t *gpio = (uint32_t *)mmap(NULL, 4*1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x00200000);
if (gpio == MAP_FAILED)
{
fprintf(stderr, "mmap() failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
while (1)
{
/* Reset Event Detect Status for bit 1 (you can try every bit, even writing 0 will work)*/
*(gpio + gpioGpeds0) = (1 << 0);
}
}
Code: Select all
I=0 && while [ $I -lt 100 ]; do echo 1 > gpio2/value && echo 0 > gpio2/value && I=$(($I+1)) && sleep 0.1; done
Code: Select all
grep gpiolib /proc/interrupts
169: 148 0 0 0 pinctrl-bcm2835 3 Edge gpiolib