Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: Hobbyking_Cheetah_Compact Hobbyking_Cheetah_Compact_DRV8323_14bit Hobbyking_Cheetah_Compact_DRV8323_V51_201907 HKC_MiniCheetah ... more
Fork of mbed-dev by
Diff: targets/TARGET_ublox/TARGET_HI2110/us_ticker.c
- Revision:
- 150:02e0a0aed4ec
- Child:
- 160:d5399cc887bb
diff -r 156823d33999 -r 02e0a0aed4ec targets/TARGET_ublox/TARGET_HI2110/us_ticker.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/TARGET_ublox/TARGET_HI2110/us_ticker.c Tue Nov 08 17:45:16 2016 +0000
@@ -0,0 +1,247 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2016 u-blox
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* The usecond ticker is mapped to TIMER0. A few issues must be dealt
+ * with in this driver:
+ *
+ * 1. The us_ticker API must count upwards, not down.
+ * 2. The expected range/resolution is 32 bits each of 1 usecond,
+ * whereas TIMER0 runs at 48 MHz (not 1 MHz) and so actually
+ * has a range/resolution of 26 bits at 0.02 useconds. Software
+ * has to compensate for this.
+ */
+
+#include "us_ticker_api.h"
+#include "critical.h"
+
+/* ----------------------------------------------------------------
+ * MACROS
+ * ----------------------------------------------------------------*/
+
+/* TIMER0 clock is 48 MHz */
+#define CLOCK_TICKS_PER_US 48
+
+/* The number of clock ticks in a full-run of
+ * TIMER0, scaled to represent useconds */
+#define USECONDS_PER_FULL_TIMER0_RUN 89478485
+
+/* ----------------------------------------------------------------
+ * TYPES
+ * ----------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------
+ * GLOBAL VARIABLES
+ * ----------------------------------------------------------------*/
+
+/* Are we ready? */
+static bool g_initialised = false;
+
+/* Keep track of the number of useconds elapsed. */
+static uint32_t g_us_overflow = 0;
+
+/* The number of useconds to increment the by at each interrupt */
+static uint32_t g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
+
+/* Keep track of extra loops required to represent a particular time
+ * as the HW timer runs faster than 1 MHz */
+static uint32_t g_timer_extra_loops_required = 0;
+static uint32_t g_timer_extra_loops_done = 0;
+
+/* Keep track of any adjustment due to user interrupts . */
+static uint32_t g_user_interrupt_offset = 0;
+
+/* Flag that a user timer is running */
+static bool g_user_interrupt = false;
+
+/* ----------------------------------------------------------------
+ * FUNCTION PROTOTYPES
+ * ----------------------------------------------------------------*/
+
+static inline uint32_t divide_by_48(uint32_t x);
+
+/* ----------------------------------------------------------------
+ * NON-API FUNCTIONS
+ * ----------------------------------------------------------------*/
+
+/* Perform a divide-by-48 operation.
+ * This is done as a multiply-shift operation to take advantage of
+ * the ARM 32 bit single-cycle multiply and avoid using division;
+ * 1/48 is equivalent to 1365/2^16. It is also done in two halves
+ * to make sure that the multiplies fit into 32 bits.
+ *
+ * The principle is:
+ * - divide the top 16 bits by 48 using multiply-shift (=> x1),
+ * - work out the remainder of that operation and divide that by 48 (=> x1r),
+ * - divide the bottom 16 bits by 48 using multiply-shift (=> x2),
+ * - add the lot together to get the result.
+ *
+ * The cost is 29 instructions.
+ */
+static inline uint32_t divide_by_48(uint32_t x)
+{
+ uint32_t x1 = ((x >> 16) * 1365) >> 16;
+ uint32_t x1r = ((x & 0xFFFF0000) - ((x1 * 48) << 16));
+ x1r = (x1r * 1365) >> 16;
+ uint32_t x2 = ((x & 0xFFFF) * 1365) >> 16;
+
+ return (x1 << 16) + x1r + x2;
+}
+
+/* Timer0 handler */
+void IRQ1_TMR0_Handler(void)
+{
+ if (g_initialised) {
+ /* Increment the overflow count and set the increment
+ * value for next time */
+ g_us_overflow += g_us_overflow_increment;
+ g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
+
+ /* Now handle the user interrupt case */
+ if (g_user_interrupt) {
+ if (g_timer_extra_loops_done < g_timer_extra_loops_required) {
+ /* Let the timer go round again */
+ g_timer_extra_loops_done++;
+ } else {
+ /* We've done with looping around for a user interrupt */
+ g_user_interrupt = false;
+
+ /* Call the mbed API */
+ us_ticker_irq_handler();
+ }
+ }
+ }
+
+ NVIC_ClearPendingIRQ(Timer_IRQn);
+}
+
+/* ----------------------------------------------------------------
+ * MBED API CALLS
+ * ----------------------------------------------------------------*/
+
+void us_ticker_init(void)
+{
+ if (!g_initialised) {
+ /* Reset the globals */
+ g_timer_extra_loops_done = 0;
+ g_timer_extra_loops_required = 0;
+ g_us_overflow = 0;
+ g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
+ g_user_interrupt_offset = 0;
+ g_user_interrupt = false;
+
+ /* Get the timer running (starting at what is zero,
+ * once inverted), with repeat */
+ NVIC_ClearPendingIRQ(Timer_IRQn);
+ TIMER0_LOAD = 0xFFFFFFFF;
+ TIMER0_CTRL = 0x03;
+ NVIC_EnableIRQ(Timer_IRQn);
+
+ g_initialised = true;
+ }
+}
+
+uint32_t us_ticker_read()
+{
+ uint32_t timeValue;
+
+ /* This can be called before initialisation has been performed */
+ if (!g_initialised) {
+ us_ticker_init();
+ }
+
+ /* Disable interrupts to avoid collisions */
+ core_util_critical_section_enter();
+
+ /* Get the timer value, adding the offset in case we've been moved
+ * around by user activity, inverting it (as a count-up timer is
+ * expected), then scaling it to useconds and finally adding the
+ * usecond overflow value to make up the 32-bit usecond total */
+ timeValue = divide_by_48(~(TIMER0_TIME + g_user_interrupt_offset)) + g_us_overflow;
+
+ /* Put interrupts back */
+ core_util_critical_section_exit();
+
+ return timeValue;
+}
+
+/* NOTE: it seems to be an accepted fact that users
+ * will never ask for a timeout of more than 2^31 useconds
+ * and hence it's possible to do signed arithmetic
+ */
+void us_ticker_set_interrupt(timestamp_t timestamp)
+{
+ g_timer_extra_loops_required = 0;
+ g_timer_extra_loops_done = 0;
+ int32_t timeDelta;
+
+ /* Disable interrupts to avoid collisions */
+ core_util_critical_section_enter();
+
+ /* Establish how far we're being asked to move */
+ timeDelta = (int32_t) ((uint32_t) timestamp - us_ticker_read());
+
+ if (timeDelta <= 0) {
+ /* Make delta positive if it's not, it will expire pretty quickly */
+ /* Note: can't just call us_ticker_irq_handler() directly as we
+ * may already be in it and will overflow the stack */
+ timeDelta = 1;
+ }
+
+ /* The TIMER0 clock source is greater than 1 MHz, so
+ * work out how many times we have to go around
+ * and what the remainder is */
+ g_timer_extra_loops_required = (uint32_t) timeDelta / USECONDS_PER_FULL_TIMER0_RUN;
+ timeDelta -= g_timer_extra_loops_required * USECONDS_PER_FULL_TIMER0_RUN;
+
+ /* Next time we hit the interrupt the increment will be smaller */
+ g_us_overflow_increment = (uint32_t) timeDelta;
+
+ /* We're about to modify the timer value; work out the
+ * difference so that we can compensate for it when
+ * the time is read */
+ timeDelta = timeDelta * CLOCK_TICKS_PER_US;
+ g_user_interrupt_offset += TIMER0_TIME - timeDelta;
+
+ /* Run for the remainder first, then we can loop for the full
+ * USECONDS_PER_FULL_TIMER0_RUN afterwards */
+ TIMER0_LOAD = timeDelta;
+
+ /* A user interrupt is now running */
+ g_user_interrupt = true;
+
+ /* Put interrupts back */
+ core_util_critical_section_exit();
+}
+
+void us_ticker_disable_interrupt(void)
+{
+ /* Can't actually disable the interrupt here
+ * as we need it to manage the timer overflow,
+ * instead switch off the user interrupt part */
+ g_user_interrupt = false;
+ g_timer_extra_loops_required = 0;
+ g_us_overflow_increment = 0;
+}
+
+void us_ticker_clear_interrupt(void)
+{
+ /* As above, can't clear the interrupt as it
+ * may just be an overflow interrupt, instead
+ * clear the variables */
+ g_user_interrupt = false;
+ g_timer_extra_loops_required = 0;
+ g_us_overflow_increment = 0;
+}
