Initial commit

Dependencies:   FastPWM

Committer:
lypinator
Date:
Wed Sep 16 01:11:49 2020 +0000
Revision:
0:bb348c97df44
Added PWM

Who changed what in which revision?

UserRevisionLine numberNew contents of line
lypinator 0:bb348c97df44 1 /* mbed Microcontroller Library
lypinator 0:bb348c97df44 2 * Copyright (c) 2018 ARM Limited
lypinator 0:bb348c97df44 3 *
lypinator 0:bb348c97df44 4 * Licensed under the Apache License, Version 2.0 (the "License");
lypinator 0:bb348c97df44 5 * you may not use this file except in compliance with the License.
lypinator 0:bb348c97df44 6 * You may obtain a copy of the License at
lypinator 0:bb348c97df44 7 *
lypinator 0:bb348c97df44 8 * http://www.apache.org/licenses/LICENSE-2.0
lypinator 0:bb348c97df44 9 *
lypinator 0:bb348c97df44 10 * Unless required by applicable law or agreed to in writing, software
lypinator 0:bb348c97df44 11 * distributed under the License is distributed on an "AS IS" BASIS,
lypinator 0:bb348c97df44 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
lypinator 0:bb348c97df44 13 * See the License for the specific language governing permissions and
lypinator 0:bb348c97df44 14 * limitations under the License.
lypinator 0:bb348c97df44 15 */
lypinator 0:bb348c97df44 16 #include "hal/lp_ticker_api.h"
lypinator 0:bb348c97df44 17
lypinator 0:bb348c97df44 18 #if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
lypinator 0:bb348c97df44 19
lypinator 0:bb348c97df44 20 #include "Timeout.h"
lypinator 0:bb348c97df44 21 #include "mbed_critical.h"
lypinator 0:bb348c97df44 22
lypinator 0:bb348c97df44 23 static const timestamp_t min_delta = LPTICKER_DELAY_TICKS;
lypinator 0:bb348c97df44 24
lypinator 0:bb348c97df44 25 static bool init = false;
lypinator 0:bb348c97df44 26 static bool pending = false;
lypinator 0:bb348c97df44 27 static bool timeout_pending = false;
lypinator 0:bb348c97df44 28 static timestamp_t last_set_interrupt = 0;
lypinator 0:bb348c97df44 29 static timestamp_t last_request = 0;
lypinator 0:bb348c97df44 30 static timestamp_t next = 0;
lypinator 0:bb348c97df44 31
lypinator 0:bb348c97df44 32 static timestamp_t mask;
lypinator 0:bb348c97df44 33 static timestamp_t reschedule_us;
lypinator 0:bb348c97df44 34
lypinator 0:bb348c97df44 35 // Do not use SingletonPtr since this must be initialized in a critical section
lypinator 0:bb348c97df44 36 static mbed::Timeout *timeout;
lypinator 0:bb348c97df44 37 static uint64_t timeout_data[sizeof(mbed::Timeout) / 8];
lypinator 0:bb348c97df44 38
lypinator 0:bb348c97df44 39 /**
lypinator 0:bb348c97df44 40 * Initialize variables
lypinator 0:bb348c97df44 41 */
lypinator 0:bb348c97df44 42 static void init_local()
lypinator 0:bb348c97df44 43 {
lypinator 0:bb348c97df44 44 MBED_ASSERT(core_util_in_critical_section());
lypinator 0:bb348c97df44 45
lypinator 0:bb348c97df44 46 const ticker_info_t *info = lp_ticker_get_info();
lypinator 0:bb348c97df44 47 if (info->bits >= 32) {
lypinator 0:bb348c97df44 48 mask = 0xffffffff;
lypinator 0:bb348c97df44 49 } else {
lypinator 0:bb348c97df44 50 mask = ((uint64_t)1 << info->bits) - 1;
lypinator 0:bb348c97df44 51 }
lypinator 0:bb348c97df44 52
lypinator 0:bb348c97df44 53 // Round us_per_tick up
lypinator 0:bb348c97df44 54 timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency;
lypinator 0:bb348c97df44 55
lypinator 0:bb348c97df44 56 // Add 1 tick to the min delta for the case where the clock transitions after you read it
lypinator 0:bb348c97df44 57 // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period)
lypinator 0:bb348c97df44 58 reschedule_us = (min_delta + 1) * us_per_tick + 4;
lypinator 0:bb348c97df44 59
lypinator 0:bb348c97df44 60 timeout = new (timeout_data) mbed::Timeout();
lypinator 0:bb348c97df44 61 }
lypinator 0:bb348c97df44 62
lypinator 0:bb348c97df44 63 /**
lypinator 0:bb348c97df44 64 * Call lp_ticker_set_interrupt with a value that is guaranteed to fire
lypinator 0:bb348c97df44 65 *
lypinator 0:bb348c97df44 66 * Assumptions
lypinator 0:bb348c97df44 67 * -Only one low power clock tick can pass from the last read (last_read)
lypinator 0:bb348c97df44 68 * -The closest an interrupt can fire is max_delta + 1
lypinator 0:bb348c97df44 69 *
lypinator 0:bb348c97df44 70 * @param last_read The last value read from lp_ticker_read
lypinator 0:bb348c97df44 71 * @param timestamp The timestamp to trigger the interrupt at
lypinator 0:bb348c97df44 72 */
lypinator 0:bb348c97df44 73 static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp)
lypinator 0:bb348c97df44 74 {
lypinator 0:bb348c97df44 75 MBED_ASSERT(core_util_in_critical_section());
lypinator 0:bb348c97df44 76 uint32_t delta = (timestamp - last_read) & mask;
lypinator 0:bb348c97df44 77 if (delta < min_delta + 2) {
lypinator 0:bb348c97df44 78 timestamp = (last_read + min_delta + 2) & mask;
lypinator 0:bb348c97df44 79 }
lypinator 0:bb348c97df44 80 lp_ticker_set_interrupt(timestamp);
lypinator 0:bb348c97df44 81 }
lypinator 0:bb348c97df44 82
lypinator 0:bb348c97df44 83 /**
lypinator 0:bb348c97df44 84 * Set the low power ticker match time when hardware is ready
lypinator 0:bb348c97df44 85 *
lypinator 0:bb348c97df44 86 * This event is scheduled to set the lp timer after the previous write
lypinator 0:bb348c97df44 87 * has taken effect and it is safe to write a new value without blocking.
lypinator 0:bb348c97df44 88 * If the time has already passed then this function fires and interrupt
lypinator 0:bb348c97df44 89 * immediately.
lypinator 0:bb348c97df44 90 */
lypinator 0:bb348c97df44 91 static void set_interrupt_later()
lypinator 0:bb348c97df44 92 {
lypinator 0:bb348c97df44 93 core_util_critical_section_enter();
lypinator 0:bb348c97df44 94
lypinator 0:bb348c97df44 95 timestamp_t current = lp_ticker_read();
lypinator 0:bb348c97df44 96 if (_ticker_match_interval_passed(last_request, current, next)) {
lypinator 0:bb348c97df44 97 lp_ticker_fire_interrupt();
lypinator 0:bb348c97df44 98 } else {
lypinator 0:bb348c97df44 99 set_interrupt_safe(current, next);
lypinator 0:bb348c97df44 100 last_set_interrupt = lp_ticker_read();
lypinator 0:bb348c97df44 101 }
lypinator 0:bb348c97df44 102 timeout_pending = false;
lypinator 0:bb348c97df44 103
lypinator 0:bb348c97df44 104 core_util_critical_section_exit();
lypinator 0:bb348c97df44 105 }
lypinator 0:bb348c97df44 106
lypinator 0:bb348c97df44 107 /**
lypinator 0:bb348c97df44 108 * Wrapper around lp_ticker_set_interrupt to prevent blocking
lypinator 0:bb348c97df44 109 *
lypinator 0:bb348c97df44 110 * Problems this function is solving:
lypinator 0:bb348c97df44 111 * 1. Interrupt may not fire if set earlier than LPTICKER_DELAY_TICKS low power clock cycles
lypinator 0:bb348c97df44 112 * 2. Setting the interrupt back-to-back will block
lypinator 0:bb348c97df44 113 *
lypinator 0:bb348c97df44 114 * This wrapper function prevents lp_ticker_set_interrupt from being called
lypinator 0:bb348c97df44 115 * back-to-back and blocking while the first write is in progress. This function
lypinator 0:bb348c97df44 116 * avoids that problem by scheduling a timeout event if the lp ticker is in the
lypinator 0:bb348c97df44 117 * middle of a write operation.
lypinator 0:bb348c97df44 118 *
lypinator 0:bb348c97df44 119 * @param timestamp Time to call ticker irq
lypinator 0:bb348c97df44 120 * @note this is a utility function and it's not required part of HAL implementation
lypinator 0:bb348c97df44 121 */
lypinator 0:bb348c97df44 122 extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp)
lypinator 0:bb348c97df44 123 {
lypinator 0:bb348c97df44 124 core_util_critical_section_enter();
lypinator 0:bb348c97df44 125
lypinator 0:bb348c97df44 126 if (!init) {
lypinator 0:bb348c97df44 127 init_local();
lypinator 0:bb348c97df44 128 init = true;
lypinator 0:bb348c97df44 129 }
lypinator 0:bb348c97df44 130
lypinator 0:bb348c97df44 131 timestamp_t current = lp_ticker_read();
lypinator 0:bb348c97df44 132 if (pending) {
lypinator 0:bb348c97df44 133 // Check if pending should be cleared
lypinator 0:bb348c97df44 134 if (((current - last_set_interrupt) & mask) >= min_delta) {
lypinator 0:bb348c97df44 135 pending = false;
lypinator 0:bb348c97df44 136 }
lypinator 0:bb348c97df44 137 }
lypinator 0:bb348c97df44 138
lypinator 0:bb348c97df44 139 if (pending || timeout_pending) {
lypinator 0:bb348c97df44 140 next = timestamp;
lypinator 0:bb348c97df44 141 last_request = current;
lypinator 0:bb348c97df44 142 if (!timeout_pending) {
lypinator 0:bb348c97df44 143 timeout->attach_us(set_interrupt_later, reschedule_us);
lypinator 0:bb348c97df44 144 timeout_pending = true;
lypinator 0:bb348c97df44 145 }
lypinator 0:bb348c97df44 146 } else {
lypinator 0:bb348c97df44 147 // Schedule immediately if nothing is pending
lypinator 0:bb348c97df44 148 set_interrupt_safe(current, timestamp);
lypinator 0:bb348c97df44 149 last_set_interrupt = lp_ticker_read();
lypinator 0:bb348c97df44 150 pending = true;
lypinator 0:bb348c97df44 151 }
lypinator 0:bb348c97df44 152
lypinator 0:bb348c97df44 153 core_util_critical_section_exit();
lypinator 0:bb348c97df44 154 }
lypinator 0:bb348c97df44 155
lypinator 0:bb348c97df44 156 #endif