From f01bd0b472a58ec6b1606d643c8b8a41ea1bb597 Mon Sep 17 00:00:00 2001
From: Jean-Matthieu DECHRISTE <jeanmatthieu@dechriste.fr>
Date: Thu, 27 Jun 2019 18:03:21 +0200
Subject: [PATCH] Enables GPIO low accuracy interrupt using the PORT event and
 the pin SENSE register.

---
 cores/nRF5/WInterrupts.c | 85 ++++++++++++++++++++++++++++++++++------
 cores/nRF5/WInterrupts.h | 12 ++++++
 2 files changed, 86 insertions(+), 11 deletions(-)

diff --git a/cores/nRF5/WInterrupts.c b/cores/nRF5/WInterrupts.c
index 59cc3cb4..67de5e07 100644
--- a/cores/nRF5/WInterrupts.c
+++ b/cores/nRF5/WInterrupts.c
@@ -32,6 +32,7 @@
 
 static voidFuncPtr callbacksInt[NUMBER_OF_GPIO_TE];
 static int8_t channelMap[NUMBER_OF_GPIO_TE];
+static voidFuncPtr callbackLowAccuracyInt;
 static int enabled = 0;
 
 /* Configure I/O interrupt sources */
@@ -39,6 +40,7 @@ static void __initialize()
 {
   memset(callbacksInt, 0, sizeof(callbacksInt));
   memset(channelMap, -1, sizeof(channelMap));
+  callbackLowAccuracyInt = NULL;
 
   NVIC_DisableIRQ(GPIOTE_IRQn);
   NVIC_ClearPendingIRQ(GPIOTE_IRQn);
@@ -125,23 +127,84 @@ void detachInterrupt(uint32_t pin)
   }
 }
 
+/*
+ * \brief Specifies a named Interrupt Service Routine (ISR) to call when an interrupt occurs.
+ *        Replaces any previous function that was attached to the interrupt.
+ *        Only one callback can be assigned to the low accuracy interrupt AKA PORT event.
+ */
+void attachInterruptLowAccuracy(uint32_t pin, voidFuncPtr callback, uint32_t mode)
+{
+  if (!enabled) {
+    __initialize();
+    enabled = 1;
+  }
+
+  if (pin >= PINS_COUNT) {
+    return;
+  }
+
+  pin = g_ADigitalPinMap[pin];
+
+  uint32_t polarity;
+
+  switch (mode) {
+    case FALLING:
+      NRF_GPIO->PIN_CNF[pin] |= ((uint32_t)GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos);
+      break;
+
+    case RISING:
+      NRF_GPIO->PIN_CNF[pin] |= ((uint32_t)GPIO_PIN_CNF_SENSE_High << GPIO_PIN_CNF_SENSE_Pos);
+      break;
+
+    default:
+      return;
+  }
+ 
+  callbackLowAccuracyInt = callback;
+  NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_PORT_Msk;
+}
+
+/*
+ * \brief Turns off the given low accuracy interrupt.
+ */
+void detachInterruptLowAccuracy(uint32_t pin)
+{
+  if (pin >= PINS_COUNT) {
+    return;
+  }
+
+  pin = g_ADigitalPinMap[pin];
+
+  callbackLowAccuracyInt = NULL;
+
+  NRF_GPIO->PIN_CNF[pin] &= ~((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
+  NRF_GPIOTE->INTENCLR = GPIOTE_INTENCLR_PORT_Msk;
+}
+
 void GPIOTE_IRQHandler()
 {
-  uint32_t event = offsetof(NRF_GPIOTE_Type, EVENTS_IN[0]);
+  if(NRF_GPIOTE->EVENTS_PORT == 1 && (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_PORT_Msk)) {
+    if(callbackLowAccuracyInt) {
+      callbackLowAccuracyInt();
+    }
+    NRF_GPIOTE->EVENTS_PORT = 0;
+  } else {
+    uint32_t event = offsetof(NRF_GPIOTE_Type, EVENTS_IN[0]);
 
-  for (int ch = 0; ch < NUMBER_OF_GPIO_TE; ch++) {
-    if ((*(uint32_t *)((uint32_t)NRF_GPIOTE + event) == 0x1UL) && (NRF_GPIOTE->INTENSET & (1 << ch))) {
-      if (channelMap[ch] != -1 && callbacksInt[ch]) {
-        callbacksInt[ch]();
-      }
+    for (int ch = 0; ch < NUMBER_OF_GPIO_TE; ch++) {
+      if ((*(uint32_t *)((uint32_t)NRF_GPIOTE + event) == 0x1UL) && (NRF_GPIOTE->INTENSET & (1 << ch))) {
+        if (channelMap[ch] != -1 && callbacksInt[ch]) {
+          callbacksInt[ch]();
+        }
 
-    *(uint32_t *)((uint32_t)NRF_GPIOTE + event) = 0;
+      *(uint32_t *)((uint32_t)NRF_GPIOTE + event) = 0;
 #if __CORTEX_M == 0x04
-    volatile uint32_t dummy = *((volatile uint32_t *)((uint32_t)NRF_GPIOTE + event));
-    (void)dummy;
+      volatile uint32_t dummy = *((volatile uint32_t *)((uint32_t)NRF_GPIOTE + event));
+      (void)dummy;
 #endif
-    }
+      }
 
-    event = (uint32_t)((uint32_t)event + 4);
+      event = (uint32_t)((uint32_t)event + 4);
+    }
   }
 }
diff --git a/cores/nRF5/WInterrupts.h b/cores/nRF5/WInterrupts.h
index 5d2b24a0..273dc3a9 100644
--- a/cores/nRF5/WInterrupts.h
+++ b/cores/nRF5/WInterrupts.h
@@ -47,6 +47,18 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode);
  */
 void detachInterrupt(uint32_t pin);
 
+/*
+ * \brief Specifies a named Interrupt Service Routine (ISR) to call when an interrupt occurs.
+ *        Replaces any previous function that was attached to the interrupt.
+ *        Only one callback can be assigned to the low accuracy interrupt AKA PORT event.
+ */
+void attachInterruptLowAccuracy(uint32_t pin, voidFuncPtr callback, uint32_t mode);
+
+/*
+ * \brief Turns off the given low accuracy interrupt.
+ */
+void detachInterruptLowAccuracy(uint32_t pin);
+
 #ifdef __cplusplus
 }
 #endif