Lancaster University's (short term!) clone of mbed-src for micro:bit. This is a copy of the github branch https://github.com/lancaster-university/mbed-classic

Fork of mbed-src by mbed official

Files at this revision

API Documentation at this revision

Comitter:
LancasterUniversity
Date:
Thu Apr 07 01:03:20 2016 +0100
Parent:
635:a11c0372f0ba
Child:
637:70e5019e2e93
Commit message:
Synchronized with git rev 6888edef
Author: James Devine
updated implementation for us_ticker.c

We are now using a timer that uses HFCLK for timer calls
this should mean that we have a higher resolution for
interrupts.

Changed in this revision

targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/us_ticker.c Show annotated file Show diff for this revision Revisions of this file
--- a/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/us_ticker.c	Wed Sep 30 17:00:09 2015 +0100
+++ b/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/us_ticker.c	Thu Apr 07 01:03:20 2016 +0100
@@ -21,120 +21,134 @@
 #include "nrf_delay.h"
 
 /*
- * Note: The micro-second timer API on the nRF51 platform is implemented using
- * the RTC counter run at 32kHz (sourced from an external oscillator). This is
- * a trade-off between precision and power. Running a normal 32-bit MCU counter
- * at high frequency causes the average power consumption to rise to a few
- * hundred micro-amps, which is prohibitive for typical low-power BLE
- * applications.
- * A 32kHz clock doesn't offer the precision needed for keeping u-second time,
- * but we're assuming that this will not be a problem for the average user.
+ * A higher precision implementation of the Ticker API using Timer 1 and a 1MHz clock.
+ * This implementation should be used when higher precision than can be provided by the
+ * LF clock source is required. i.e. precision of less than 30uS.
  */
 
-#define MAX_RTC_COUNTER_VAL     0x00FFFFFF               /**< Maximum value of the RTC counter. */
-#define RTC_CLOCK_FREQ          (uint32_t)(32768)
-#define RTC1_IRQ_PRI            3                        /**< Priority of the RTC1 interrupt (used
+#define MAX_TMR1_COUNTER_VAL     0x0000FFFF               // Run the timer in 16 bit mode, for consistency with RTC algorithm above.
+#define TMR1_CLOCK_FREQ          (uint32_t)(1000000)	  // Run at 1MHz so the lower power 1MHz clock source can be used.
+#define TMR1_IRQ_PRI            1                        /**< Priority of the TMR1 interrupt (used
                                                           *  for checking for timeouts and executing
                                                           *  timeout handlers). This must be the same
                                                           *  as APP_IRQ_PRIORITY_LOW; taken from the
                                                           *  Nordic SDK. */
-#define MAX_RTC_TASKS_DELAY     47                       /**< Maximum delay until an RTC task is executed. */
+#define MAX_TMR1_TASKS_DELAY     47                       /**< Maximum delay until an RTC task is executed. */
 
-#define FUZZY_RTC_TICKS          2  /* RTC COMPARE occurs when a CC register is N and the RTC
+#define FUZZY_TMR1_TICKS          10  /* TMR COMPARE occurs when a CC register is N and the TMR
                                      * COUNTER value transitions from N-1 to N. If we're trying to
                                      * setup a callback for a time which will arrive very shortly,
                                      * there are limits to how short the callback interval may be for us
-                                     * to rely upon the RTC Compare trigger. If the COUNTER is N,
+                                     * to rely upon the TMR Compare trigger. If the COUNTER is N,
                                      * writing N+2 to a CC register is guaranteed to trigger a COMPARE
                                      * event at N+2. */
 
-#define RTC_UNITS_TO_MICROSECONDS(RTC_UNITS) (((RTC_UNITS) * (uint64_t)1000000) / RTC_CLOCK_FREQ)
-#define MICROSECONDS_TO_RTC_UNITS(MICROS)    ((((uint64_t)(MICROS) * RTC_CLOCK_FREQ) + 999999) / 1000000)
+#define TMR1_UNITS_TO_MICROSECONDS(TMR1_UNITS) TMR1_UNITS
+#define MICROSECONDS_TO_TMR1_UNITS(MICROS)    MICROS
 
 static bool              us_ticker_inited = false;
