takashi kadono / Mbed OS Nucleo446_SSD1331

Dependencies:   ssd1331

Committer:
kadonotakashi
Date:
Thu Oct 11 02:27:46 2018 +0000
Revision:
3:f3764f852aa8
Parent:
0:8fdf9a60065b
Nucreo 446 + SSD1331 test version;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kadonotakashi 0:8fdf9a60065b 1 /* mbed Microcontroller Library
kadonotakashi 0:8fdf9a60065b 2 * Copyright (c) 2016 u-blox
kadonotakashi 0:8fdf9a60065b 3 *
kadonotakashi 0:8fdf9a60065b 4 * Licensed under the Apache License, Version 2.0 (the "License");
kadonotakashi 0:8fdf9a60065b 5 * you may not use this file except in compliance with the License.
kadonotakashi 0:8fdf9a60065b 6 * You may obtain a copy of the License at
kadonotakashi 0:8fdf9a60065b 7 *
kadonotakashi 0:8fdf9a60065b 8 * http://www.apache.org/licenses/LICENSE-2.0
kadonotakashi 0:8fdf9a60065b 9 *
kadonotakashi 0:8fdf9a60065b 10 * Unless required by applicable law or agreed to in writing, software
kadonotakashi 0:8fdf9a60065b 11 * distributed under the License is distributed on an "AS IS" BASIS,
kadonotakashi 0:8fdf9a60065b 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
kadonotakashi 0:8fdf9a60065b 13 * See the License for the specific language governing permissions and
kadonotakashi 0:8fdf9a60065b 14 * limitations under the License.
kadonotakashi 0:8fdf9a60065b 15 */
kadonotakashi 0:8fdf9a60065b 16
kadonotakashi 0:8fdf9a60065b 17 /* The usecond ticker is mapped to TIMER0. A few issues must be dealt
kadonotakashi 0:8fdf9a60065b 18 * with in this driver:
kadonotakashi 0:8fdf9a60065b 19 *
kadonotakashi 0:8fdf9a60065b 20 * 1. The us_ticker API must count upwards, not down.
kadonotakashi 0:8fdf9a60065b 21 * 2. The expected range/resolution is 32 bits each of 1 usecond,
kadonotakashi 0:8fdf9a60065b 22 * whereas TIMER0 runs at 48 MHz (not 1 MHz) and so actually
kadonotakashi 0:8fdf9a60065b 23 * has a range/resolution of 26 bits at 0.02 useconds. Software
kadonotakashi 0:8fdf9a60065b 24 * has to compensate for this.
kadonotakashi 0:8fdf9a60065b 25 */
kadonotakashi 0:8fdf9a60065b 26
kadonotakashi 0:8fdf9a60065b 27 #include "us_ticker_api.h"
kadonotakashi 0:8fdf9a60065b 28 #include "mbed_critical.h"
kadonotakashi 0:8fdf9a60065b 29
kadonotakashi 0:8fdf9a60065b 30 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 31 * MACROS
kadonotakashi 0:8fdf9a60065b 32 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 33
kadonotakashi 0:8fdf9a60065b 34 /* TIMER0 clock is 48 MHz */
kadonotakashi 0:8fdf9a60065b 35 #define CLOCK_TICKS_PER_US 48
kadonotakashi 0:8fdf9a60065b 36
kadonotakashi 0:8fdf9a60065b 37 /* The number of clock ticks in a full-run of
kadonotakashi 0:8fdf9a60065b 38 * TIMER0, scaled to represent useconds */
kadonotakashi 0:8fdf9a60065b 39 #define USECONDS_PER_FULL_TIMER0_RUN 89478485
kadonotakashi 0:8fdf9a60065b 40
kadonotakashi 0:8fdf9a60065b 41 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 42 * TYPES
kadonotakashi 0:8fdf9a60065b 43 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 44
kadonotakashi 0:8fdf9a60065b 45 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 46 * GLOBAL VARIABLES
kadonotakashi 0:8fdf9a60065b 47 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 48
kadonotakashi 0:8fdf9a60065b 49 /* Are we ready? */
kadonotakashi 0:8fdf9a60065b 50 static bool g_initialised = false;
kadonotakashi 0:8fdf9a60065b 51
kadonotakashi 0:8fdf9a60065b 52 /* Keep track of the number of useconds elapsed. */
kadonotakashi 0:8fdf9a60065b 53 static uint32_t g_us_overflow = 0;
kadonotakashi 0:8fdf9a60065b 54
kadonotakashi 0:8fdf9a60065b 55 /* The number of useconds to increment the by at each interrupt */
kadonotakashi 0:8fdf9a60065b 56 static uint32_t g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
kadonotakashi 0:8fdf9a60065b 57
kadonotakashi 0:8fdf9a60065b 58 /* Keep track of extra loops required to represent a particular time
kadonotakashi 0:8fdf9a60065b 59 * as the HW timer runs faster than 1 MHz */
kadonotakashi 0:8fdf9a60065b 60 static uint32_t g_timer_extra_loops_required = 0;
kadonotakashi 0:8fdf9a60065b 61 static uint32_t g_timer_extra_loops_done = 0;
kadonotakashi 0:8fdf9a60065b 62
kadonotakashi 0:8fdf9a60065b 63 /* Keep track of any adjustment due to user interrupts . */
kadonotakashi 0:8fdf9a60065b 64 static uint32_t g_user_interrupt_offset = 0;
kadonotakashi 0:8fdf9a60065b 65
kadonotakashi 0:8fdf9a60065b 66 /* Flag that a user timer is running */
kadonotakashi 0:8fdf9a60065b 67 static bool g_user_interrupt = false;
kadonotakashi 0:8fdf9a60065b 68
kadonotakashi 0:8fdf9a60065b 69 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 70 * FUNCTION PROTOTYPES
kadonotakashi 0:8fdf9a60065b 71 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 72
kadonotakashi 0:8fdf9a60065b 73 static inline uint32_t divide_by_48(uint32_t x);
kadonotakashi 0:8fdf9a60065b 74
kadonotakashi 0:8fdf9a60065b 75 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 76 * NON-API FUNCTIONS
kadonotakashi 0:8fdf9a60065b 77 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 78
kadonotakashi 0:8fdf9a60065b 79 /* Perform a divide-by-48 operation.
kadonotakashi 0:8fdf9a60065b 80 * This is done as a multiply-shift operation to take advantage of
kadonotakashi 0:8fdf9a60065b 81 * the ARM 32 bit single-cycle multiply and avoid using division;
kadonotakashi 0:8fdf9a60065b 82 * 1/48 is equivalent to 1365/2^16. It is also done in two halves
kadonotakashi 0:8fdf9a60065b 83 * to make sure that the multiplies fit into 32 bits.
kadonotakashi 0:8fdf9a60065b 84 *
kadonotakashi 0:8fdf9a60065b 85 * The principle is:
kadonotakashi 0:8fdf9a60065b 86 * - divide the top 16 bits by 48 using multiply-shift (=> x1),
kadonotakashi 0:8fdf9a60065b 87 * - work out the remainder of that operation and divide that by 48 (=> x1r),
kadonotakashi 0:8fdf9a60065b 88 * - divide the bottom 16 bits by 48 using multiply-shift (=> x2),
kadonotakashi 0:8fdf9a60065b 89 * - add the lot together to get the result.
kadonotakashi 0:8fdf9a60065b 90 *
kadonotakashi 0:8fdf9a60065b 91 * The cost is 29 instructions.
kadonotakashi 0:8fdf9a60065b 92 */
kadonotakashi 0:8fdf9a60065b 93 static inline uint32_t divide_by_48(uint32_t x)
kadonotakashi 0:8fdf9a60065b 94 {
kadonotakashi 0:8fdf9a60065b 95 uint32_t x1 = ((x >> 16) * 1365) >> 16;
kadonotakashi 0:8fdf9a60065b 96 uint32_t x1r = ((x & 0xFFFF0000) - ((x1 * 48) << 16));
kadonotakashi 0:8fdf9a60065b 97 x1r = (x1r * 1365) >> 16;
kadonotakashi 0:8fdf9a60065b 98 uint32_t x2 = ((x & 0xFFFF) * 1365) >> 16;
kadonotakashi 0:8fdf9a60065b 99
kadonotakashi 0:8fdf9a60065b 100 return (x1 << 16) + x1r + x2;
kadonotakashi 0:8fdf9a60065b 101 }
kadonotakashi 0:8fdf9a60065b 102
kadonotakashi 0:8fdf9a60065b 103 /* Timer0 handler */
kadonotakashi 0:8fdf9a60065b 104 void IRQ1_TMR0_Handler(void)
kadonotakashi 0:8fdf9a60065b 105 {
kadonotakashi 0:8fdf9a60065b 106 if (g_initialised) {
kadonotakashi 0:8fdf9a60065b 107 /* Increment the overflow count and set the increment
kadonotakashi 0:8fdf9a60065b 108 * value for next time */
kadonotakashi 0:8fdf9a60065b 109 g_us_overflow += g_us_overflow_increment;
kadonotakashi 0:8fdf9a60065b 110 g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
kadonotakashi 0:8fdf9a60065b 111
kadonotakashi 0:8fdf9a60065b 112 /* Now handle the user interrupt case */
kadonotakashi 0:8fdf9a60065b 113 if (g_user_interrupt) {
kadonotakashi 0:8fdf9a60065b 114 if (g_timer_extra_loops_done < g_timer_extra_loops_required) {
kadonotakashi 0:8fdf9a60065b 115 /* Let the timer go round again */
kadonotakashi 0:8fdf9a60065b 116 g_timer_extra_loops_done++;
kadonotakashi 0:8fdf9a60065b 117 } else {
kadonotakashi 0:8fdf9a60065b 118 /* We've done with looping around for a user interrupt */
kadonotakashi 0:8fdf9a60065b 119 g_user_interrupt = false;
kadonotakashi 0:8fdf9a60065b 120
kadonotakashi 0:8fdf9a60065b 121 /* Call the mbed API */
kadonotakashi 0:8fdf9a60065b 122 us_ticker_irq_handler();
kadonotakashi 0:8fdf9a60065b 123 }
kadonotakashi 0:8fdf9a60065b 124 }
kadonotakashi 0:8fdf9a60065b 125 }
kadonotakashi 0:8fdf9a60065b 126
kadonotakashi 0:8fdf9a60065b 127 NVIC_ClearPendingIRQ(Timer_IRQn);
kadonotakashi 0:8fdf9a60065b 128 }
kadonotakashi 0:8fdf9a60065b 129
kadonotakashi 0:8fdf9a60065b 130 /* ----------------------------------------------------------------
kadonotakashi 0:8fdf9a60065b 131 * MBED API CALLS
kadonotakashi 0:8fdf9a60065b 132 * ----------------------------------------------------------------*/
kadonotakashi 0:8fdf9a60065b 133
kadonotakashi 0:8fdf9a60065b 134 void us_ticker_init(void)
kadonotakashi 0:8fdf9a60065b 135 {
kadonotakashi 0:8fdf9a60065b 136 if (!g_initialised) {
kadonotakashi 0:8fdf9a60065b 137 /* Reset the globals */
kadonotakashi 0:8fdf9a60065b 138 g_timer_extra_loops_done = 0;
kadonotakashi 0:8fdf9a60065b 139 g_timer_extra_loops_required = 0;
kadonotakashi 0:8fdf9a60065b 140 g_us_overflow = 0;
kadonotakashi 0:8fdf9a60065b 141 g_us_overflow_increment = USECONDS_PER_FULL_TIMER0_RUN;
kadonotakashi 0:8fdf9a60065b 142 g_user_interrupt_offset = 0;
kadonotakashi 0:8fdf9a60065b 143 g_user_interrupt = false;
kadonotakashi 0:8fdf9a60065b 144
kadonotakashi 0:8fdf9a60065b 145 /* Get the timer running (starting at what is zero,
kadonotakashi 0:8fdf9a60065b 146 * once inverted), with repeat */
kadonotakashi 0:8fdf9a60065b 147 NVIC_ClearPendingIRQ(Timer_IRQn);
kadonotakashi 0:8fdf9a60065b 148 TIMER0_LOAD = 0xFFFFFFFF;
kadonotakashi 0:8fdf9a60065b 149 TIMER0_CTRL = 0x03;
kadonotakashi 0:8fdf9a60065b 150 NVIC_EnableIRQ(Timer_IRQn);
kadonotakashi 0:8fdf9a60065b 151
kadonotakashi 0:8fdf9a60065b 152 g_initialised = true;
kadonotakashi 0:8fdf9a60065b 153 }
kadonotakashi 0:8fdf9a60065b 154 }
kadonotakashi 0:8fdf9a60065b 155
kadonotakashi 0:8fdf9a60065b 156 uint32_t us_ticker_read()
kadonotakashi 0:8fdf9a60065b 157 {
kadonotakashi 0:8fdf9a60065b 158 uint32_t timeValue;
kadonotakashi 0:8fdf9a60065b 159
kadonotakashi 0:8fdf9a60065b 160 /* This can be called before initialisation has been performed */
kadonotakashi 0:8fdf9a60065b 161 if (!g_initialised) {
kadonotakashi 0:8fdf9a60065b 162 us_ticker_init();
kadonotakashi 0:8fdf9a60065b 163 }
kadonotakashi 0:8fdf9a60065b 164
kadonotakashi 0:8fdf9a60065b 165 /* Disable interrupts to avoid collisions */
kadonotakashi 0:8fdf9a60065b 166 core_util_critical_section_enter();
kadonotakashi 0:8fdf9a60065b 167
kadonotakashi 0:8fdf9a60065b 168 /* Get the timer value, adding the offset in case we've been moved
kadonotakashi 0:8fdf9a60065b 169 * around by user activity, inverting it (as a count-up timer is
kadonotakashi 0:8fdf9a60065b 170 * expected), then scaling it to useconds and finally adding the
kadonotakashi 0:8fdf9a60065b 171 * usecond overflow value to make up the 32-bit usecond total */
kadonotakashi 0:8fdf9a60065b 172 timeValue = divide_by_48(~(TIMER0_TIME + g_user_interrupt_offset)) + g_us_overflow;
kadonotakashi 0:8fdf9a60065b 173
kadonotakashi 0:8fdf9a60065b 174 /* Put interrupts back */
kadonotakashi 0:8fdf9a60065b 175 core_util_critical_section_exit();
kadonotakashi 0:8fdf9a60065b 176
kadonotakashi 0:8fdf9a60065b 177 return timeValue;
kadonotakashi 0:8fdf9a60065b 178 }
kadonotakashi 0:8fdf9a60065b 179
kadonotakashi 0:8fdf9a60065b 180 /* NOTE: it seems to be an accepted fact that users
kadonotakashi 0:8fdf9a60065b 181 * will never ask for a timeout of more than 2^31 useconds
kadonotakashi 0:8fdf9a60065b 182 * and hence it's possible to do signed arithmetic
kadonotakashi 0:8fdf9a60065b 183 */
kadonotakashi 0:8fdf9a60065b 184 void us_ticker_set_interrupt(timestamp_t timestamp)
kadonotakashi 0:8fdf9a60065b 185 {
kadonotakashi 0:8fdf9a60065b 186 g_timer_extra_loops_required = 0;
kadonotakashi 0:8fdf9a60065b 187 g_timer_extra_loops_done = 0;
kadonotakashi 0:8fdf9a60065b 188 int32_t timeDelta;
kadonotakashi 0:8fdf9a60065b 189
kadonotakashi 0:8fdf9a60065b 190 /* Disable interrupts to avoid collisions */
kadonotakashi 0:8fdf9a60065b 191 core_util_critical_section_enter();
kadonotakashi 0:8fdf9a60065b 192
kadonotakashi 0:8fdf9a60065b 193 /* Establish how far we're being asked to move */
kadonotakashi 0:8fdf9a60065b 194 timeDelta = (int32_t) ((uint32_t) timestamp - us_ticker_read());
kadonotakashi 0:8fdf9a60065b 195
kadonotakashi 0:8fdf9a60065b 196 if (timeDelta <= 0) {
kadonotakashi 0:8fdf9a60065b 197 /* Make delta positive if it's not, it will expire pretty quickly */
kadonotakashi 0:8fdf9a60065b 198 /* Note: can't just call us_ticker_irq_handler() directly as we
kadonotakashi 0:8fdf9a60065b 199 * may already be in it and will overflow the stack */
kadonotakashi 0:8fdf9a60065b 200 timeDelta = 1;
kadonotakashi 0:8fdf9a60065b 201 }
kadonotakashi 0:8fdf9a60065b 202
kadonotakashi 0:8fdf9a60065b 203 /* The TIMER0 clock source is greater than 1 MHz, so
kadonotakashi 0:8fdf9a60065b 204 * work out how many times we have to go around
kadonotakashi 0:8fdf9a60065b 205 * and what the remainder is */
kadonotakashi 0:8fdf9a60065b 206 g_timer_extra_loops_required = (uint32_t) timeDelta / USECONDS_PER_FULL_TIMER0_RUN;
kadonotakashi 0:8fdf9a60065b 207 timeDelta -= g_timer_extra_loops_required * USECONDS_PER_FULL_TIMER0_RUN;
kadonotakashi 0:8fdf9a60065b 208
kadonotakashi 0:8fdf9a60065b 209 /* Next time we hit the interrupt the increment will be smaller */
kadonotakashi 0:8fdf9a60065b 210 g_us_overflow_increment = (uint32_t) timeDelta;
kadonotakashi 0:8fdf9a60065b 211
kadonotakashi 0:8fdf9a60065b 212 /* We're about to modify the timer value; work out the
kadonotakashi 0:8fdf9a60065b 213 * difference so that we can compensate for it when
kadonotakashi 0:8fdf9a60065b 214 * the time is read */
kadonotakashi 0:8fdf9a60065b 215 timeDelta = timeDelta * CLOCK_TICKS_PER_US;
kadonotakashi 0:8fdf9a60065b 216 g_user_interrupt_offset += TIMER0_TIME - timeDelta;
kadonotakashi 0:8fdf9a60065b 217
kadonotakashi 0:8fdf9a60065b 218 /* Run for the remainder first, then we can loop for the full
kadonotakashi 0:8fdf9a60065b 219 * USECONDS_PER_FULL_TIMER0_RUN afterwards */
kadonotakashi 0:8fdf9a60065b 220 TIMER0_LOAD = timeDelta;
kadonotakashi 0:8fdf9a60065b 221
kadonotakashi 0:8fdf9a60065b 222 /* A user interrupt is now running */
kadonotakashi 0:8fdf9a60065b 223 g_user_interrupt = true;
kadonotakashi 0:8fdf9a60065b 224
kadonotakashi 0:8fdf9a60065b 225 /* Put interrupts back */
kadonotakashi 0:8fdf9a60065b 226 core_util_critical_section_exit();
kadonotakashi 0:8fdf9a60065b 227 }
kadonotakashi 0:8fdf9a60065b 228
kadonotakashi 0:8fdf9a60065b 229 void us_ticker_fire_interrupt(void)
kadonotakashi 0:8fdf9a60065b 230 {
kadonotakashi 0:8fdf9a60065b 231 g_user_interrupt = true;
kadonotakashi 0:8fdf9a60065b 232 NVIC_SetPendingIRQ(Timer_IRQn);
kadonotakashi 0:8fdf9a60065b 233 }
kadonotakashi 0:8fdf9a60065b 234
kadonotakashi 0:8fdf9a60065b 235 void us_ticker_disable_interrupt(void)
kadonotakashi 0:8fdf9a60065b 236 {
kadonotakashi 0:8fdf9a60065b 237 /* Can't actually disable the interrupt here
kadonotakashi 0:8fdf9a60065b 238 * as we need it to manage the timer overflow,
kadonotakashi 0:8fdf9a60065b 239 * instead switch off the user interrupt part */
kadonotakashi 0:8fdf9a60065b 240 g_user_interrupt = false;
kadonotakashi 0:8fdf9a60065b 241 g_timer_extra_loops_required = 0;
kadonotakashi 0:8fdf9a60065b 242 g_us_overflow_increment = 0;
kadonotakashi 0:8fdf9a60065b 243 }
kadonotakashi 0:8fdf9a60065b 244
kadonotakashi 0:8fdf9a60065b 245 void us_ticker_clear_interrupt(void)
kadonotakashi 0:8fdf9a60065b 246 {
kadonotakashi 0:8fdf9a60065b 247 /* As above, can't clear the interrupt as it
kadonotakashi 0:8fdf9a60065b 248 * may just be an overflow interrupt, instead
kadonotakashi 0:8fdf9a60065b 249 * clear the variables */
kadonotakashi 0:8fdf9a60065b 250 g_user_interrupt = false;
kadonotakashi 0:8fdf9a60065b 251 g_timer_extra_loops_required = 0;
kadonotakashi 0:8fdf9a60065b 252 g_us_overflow_increment = 0;
kadonotakashi 0:8fdf9a60065b 253 }
kadonotakashi 0:8fdf9a60065b 254
kadonotakashi 0:8fdf9a60065b 255 void us_ticker_free(void)
kadonotakashi 0:8fdf9a60065b 256 {
kadonotakashi 0:8fdf9a60065b 257
kadonotakashi 0:8fdf9a60065b 258 }