diff --git a/VERSION b/VERSION index 6324d40..3767b4b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.14 +3.14 \ No newline at end of file diff --git a/documentation/deutsch/functions.md b/documentation/deutsch/functions.md index 114faa3..0267556 100644 --- a/documentation/deutsch/functions.md +++ b/documentation/deutsch/functions.md @@ -20,13 +20,13 @@ sudo apt install git git clone https://github.com/WiringPi/WiringPi.git cd WiringPi ./build debian -mv debian-template/wiringpi-3.0-1.deb . +mv debian-template/wiringpi-3.13.deb . ``` **Debian-Paket installieren:** ```bash -sudo apt install ./wiringpi-3.0-1.deb +sudo apt install ./wiringpi-3.13.deb ``` **Debian-Paket deinstallieren:** @@ -291,21 +291,27 @@ Registriert eine Interrupt Service Routine (ISR) bzw. Funktion die bei Flankenwe >>> ```C -int wiringPiISR(int pin, int mode, void (*function)(void)); +int wiringPiISR(int pin, int edgeMode, void (*function)(unsigned int, long long int), unsigned long bouncetime); ``` -``pin``: Der gewünschte Pin (BCM-, WiringPi- oder Pin-Nummer). -``mode``: Auslösende Flankenmodus +``pin``: Der gewünschte Pin (BCM\-, WiringPi\- oder Pin\-Nummer). + +``edgeMode``: Auslösende Flankenmodus - INT_EDGE_RISING ... Steigende Flanke - INT_EDGE_FALLING ... Fallende Flanke - INT_EDGE_BOTH ... Steigende und fallende Flanke -``*function``: Funktionspointer für ISR +``*function``: Funktionspointer für ISR mit Rückgabeparameter pin und Zeitstempel: + > unsigned int: pin + > long long int: Zeitstempel + +``bouncetime``: Entprellzeit in Microsekunden, 0 ms schaltet das Entprellen ab + ``Rückgabewert``: > 0 ... Erfolgreich -Beispiel siehe wiringPiISRStop. +Beispiel siehe waitForInterrupt. ### wiringPiISRStop @@ -323,42 +329,148 @@ int wiringPiISRStop (int pin) -**Beispiel:** - -```C -static volatile int edgeCounter; - -static void isr(void) { - edgeCounter++; -} - -int main (void) { - wiringPiSetupPinType(WPI_PIN_BCM); - edgeCounter = 0; - wiringPiISR (17, INT_EDGE_RISING, &isr); - Sleep(1000); - printf("%d rinsing edges\n", ) - wiringPiISRStop(17) ; -} -``` - - ### waitForInterrupt -Wartet auf einen Aufruf der Interrupt Service Routine (ISR) mit Timeout. - +Wartet auf einen Aufruf der Interrupt Service Routine (ISR) mit Timeout und Entprellzeit in Mikrosekunden. +Blockiert das Programm bis zum Eintreffen der auslösenden Flanke oder bis zum Ablauf des Timeouts. >>> ```C -int waitForInterrupt (int pin, int mS) +long long int waitForInterrupt (int pin, int edgeMode, int mS, unsigned long bouncetime) ``` -``pin``: Der gewünschte Pin (BCM-, WiringPi- oder Pin-Nummer). -``mS``: Timeout in Milisekunden. -``Rückgabewert``: Fehler -> 0 ... Erfolgreich -> -1 ... GPIO Device Chip nicht erfolgreich geöffnet -> -2 ... ISR wurde nicht registriert (wiringPiISR muss aufgerufen werden) +``pin``: Der gewünschte Pin (BCM\-, WiringPi\- oder Pin\-Nummer). +``edgeMode``: Auslösende Flankenmodus + - INT_EDGE_RISING ... Steigende Flanke + - INT_EDGE_FALLING ... Fallende Flanke + - INT_EDGE_BOTH ... Steigende und fallende Flanke + +``mS``: Timeout in Milisekunden. + - \-1 warten ohne timeout + - 0 wartet nicht + - 1...n wartet maximal n mS Millisekunden + +``bouncetime``: Entprellzeit in Microsekunden, 0 schaltet Entprellen ab. + +``Rückgabewert``: +> \>0 ... Zeitstempel des Interrupt Ereignisses in Mikrosekunden +> 0 ... Timeout +> \-1 ... GPIO Device Chip nicht erfolgreich geöffnet +> \-2 ... ISR wurde nicht registriert + + +**Beispiel:** + +```C +/* + * isr_debounce.c: + * Wait for Interrupt test program WiringPi >=3.14 - ISR method + * + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define BOUNCETIME 3000 // microseconds +#define BOUNCETIME_WFI 300 +#define TIMEOUT 10000 +//************************************* +// BCM pins +// IRQpin : setup as input with internal pullup. Connected with push button to GND with 1K resistor in series. +// OUTpin : connected to a LED with 470 Ohm resistor in series to GND. Toggles LED with every push button pressed. +//************************************* +#define IRQpin 16 +#define OUTpin 12 + +int toggle = 0; + +/* + * myInterrupt: + ********************************************************************************* + */ + +static void wfi (unsigned int gpio, long long int timestamp) { +// struct timeval now; + long long int timenow, diff; + struct timespec curr; + + if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) { + printf("clock_gettime error"); + return; + } + + timenow = curr.tv_sec * 1000000LL + curr.tv_nsec/1000L; // convert to microseconds + diff = timenow - timestamp; + + printf("gpio BCM = %d, IRQ timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", gpio, timestamp, timenow, diff); + if (toggle == 0) { + digitalWrite (OUTpin, HIGH) ; + toggle = 1; + } + else { + digitalWrite (OUTpin, LOW) ; + toggle = 0; + } +} + + + +int main (void) +{ + int major, minor; + long long int ret; + + wiringPiVersion(&major, &minor); + + printf("\nISR debounce test (WiringPi %d.%d)\n\n", major, minor); + + wiringPiSetupGpio() ; + + pinMode(IRQpin, INPUT); + + // pull up/down mode (PUD_OFF, PUD_UP, PUD_DOWN) => down + pullUpDnControl(IRQpin, PUD_UP); + + pinMode(OUTpin, OUTPUT); + digitalWrite (OUTpin, LOW) ; + + printf("Testing waitForInterrupt IRQ @ GPIO%d, timeout is %d\n", IRQpin, TIMEOUT); + ret = waitForInterrupt(IRQpin, INT_EDGE_FALLING, TIMEOUT, BOUNCETIME_WFI ); + if (ret < 0) { + printf("waitForInterrupt returned error %lld\n", ret); + wiringPiISRStop (IRQpin) ; + pinMode(OUTpin, INPUT); + return 0; + } + else if (ret == 0) { + printf("waitForInterrupt timed out %lld\n\n", ret); + wiringPiISRStop (IRQpin) ; + } + else { + printf("waitForInterrupt: falling edge fired at %lld microseconds\n\n", ret); + wiringPiISRStop (IRQpin) ; + } + + printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d falling edge and bouncetime %d microseconds. Toggle LED @ OUTpin on IRQ.\n\n", IRQpin, IRQpin, BOUNCETIME, OUTpin); + printf("To stop program hit return key\n\n"); + + wiringPiISR (IRQpin, INT_EDGE_FALLING, &wfi, BOUNCETIME) ; + + getc(stdin); + + wiringPiISRStop (IRQpin) ; + pinMode(OUTpin, INPUT); + + return 0 ; +} +``` ## Hardware PWM (Pulsweitenmodulation) @@ -564,4 +676,4 @@ if (fd>0) { ## SPI - Bus -... \ No newline at end of file +... diff --git a/examples/isr_debounce.c b/examples/isr_debounce.c new file mode 100644 index 0000000..8ddc9c1 --- /dev/null +++ b/examples/isr_debounce.c @@ -0,0 +1,116 @@ +/* + * isr_debounce.c: + * Wait for Interrupt test program WiringPi >=3.13 - ISR method + * + * + */ + +#include +#include +#include +#include +#include + +#include + +#define BOUNCETIME 300 +#define TIMEOUT 10000 +//************************************* +// BCM pins +// IRQpin : setup as input with internal pullup. Connected with push button to GND with 1K resistor in series. +// OUTpin : connected to a LED with 470 Ohm resistor in series to GND. Toggles LED with every push button pressed. +//************************************* +#define IRQpin 16 +#define OUTpin 12 + +int toggle = 0; + +/* + * myInterrupt: + ********************************************************************************* + */ + +static void wfi (unsigned int gpio, long long int timestamp) { +// struct timeval now; + long long int timenow, diff; + struct timespec curr; + + if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) { + printf("clock_gettime error"); + return; + } + + timenow = curr.tv_sec * 1000000LL + curr.tv_nsec/1000L; // convert to microseconds + diff = timenow - timestamp; + + printf("gpio BCM = %d, IRQ timestamp = %lld microseconds, timenow = %lld, diff = %lld\n", gpio, timestamp, timenow, diff); + if (toggle == 0) { + digitalWrite (OUTpin, HIGH) ; + toggle = 1; + } + else { + digitalWrite (OUTpin, LOW) ; + toggle = 0; + } +} + + + +int main (void) +{ + int major, minor; + long long int ret; + + wiringPiVersion(&major, &minor); + + printf("\nISR debounce test (WiringPi %d.%d)\n\n", major, minor); + + wiringPiSetupGpio() ; + + pinMode(IRQpin, INPUT); + + // pull up/down mode (PUD_OFF, PUD_UP, PUD_DOWN) => down + pullUpDnControl(IRQpin, PUD_UP); + + pinMode(OUTpin, OUTPUT); + digitalWrite (OUTpin, LOW) ; + + // test waitForInterrupt + + ret = waitForInterruptInit (IRQpin, INT_EDGE_FALLING); + if (ret < 0) { + printf("waitForInterruptInit returned error %d\n", ret); + pinMode(OUTpin, INPUT); + return 0; + } + + printf("Testing waitForInterrupt IRQ @ GPIO%d, timeout is %d\n", IRQpin, TIMEOUT); + ret = waitForInterrupt(IRQpin, TIMEOUT, 0); + if (ret < 0) { + printf("waitForInterrupt returned error %lld\n", ret); + wiringPiISRStop (IRQpin) ; + pinMode(OUTpin, INPUT); + return 0; + } + else if (ret == 0) { + printf("waitForInterrupt timed out %lld\n\n", ret); + wiringPiISRStop (IRQpin) ; + } + else { + printf("waitForInterrupt: falling edge fired at %lld microseconds\n\n", ret); + wiringPiISRStop (IRQpin) ; + } + + printf("Testing IRQ @ GPIO%d with trigger @ GPIO%d falling edge and bouncetime %d ms. Toggle LED @ OUTpin on IRQ.\n\n", IRQpin, IRQpin, BOUNCETIME, OUTpin); + printf("To stop program hit return key\n\n"); + + wiringPiISR (IRQpin, INT_EDGE_FALLING, &wfi, BOUNCETIME) ; + +// sleep(30); + getc(stdin); + + wiringPiISRStop (IRQpin) ; + pinMode(OUTpin, INPUT); + + return 0 ; +} \ No newline at end of file diff --git a/gpio/gpio.c b/gpio/gpio.c index 3fc97a3..f79188b 100644 --- a/gpio/gpio.c +++ b/gpio/gpio.c @@ -177,7 +177,7 @@ void printgpio(const char* text) { } } -static void wfi (void) { +static void wfi (unsigned int pin, long long int timestamp) { globalCounter++; if(globalCounter>=iterations) { printgpio("finished\n"); @@ -217,7 +217,7 @@ void doWfi (int argc, char *argv []) timeoutSec = atoi(argv [5]); } - if (wiringPiISR (pin, mode, &wfi) < 0) + if (wiringPiISR (pin, mode, &wfi, 0) < 0) { fprintf (stderr, "%s: wfi: Unable to setup ISR: %s\n", argv [1], strerror (errno)) ; exit (1) ; diff --git a/wiringPi/wiringPi.c b/wiringPi/wiringPi.c index 7887633..1a013d0 100644 --- a/wiringPi/wiringPi.c +++ b/wiringPi/wiringPi.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -477,11 +478,14 @@ static int isrFds [64] = -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, } ; + + // ISR Data static int chipFd = -1; -static void (*isrFunctions [64])(void) ; +static void (*isrFunctions [64])(unsigned int, long long int) ; static pthread_t isrThreads[64]; -static int isrMode[64]; +static int isrMode[64]; // irq on rising/falling edge +static unsigned long isrDebouncePeriodUs [64]; // 0: debounce is off // Doing it the Arduino way with lookup tables... // Yes, it's probably more innefficient than all the bit-twidling, but it @@ -1753,11 +1757,14 @@ void releaseLine(int pin) { lineFlags[pin] = 0; close(lineFds[pin]); lineFds[pin] = -1; + isrDebouncePeriodUs[pin] = 0; } int requestLine(int pin, unsigned int lineRequestFlags) { - struct gpiohandle_request rq; - + struct gpio_v2_line_request req; + struct gpio_v2_line_config config; + int ret; + if (lineFds[pin]>=0) { if (lineRequestFlags == lineFlags[pin]) { //already requested @@ -1772,17 +1779,25 @@ int requestLine(int pin, unsigned int lineRequestFlags) { if (wiringPiGpioDeviceGetFd()<0) { return -1; // error } - rq.lineoffsets[0] = pin; - rq.lines = 1; - rq.flags = lineRequestFlags; - int ret = ioctl(chipFd, GPIO_GET_LINEHANDLE_IOCTL, &rq); - if (ret || rq.fd<0) { + + memset(&req, 0, sizeof(req)); + memset(&config, 0, sizeof(config)); + config.flags = lineRequestFlags; + strcpy(req.consumer, "wiringpi_gpio_req"); + + req.offsets[0] = pin; + req.num_lines = 1; + req.config = config; + + ret = ioctl(chipFd, GPIO_V2_GET_LINE_IOCTL, &req); + + if (ret || req.fd<0) { ReportDeviceError("get line handle", pin, "RequestLine", ret); return -1; // error } lineFlags[pin] = lineRequestFlags; - lineFds[pin] = rq.fd; + lineFds[pin] = req.fd; if (wiringPiDebug) printf ("requestLine succeeded: pin:%d, flags: %u, fd :%d\n", pin, lineRequestFlags, lineFds[pin]) ; return lineFds[pin]; @@ -1882,16 +1897,16 @@ void pinModeFlagsDevice (int pin, int mode, unsigned int flags) { if (wiringPiDebug) printf ("pinModeFlagsDevice: pin:%d mode:%d, flags: %u\n", pin, mode, flags) ; - lflag &= ~(GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_OUTPUT); + lflag &= ~(GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_OUTPUT); switch(mode) { default: fprintf(stderr, "pinMode: invalid mode request (only input und output supported)\n"); return; case INPUT: - lflag |= GPIOHANDLE_REQUEST_INPUT; + lflag |= GPIO_V2_LINE_FLAG_INPUT; break; case OUTPUT: - lflag |= GPIOHANDLE_REQUEST_OUTPUT; + lflag |= GPIO_V2_LINE_FLAG_OUTPUT; break; case PM_OFF: pinModeFlagsDevice(pin, INPUT, 0); @@ -2057,20 +2072,20 @@ void pinMode (int pin, int mode) */ void pullUpDnControlDevice (int pin, int pud) { unsigned int flag = lineFlags[pin]; - unsigned int biasflags = GPIOHANDLE_REQUEST_BIAS_DISABLE | GPIOHANDLE_REQUEST_BIAS_PULL_UP | GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; + unsigned int biasflags = GPIO_V2_LINE_FLAG_BIAS_DISABLED | GPIO_V2_LINE_FLAG_BIAS_PULL_UP | GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; flag &= ~biasflags; switch (pud){ - case PUD_OFF: flag |= GPIOHANDLE_REQUEST_BIAS_DISABLE; break; - case PUD_UP: flag |= GPIOHANDLE_REQUEST_BIAS_PULL_UP; break; - case PUD_DOWN: flag |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN; break; + case PUD_OFF: flag |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; break; + case PUD_UP: flag |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP; break; + case PUD_DOWN: flag |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; break; default: return ; /* An illegal value */ } // reset input/output - if (lineFlags[pin] & GPIOHANDLE_REQUEST_OUTPUT) { + if (lineFlags[pin] & GPIO_V2_LINE_FLAG_OUTPUT) { pinModeFlagsDevice (pin, OUTPUT, flag); - } else if(lineFlags[pin] & GPIOHANDLE_REQUEST_INPUT) { + } else if(lineFlags[pin] & GPIO_V2_LINE_FLAG_INPUT) { pinModeFlagsDevice (pin, INPUT, flag); } else { lineFlags[pin] = flag; // only store for later @@ -2154,7 +2169,33 @@ void pullUpDnControl (int pin, int pud) } } +/* + helper functions for gpio_v2_line_values bits +*/ +static inline void gpiotools_set_bit(__u64 *b, int n) +{ + *b |= _BITULL(n); +} +static inline void gpiotools_clear_bit(__u64 *b, int n) +{ + *b &= ~_BITULL(n); +} + +static inline void gpiotools_assign_bit(__u64 *b, int n, bool value) +{ + if (value) + gpiotools_set_bit(b, n); + else + gpiotools_clear_bit(b, n); +} + +static inline int gpiotools_test_bit(__u64 b, int n) +{ + return !!(b & _BITULL(n)); +} + +//********************************************* /* @@ -2164,19 +2205,23 @@ void pullUpDnControl (int pin, int pud) */ int digitalReadDevice (int pin) { // INPUT and OUTPUT should work - - if (lineFds[pin]<0) { + struct gpio_v2_line_values lv; + int ret; + + if (lineFds[pin]<0) { // line not requested - auto request on first read as input - pinModeDevice(pin, INPUT); + pinModeDevice(pin, INPUT); } + lv.mask = 0; + lv.bits = 0; if (lineFds[pin]>=0) { - struct gpiohandle_data data; - int ret = ioctl(lineFds[pin], GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + gpiotools_set_bit(&lv.mask, 0); + ret = ioctl(lineFds[pin], GPIO_V2_LINE_GET_VALUES_IOCTL, &lv); if (ret) { ReportDeviceError("get line values", pin, "digitalRead", ret); return LOW; // error } - return data.values[0]; + return gpiotools_test_bit(lv.bits, 0); } return LOW; // error , need to request line before } @@ -2258,7 +2303,9 @@ unsigned int digitalRead8 (int pin) */ void digitalWriteDevice (int pin, int value) { - + int ret; + struct gpio_v2_line_values values; + if (wiringPiDebug) printf ("digitalWriteDevice: ioctl pin:%d value: %d\n", pin, value) ; @@ -2266,22 +2313,25 @@ void digitalWriteDevice (int pin, int value) { // line not requested - auto request on first write as output pinModeDevice(pin, OUTPUT); } - if (lineFds[pin]>=0 && (lineFlags[pin] & GPIOHANDLE_REQUEST_OUTPUT)>0) { - struct gpiohandle_data data; - data.values[0] = value; - if (wiringPiDebug) - printf ("digitalWriteDevice: ioctl pin:%d cmd: GPIOHANDLE_SET_LINE_VALUES_IOCTL, value: %d\n", pin, value) ; - int ret = ioctl(lineFds[pin], GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); - if (ret) { - ReportDeviceError("set line values", pin, "digitalWrite", ret); - return; // error - } + + if (lineFds[pin]>=0 && (lineFlags[pin] & GPIO_V2_LINE_FLAG_OUTPUT)>0) { + values.mask = 0; + values.bits = 0; + gpiotools_set_bit(&values.mask, 0); + gpiotools_assign_bit(&values.bits, 0, !!value); + + ret = ioctl(lineFds[pin], GPIO_V2_LINE_SET_VALUES_IOCTL, &values); + if (ret == -1) { + ReportDeviceError("digitalWriteDevice", pin, "GPIO_V2_LINE_SET_VALUES_IOCTL", ret); + return; // error + } } else { fprintf(stderr, "digitalWrite: no output (%d)\n", lineFlags[pin]); } return; // error } + void digitalWrite (int pin, int value) { struct wiringPiNodeStruct *node = wiringPiNodes ; @@ -2579,63 +2629,23 @@ unsigned int digitalReadByte2 (void) } -/* - * waitForInterrupt: - * Pi Specific. - * Wait for Interrupt on a GPIO pin. - * This is actually done via the /dev/gpiochip interface regardless of - * the wiringPi access mode in-use. Maybe sometime it might get a better - * way for a bit more efficiency. - ********************************************************************************* - */ -int waitForInterrupt (int pin, int mS) -{ - int fd, ret; - struct pollfd polls ; - struct gpioevent_data evdata; - //struct gpio_v2_line_request req2; - if (wiringPiMode == WPI_MODE_PINS) - pin = pinToGpio [pin] ; - else if (wiringPiMode == WPI_MODE_PHYS) - pin = physToGpio [pin] ; +//-------------------------------------------------------------------- +// waitForInteruptInit +// Prepares waitForInterrupt for edgeMode, and debounce_period_us +// +// returns -1 on error +// returns 0 on success +//-------------------------------------------------------------------- - if ((fd = isrFds [pin]) == -1) - return -2 ; - - // Setup poll structure - polls.fd = fd; - polls.events = POLLIN | POLLERR ; - polls.revents = 0; - - // Wait for it ... - ret = poll(&polls, 1, mS); - if (ret <= 0) { - fprintf(stderr, "wiringPi: ERROR: poll returned=%d\n", ret); - } else { - //if (polls.revents & POLLIN) - if (wiringPiDebug) { - printf ("wiringPi: IRQ line %d received %d, fd=%d\n", pin, ret, isrFds[pin]) ; - } - /* read event data */ - int readret = read(isrFds [pin], &evdata, sizeof(evdata)); - if (readret == sizeof(evdata)) { - if (wiringPiDebug) { - printf ("wiringPi: IRQ data id: %d, timestamp: %lld\n", evdata.id, evdata.timestamp) ; - } - ret = evdata.id; - } else { - ret = 0; - } - } - return ret; -} - -int waitForInterruptInit (int pin, int mode) +int waitForInterruptInit (int pin, int edgeMode, unsigned long debounce_period_us) { const char* strmode = ""; - + struct gpio_v2_line_config config; + struct gpio_v2_line_request req; + int attr, ret; + if (wiringPiMode == WPI_MODE_PINS) { pin = pinToGpio [pin] ; } else if (wiringPiMode == WPI_MODE_PHYS) { @@ -2643,15 +2653,17 @@ int waitForInterruptInit (int pin, int mode) } /* open gpio */ - sleep(1); +// sleep(1); if (wiringPiGpioDeviceGetFd()<0) { return -1; } - - struct gpioevent_request req; - req.lineoffset = pin; - req.handleflags = GPIOHANDLE_REQUEST_INPUT; - switch(mode) { + + memset(&req, 0, sizeof(req)); + memset(&config, 0, sizeof(config)); + + /* setup config */ + config.flags = GPIO_V2_LINE_FLAG_INPUT; + switch(edgeMode) { default: case INT_EDGE_SETUP: if (wiringPiDebug) { @@ -2659,33 +2671,49 @@ int waitForInterruptInit (int pin, int mode) } return -1; case INT_EDGE_FALLING: - req.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; + config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; strmode = "falling"; break; case INT_EDGE_RISING: - req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; + config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; strmode = "rising"; break; case INT_EDGE_BOTH: - req.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; + config.flags |= (GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING); strmode = "both"; break; } - strncpy(req.consumer_label, "wiringpi_gpio_irq", sizeof(req.consumer_label) - 1); + strcpy(req.consumer, "wiringpi_gpio_irq"); + + if (debounce_period_us) { + attr = config.num_attrs; + config.num_attrs++; + gpiotools_set_bit(&config.attrs[attr].mask, 0); + config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + config.attrs[attr].attr.debounce_period_us = debounce_period_us; + } + + req.num_lines = 1; + req.offsets[0] = pin; + req.event_buffer_size = 32; + req.config = config; - //later implement GPIO_V2_GET_LINE_IOCTL req2 - int ret = ioctl(chipFd, GPIO_GET_LINEEVENT_IOCTL, &req); - if (ret) { - ReportDeviceError("get line event", pin , strmode, ret); + ret = ioctl(chipFd, GPIO_V2_GET_LINE_IOCTL, &req); + if (ret == -1) { + ReportDeviceError("GPIO_V2_GET_LINE_IOCTL", pin , strmode, ret); return -1; } + if (wiringPiDebug) { printf ("wiringPi: GPIO get line %d , mode %s succeded, fd=%d\n", pin, strmode, req.fd) ; } - - /* set event fd nonbloack read */ + int fd_line = req.fd; isrFds [pin] = fd_line; + isrDebouncePeriodUs[pin] = debounce_period_us; + +/* set event fd nonbloack read */ + /* int flags = fcntl(fd_line, F_GETFL); flags |= O_NONBLOCK; ret = fcntl(fd_line, F_SETFL, flags); @@ -2693,30 +2721,109 @@ int waitForInterruptInit (int pin, int mode) fprintf(stderr, "wiringPi: ERROR: fcntl set nonblock return=%d\n", ret); return -1; } - +*/ return 0; } +/* + * waitForInterrupt: + * Pi Specific. + * Wait for Interrupt on a GPIO pin. + * This is actually done via the /dev/gpiochip interface regardless of + * the wiringPi access mode in-use. Maybe sometime it might get a better + * way for a bit more efficiency. + * Returns timestamp in microseconds on interrupt + ********************************************************************************* + */ + +long long int waitForInterrupt (int pin, int edgeMode, int mS, unsigned long debounce_period_us) // ms < 0 wait infinite, = 0 return immediately, > 0 wait timeout +{ + long long int ret; + int fd; + struct pollfd polls ; + struct gpio_v2_line_event evdata; + + if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + + if (waitForInterruptInit (pin, edgeMode, debounce_period_us ) != 0) + return -1 ; + + fd = isrFds[pin]; + // Setup poll structure + polls.fd = fd; + polls.events = POLLIN | POLLERR ; + polls.revents = 0; + + ret = (long long int)poll(&polls, 1, mS); + + if (ret < 0) { + if (wiringPiDebug) + fprintf(stderr, "wiringPi: ERROR: poll returned=%lld\n", ret); + } else if (ret == 0) { + if (wiringPiDebug) + fprintf(stderr, "wiringPi: timeout: poll returned=%lld\n", ret); + } + else { + if (wiringPiDebug) + printf ("wiringPi: IRQ line %d received %lld, fd=%d\n", pin, ret, isrFds[pin]) ; + if (polls.revents & POLLIN) { + /* read event data */ + int readret = read(isrFds [pin], &evdata, sizeof(evdata)); + if (readret == sizeof(evdata)) { + if (wiringPiDebug) + printf ("wiringPi: IRQ at PIN: %d, timestamp: %lld\n", evdata.offset, evdata.timestamp_ns) ; + + switch (evdata.id) { + case GPIO_V2_LINE_EVENT_RISING_EDGE: + if (wiringPiDebug) + printf("wiringPi: rising edge\n"); + break; + case GPIO_V2_LINE_EVENT_FALLING_EDGE: + if (wiringPiDebug) + printf("wiringPi: falling edge\n"); + break; + default: + if (wiringPiDebug) + printf("wiringPi: unknown event\n"); + break; + } + ret = evdata.timestamp_ns / 1000LL; // nanoseconds u64 to microseconds + } + else { + ret = -1; + } + } + } + return ret; +} + + int waitForInterruptClose (int pin) { if (isrFds[pin]>0) { if (wiringPiDebug) { printf ("wiringPi: waitForInterruptClose close thread 0x%lX\n", (unsigned long)isrThreads[pin]) ; } - if (pthread_cancel(isrThreads[pin]) == 0) { - if (wiringPiDebug) { - printf ("wiringPi: waitForInterruptClose thread canceled successfuly\n") ; - } - } else { - if (wiringPiDebug) { - fprintf (stderr, "wiringPi: waitForInterruptClose could not cancel thread\n"); + if (isrThreads[pin] != 0) { + if (pthread_cancel(isrThreads[pin]) == 0) { + if (wiringPiDebug) { + printf ("wiringPi: waitForInterruptClose thread canceled successfuly\n") ; + } + } else { + if (wiringPiDebug) { + fprintf (stderr, "wiringPi: waitForInterruptClose could not cancel thread\n"); + } } } close(isrFds [pin]); } isrFds [pin] = -1; isrFunctions [pin] = NULL; - + isrDebouncePeriodUs[pin] = 0; + /* -not closing so far - other isr may be using it - only close if no other is using - will code later if (chipFd>0) { close(chipFd); @@ -2730,6 +2837,77 @@ int waitForInterruptClose (int pin) { } +/* + * wfi: + * Pi Specific. + * Wait for Interrupt on a GPIO pin, called from interruptHandler + * Returns timestamp in microseconds on interrupt + ********************************************************************************* + */ + +long long int wfi(int pin) +{ + long long int ret; + int fd; + struct pollfd polls ; + struct gpio_v2_line_event evdata; + + if (wiringPiMode == WPI_MODE_PINS) + pin = pinToGpio [pin] ; + else if (wiringPiMode == WPI_MODE_PHYS) + pin = physToGpio [pin] ; + + if ((fd = isrFds [pin]) == -1) + return -2 ; + + // Setup poll structure + polls.fd = fd; + polls.events = POLLIN | POLLERR ; + polls.revents = 0; + + ret = (long long int)poll(&polls, 1, -1); // no timeout, wait forevver + + if (ret < 0) { + if (wiringPiDebug) + fprintf(stderr, "wiringPi: ERROR: poll returned=%lld\n", ret); + } else if (ret == 0) { + if (wiringPiDebug) + fprintf(stderr, "wiringPi: timeout: poll returned=%lld\n", ret); + } + else { + if (wiringPiDebug) + printf ("wiringPi: IRQ line %d received %lld, fd=%d\n", pin, ret, isrFds[pin]) ; + if (polls.revents & POLLIN) { + /* read event data */ + int readret = read(isrFds [pin], &evdata, sizeof(evdata)); + if (readret == sizeof(evdata)) { + if (wiringPiDebug) + printf ("wiringPi: IRQ at PIN: %d, timestamp: %lld\n", evdata.offset, evdata.timestamp_ns) ; + + switch (evdata.id) { + case GPIO_V2_LINE_EVENT_RISING_EDGE: + if (wiringPiDebug) + printf("wiringPi: rising edge\n"); + break; + case GPIO_V2_LINE_EVENT_FALLING_EDGE: + if (wiringPiDebug) + printf("wiringPi: falling edge\n"); + break; + default: + if (wiringPiDebug) + printf("wiringPi: unknown event\n"); + break; + } + ret = evdata.timestamp_ns / 1000LL; // nanoseconds u64 to microseconds + } + else { + ret = -1; + } + } + } + return ret; +} + int wiringPiISRStop (int pin) { return waitForInterruptClose (pin); } @@ -2742,23 +2920,94 @@ int wiringPiISRStop (int pin) { ********************************************************************************* */ -static void *interruptHandler (UNU void *arg) +void *interruptHandler (void *arg) { - int pin ; + const char* strmode = ""; + int pin, mode ; + unsigned long debounce_period_us; + struct gpio_v2_line_config config; + struct gpio_v2_line_request req; + int attr, ret; + + pin = *(int *)arg; + +/* pin = pinPass ; + pinPass = -1 ; +*/ + if (wiringPiGpioDeviceGetFd()<0) { + return NULL; + } + + mode = isrMode[pin]; + debounce_period_us = isrDebouncePeriodUs[pin]; + + if (wiringPiDebug) { + printf ("interruptHandler: GPIO line %d, mode %d, debounce_period_us %lu \n", pin, mode, debounce_period_us) ; + } + + memset(&req, 0, sizeof(req)); + memset(&config, 0, sizeof(config)); + + /* setup config */ + config.flags = GPIO_V2_LINE_FLAG_INPUT; + switch(mode) { + default: + case INT_EDGE_SETUP: + if (wiringPiDebug) { + printf ("wiringPi: waitForInterruptMode mode INT_EDGE_SETUP - exiting\n") ; + } + return NULL; + case INT_EDGE_FALLING: + config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; + strmode = "falling"; + break; + case INT_EDGE_RISING: + config.flags |= GPIO_V2_LINE_FLAG_EDGE_RISING; + strmode = "rising"; + break; + case INT_EDGE_BOTH: + config.flags |= (GPIO_V2_LINE_FLAG_EDGE_FALLING | GPIO_V2_LINE_FLAG_EDGE_RISING); + strmode = "both"; + break; + } + strcpy(req.consumer, "wiringpi_gpio_irq"); + + if (debounce_period_us) { + attr = config.num_attrs; + config.num_attrs++; + gpiotools_set_bit(&config.attrs[attr].mask, 0); + config.attrs[attr].attr.id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + config.attrs[attr].attr.debounce_period_us = debounce_period_us; + } + + req.num_lines = 1; + req.offsets[0] = pin; + req.config = config; + ret = ioctl(chipFd, GPIO_V2_GET_LINE_IOCTL, &req); + if (ret == -1) { + ReportDeviceError("get line event", pin , strmode, ret); + return NULL; + } +/* + if (wiringPiDebug) { + printf ("wiringPi: GPIO get line %d , mode %s succeded, fd=%d\n", pin, strmode, req.fd) ; + } +*/ + /* set event fd */ + int fd_line = req.fd; + isrFds [pin] = fd_line; + (void)piHiPri (55) ; // Only effective if we run as root - pin = pinPass ; - pinPass = -1 ; - for (;;) { - int ret = waitForInterrupt(pin, -1); - if ( ret> 0) { + long long int ret = wfi(pin); // poll for event and return timestamp + if ( ret> 0) { // return timestamp in microseconds if (wiringPiDebug) { printf ("wiringPi: call function\n") ; } if(isrFunctions [pin]) { - isrFunctions [pin] () ; + isrFunctions [pin] ((unsigned int)pin, ret) ; } // wait again - in the past forever - now can be stopped by waitForInterruptClose } else if( ret< 0) { @@ -2779,10 +3028,11 @@ static void *interruptHandler (UNU void *arg) * Pi Specific. * Take the details and create an interrupt handler that will do a call- * back to the user supplied function. + * debounce_period_us in microseconds ********************************************************************************* */ -int wiringPiISR (int pin, int mode, void (*function)(void)) +int wiringPiISR (int pin, int edgeMode, void (*function)(unsigned int, long long int), unsigned long debounce_period_us) { const int maxpin = GetMaxPin(); @@ -2790,21 +3040,24 @@ int wiringPiISR (int pin, int mode, void (*function)(void)) return wiringPiFailure (WPI_FATAL, "wiringPiISR: pin must be 0-%d (%d)\n", maxpin, pin) ; if (wiringPiMode == WPI_MODE_UNINITIALISED) return wiringPiFailure (WPI_FATAL, "wiringPiISR: wiringPi has not been initialised. Unable to continue.\n") ; + + if (wiringPiMode == WPI_MODE_PINS) { + pin = pinToGpio [pin] ; + } else if (wiringPiMode == WPI_MODE_PHYS) { + pin = physToGpio [pin] ; + } + if (wiringPiDebug) { - printf ("wiringPi: wiringPiISR pin %d, mode %d\n", pin, mode) ; + printf ("wiringPi: wiringPiISR pin %d, edgeMode %d\n", pin, edgeMode) ; } if (isrFunctions [pin]) { - printf ("wiringPi: ISR function alread active, ignoring \n") ; + printf ("wiringPi: ISR function already active, ignoring \n") ; } isrFunctions [pin] = function ; - isrMode[pin] = mode; - if(waitForInterruptInit (pin, mode)<0) { - if (wiringPiDebug) { - fprintf (stderr, "wiringPi: waitForInterruptInit failed\n") ; - } - }; - + isrMode[pin] = edgeMode; + isrDebouncePeriodUs[pin] = debounce_period_us; + if (wiringPiDebug) { printf ("wiringPi: mutex in\n") ; } @@ -2813,12 +3066,17 @@ int wiringPiISR (int pin, int mode, void (*function)(void)) if (wiringPiDebug) { printf("wiringPi: pthread_create before 0x%lX\n", (unsigned long)isrThreads[pin]); } - if (pthread_create (&isrThreads[pin], NULL, interruptHandler, NULL)==0) { + if (pthread_create (&isrThreads[pin], NULL, interruptHandler, &pin)==0) { if (wiringPiDebug) { printf("wiringPi: pthread_create successed, 0x%lX\n", (unsigned long)isrThreads[pin]); } - while (pinPass != -1) - delay (1) ; +/* while (pinPass != -1) + delay (1) ; */ + // wait so that interruptHandler is up und running. + // when interruptHandler is running, the calling function wiringPiISR + // must be still alive, otherwise the thread argument &pin points into nirwana, + // when it is picked up from interruptHandler. + delay (10); } else { if (wiringPiDebug) { printf("wiringPi: pthread_create failed\n"); @@ -3170,12 +3428,12 @@ int wiringPiSetup (void) /**/ if (piGpioLayout () == GPIO_LAYOUT_PI1_REV1) // A, B, Rev 1, 1.1 { - pinToGpio = pinToGpioR1 ; + pinToGpio = pinToGpioR1 ; physToGpio = physToGpioR1 ; } else // A2, B2, A+, B+, CM, Pi2, Pi3, Zero, Zero W, Zero 2 W { - pinToGpio = pinToGpioR2 ; + pinToGpio = pinToGpioR2 ; physToGpio = physToGpioR2 ; } @@ -3184,7 +3442,7 @@ int wiringPiSetup (void) // Try /dev/mem. If that fails, then // try /dev/gpiomem. If that fails then game over. - const char* gpiomemGlobal = gpiomem_global; + const char* gpiomemGlobal = gpiomem_global; const char* gpiomemModule = gpiomem_BCM; if (piRP1Model()) { @@ -3225,7 +3483,7 @@ int wiringPiSetup (void) printf ("wiringPi: access to %s succeded %d\n", usingGpioMem ? gpiomemModule : gpiomemGlobal, fd) ; } // GPIO: - if (!piRP1Model()) { + if (!piRP1Model()) { //Set the offsets into the memory interface. GPIO_PADS = piGpioBase + 0x00100000 ; diff --git a/wiringPi/wiringPi.h b/wiringPi/wiringPi.h index f2a4599..964fed3 100644 --- a/wiringPi/wiringPi.h +++ b/wiringPi/wiringPi.h @@ -298,8 +298,8 @@ extern void digitalWriteByte2 (int value) ; // Interrupts // (Also Pi hardware specific) -extern int waitForInterrupt (int pin, int mS) ; -extern int wiringPiISR (int pin, int mode, void (*function)(void)) ; +extern long long int waitForInterrupt (int pin, int edgeMode, int mS, unsigned long debounce_period_us) ; // V3.14 phylax +extern int wiringPiISR (int pin, int mode, void (*function)(unsigned int, long long int), unsigned long debounce_period_us) ; // v3.14 phylax extern int wiringPiISRStop (int pin) ; //V3.2 extern int waitForInterruptClose(int pin) ; //V3.2