-static volatile uint32_t overflowCount;                   /**< The number of times the 24-bit RTC counter has overflowed. */
+static volatile uint32_t overflowCount;                   /**< The number of times the 24-bit TMR1 counter has overflowed. */
 static volatile bool     us_ticker_callbackPending = false;
 static uint32_t          us_ticker_callbackTimestamp;
 
-static inline void rtc1_enableCompareInterrupt(void)
+static inline void tmr1_enableCompareInterrupt(void)
 {
-    NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
-    NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
+    NRF_TIMER1->INTENSET    = TIMER_INTENSET_COMPARE0_Msk;
+
+    //NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
+    //NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
+}
+
+static inline void tmr1_disableCompareInterrupt(void)
+{
+    NRF_TIMER1->INTENCLR    = TIMER_INTENSET_COMPARE0_Msk;
+
+    //NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
+    //NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
 }
 
-static inline void rtc1_disableCompareInterrupt(void)
+static inline void tmr1_enableOverflowInterrupt(void)
 {
-    NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
-    NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
+	NRF_TIMER1->CC[3] = 0;
+	NRF_TIMER1->INTENSET = (TIMER_INTENSET_COMPARE3_Enabled << TIMER_INTENSET_COMPARE3_Pos);
+
+    //NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
+    //NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
 }
 
-static inline void rtc1_enableOverflowInterrupt(void)
+static inline void tmr1_disableOverflowInterrupt(void)
 {
-    NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
-    NRF_RTC1->INTENSET = RTC_INTENSET_OVRFLW_Msk;
-}
+	NRF_TIMER1->INTENCLR = (TIMER_INTENSET_COMPARE3_Enabled << TIMER_INTENSET_COMPARE3_Pos);
 
-static inline void rtc1_disableOverflowInterrupt(void)
-{
-    NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
-    NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
+    //NRF_RTC1->INTENCLR = RTC_INTENSET_OVRFLW_Msk;
+    //NRF_RTC1->EVTENCLR = RTC_EVTEN_OVRFLW_Msk;
 }
 
 static inline void invokeCallback(void)
 {
     us_ticker_callbackPending = false;
-    rtc1_disableCompareInterrupt();
+    //tmr1_disableCompareInterrupt();
     us_ticker_irq_handler();
 }
 
 /**
- * @brief Function for starting the RTC1 timer. The RTC timer is expected to
+ * @brief Function for starting the TIMER1 timer. The timer is expected to
  * keep running--some interrupts may be disabled temporarily.
  */
-static void rtc1_start()
+static void tmr1_start()
 {
-    NRF_RTC1->PRESCALER = 0; /* for no pre-scaling. */
+	NRF_TIMER1->TASKS_STOP = 1;
 
-    rtc1_enableOverflowInterrupt();
+	NRF_CLOCK->EVENTS_HFCLKSTARTED  = 0;
+	NRF_CLOCK->TASKS_HFCLKSTART     = 1;
+	while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;
 
-    NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
-    NVIC_ClearPendingIRQ(RTC1_IRQn);
-    NVIC_EnableIRQ(RTC1_IRQn);
+	NRF_TIMER1->MODE      = TIMER_MODE_MODE_Timer;
+	NRF_TIMER1->TASKS_CLEAR = 1;
+	NRF_TIMER1->PRESCALER = 4; 		// Configure for 1MHz operation
+	NRF_TIMER1->BITMODE   = TIMER_BITMODE_BITMODE_16Bit;
+	tmr1_enableOverflowInterrupt();
+    tmr1_enableCompareInterrupt();
 
-    NRF_RTC1->TASKS_START = 1;
-    nrf_delay_us(MAX_RTC_TASKS_DELAY);
+    NVIC_SetPriority(TIMER1_IRQn, TMR1_IRQ_PRI);
+    NVIC_ClearPendingIRQ(TIMER1_IRQn);
+    NVIC_EnableIRQ(TIMER1_IRQn);
+
+    NRF_TIMER1->TASKS_START = 1;
 }
 
 /**
  * @brief Function for stopping the RTC1 timer. We don't expect to call this.
  */
-void rtc1_stop(void)
+void tmr1_stop(void)
 {
-    NVIC_DisableIRQ(RTC1_IRQn);
-    rtc1_disableCompareInterrupt();
-    rtc1_disableOverflowInterrupt();
+    NVIC_DisableIRQ(TIMER1_IRQn);
+    tmr1_disableCompareInterrupt();
+    tmr1_disableOverflowInterrupt();
 
-    NRF_RTC1->TASKS_STOP = 1;
-    nrf_delay_us(MAX_RTC_TASKS_DELAY);
+    NRF_TIMER1->TASKS_STOP = 1;
+    nrf_delay_us(MAX_TMR1_TASKS_DELAY);
 
-    NRF_RTC1->TASKS_CLEAR = 1;
-    nrf_delay_us(MAX_RTC_TASKS_DELAY);
+    NRF_TIMER1->TASKS_CLEAR = 1;
+    nrf_delay_us(MAX_TMR1_TASKS_DELAY);
 }
 
 /**
  * @brief Function for returning the current value of the RTC1 counter.
  *
- * @return Current RTC1 counter as a 64-bit value with 56-bit precision (even
- *         though the underlying counter is 24-bit)
+ * @return Current RTC1 counter as a 64-bit value with 48-bit precision (even
+ *         though the underlying counter is 16-bit)
  */
-static inline uint64_t rtc1_getCounter64(void)
+static inline uint64_t tmr1_getCounter64(void)
 {
-    if (NRF_RTC1->EVENTS_OVRFLW) {
+    if (NRF_TIMER1->EVENTS_COMPARE[3]) {
         overflowCount++;
-        NRF_RTC1->EVENTS_OVRFLW = 0;
-        NRF_RTC1->EVTENCLR      = RTC_EVTEN_OVRFLW_Msk;
+        NRF_TIMER1->EVENTS_COMPARE[3] = 0;
     }
-    return ((uint64_t)overflowCount << 24) | NRF_RTC1->COUNTER;
+
+    NRF_TIMER1->TASKS_CAPTURE[2] = 1;
+
+    return ((uint64_t)overflowCount << 16) | (NRF_TIMER1->CC[2] & MAX_TMR1_COUNTER_VAL);
 }
 
 /**
@@ -142,9 +156,9 @@
  *
  * @return Current RTC1 counter as a 32-bit value (even though the underlying counter is 24-bit)
  */
-static inline uint32_t rtc1_getCounter(void)
+static inline uint32_t tmr1_getCounter(void)
 {
-    return rtc1_getCounter64();
+    return tmr1_getCounter64();
 }
 
 /**
@@ -152,18 +166,19 @@
  *
  * @details Checks for timeouts, and executes timeout handlers for expired timers.
  */
-void RTC1_IRQHandler(void)
+void TIMER1_IRQHandler(void)
 {
-    if (NRF_RTC1->EVENTS_OVRFLW) {
+    if (NRF_TIMER1->EVENTS_COMPARE[0])
+	{
+		NRF_TIMER1->EVENTS_COMPARE[0] = 0;
+		if (us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - tmr1_getCounter()) <= 0)) {
+			invokeCallback();
+		}
+	}
+
+    if (NRF_TIMER1->EVENTS_COMPARE[3]) {
         overflowCount++;
-        NRF_RTC1->EVENTS_OVRFLW = 0;
-        NRF_RTC1->EVTENCLR      = RTC_EVTEN_OVRFLW_Msk;
-    }
-    if (NRF_RTC1->EVENTS_COMPARE[0]) {
-        NRF_RTC1->EVENTS_COMPARE[0] = 0;
-        NRF_RTC1->EVTENCLR          = RTC_EVTEN_COMPARE0_Msk;
-        if (us_ticker_callbackPending && ((int)(us_ticker_callbackTimestamp - rtc1_getCounter()) <= 0))
-            invokeCallback();
+        NRF_TIMER1->EVENTS_COMPARE[3] = 0;
     }
 }
 
@@ -173,7 +188,7 @@
         return;
     }
 
-    rtc1_start();
+    tmr1_start();
     us_ticker_inited = true;
 }
 
@@ -183,9 +198,7 @@
         us_ticker_init();
     }
 
-    /* Return a pseudo microsecond counter value. This is only as precise as the
-     * 32khz low-freq clock source, but could be adequate.*/
-    return RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
+    return TMR1_UNITS_TO_MICROSECONDS(tmr1_getCounter64());
 }
 
 /**
@@ -229,12 +242,17 @@
      * additional 32 bits. RTC_UNITS_TO_MICROSECONDS() converts this into
      * microsecond units (in 64-bits).
      */
-    const uint64_t currentTime64 = RTC_UNITS_TO_MICROSECONDS(rtc1_getCounter64());
+
+	/*
+    const uint64_t currentTime64 = TMR1_UNITS_TO_MICROSECONDS(tmr1_getCounter64());
     uint64_t timestamp64 = (currentTime64 & ~(uint64_t)0xFFFFFFFFULL) + timestamp;
     if (((uint32_t)currentTime64 > 0x80000000) && (timestamp < 0x80000000)) {
         timestamp64 += (uint64_t)0x100000000ULL;
     }
-    uint32_t newCallbackTime = MICROSECONDS_TO_RTC_UNITS(timestamp64);
+    uint32_t newCallbackTime = MICROSECONDS_TO_TMR1_UNITS(timestamp64);
+	*/
+
+    uint32_t newCallbackTime = timestamp;
 
     /* Check for repeat setup of an existing callback. This is actually not
      * important; the following code should work even without this check. */
@@ -246,28 +264,26 @@
      * Even if they are immediately pending, they are scheduled to trigger a few
      * ticks later. This keeps things simple by invoking the callback from an
      * independent interrupt context. */
-    if ((int)(newCallbackTime - rtc1_getCounter()) <= (int)FUZZY_RTC_TICKS) {
-        newCallbackTime = rtc1_getCounter() + FUZZY_RTC_TICKS;
+    if ((int)(newCallbackTime - tmr1_getCounter()) <= (int)FUZZY_TMR1_TICKS) {
+        newCallbackTime = tmr1_getCounter() + FUZZY_TMR1_TICKS;
     }
 
-    NRF_RTC1->CC[0]             = newCallbackTime & MAX_RTC_COUNTER_VAL;
     us_ticker_callbackTimestamp = newCallbackTime;
-    if (!us_ticker_callbackPending) {
-        us_ticker_callbackPending = true;
-        rtc1_enableCompareInterrupt();
-    }
+    us_ticker_callbackPending = true;
+    NRF_TIMER1->CC[0]             = newCallbackTime & MAX_TMR1_COUNTER_VAL;
+    //tmr1_enableCompareInterrupt();
 }
 
 void us_ticker_disable_interrupt(void)
 {
-    if (us_ticker_callbackPending) {
-        rtc1_disableCompareInterrupt();
-        us_ticker_callbackPending = false;
-    }
+    //if (us_ticker_callbackPending) {
+    //    //tmr1_disableCompareInterrupt();
+    //    us_ticker_callbackPending = false;
+    //}
 }
 
 void us_ticker_clear_interrupt(void)
 {
-    NRF_RTC1->EVENTS_OVRFLW     = 0;
-    NRF_RTC1->EVENTS_COMPARE[0] = 0;
+    //NRF_TIMER1->EVENTS_COMPARE[3] = 0;
+    //NRF_TIMER1->EVENTS_COMPARE[0] = 0;
 }