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.
mbed_lp_ticker_wrapper.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2018 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 #include "hal/lp_ticker_api.h" 00017 00018 #if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0) 00019 00020 #include "Timeout.h" 00021 #include "mbed_critical.h" 00022 00023 static const timestamp_t min_delta = LPTICKER_DELAY_TICKS; 00024 00025 static bool init = false; 00026 static bool pending = false; 00027 static bool timeout_pending = false; 00028 static timestamp_t last_set_interrupt = 0; 00029 static timestamp_t last_request = 0; 00030 static timestamp_t next = 0; 00031 00032 static timestamp_t mask; 00033 static timestamp_t reschedule_us; 00034 00035 // Do not use SingletonPtr since this must be initialized in a critical section 00036 static mbed::Timeout *timeout; 00037 static uint64_t timeout_data[sizeof(mbed::Timeout) / 8]; 00038 00039 /** 00040 * Initialize variables 00041 */ 00042 static void init_local() 00043 { 00044 MBED_ASSERT(core_util_in_critical_section()); 00045 00046 const ticker_info_t* info = lp_ticker_get_info(); 00047 if (info->bits >= 32) { 00048 mask = 0xffffffff; 00049 } else { 00050 mask = ((uint64_t)1 << info->bits) - 1; 00051 } 00052 00053 // Round us_per_tick up 00054 timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency; 00055 00056 // Add 1 tick to the min delta for the case where the clock transitions after you read it 00057 // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) 00058 reschedule_us = (min_delta + 1) * us_per_tick + 4; 00059 00060 timeout = new (timeout_data) mbed::Timeout(); 00061 } 00062 00063 /** 00064 * Call lp_ticker_set_interrupt with a value that is guaranteed to fire 00065 * 00066 * Assumptions 00067 * -Only one low power clock tick can pass from the last read (last_read) 00068 * -The closest an interrupt can fire is max_delta + 1 00069 * 00070 * @param last_read The last value read from lp_ticker_read 00071 * @param timestamp The timestamp to trigger the interrupt at 00072 */ 00073 static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp) 00074 { 00075 MBED_ASSERT(core_util_in_critical_section()); 00076 uint32_t delta = (timestamp - last_read) & mask; 00077 if (delta < min_delta + 2) { 00078 timestamp = (last_read + min_delta + 2) & mask; 00079 } 00080 lp_ticker_set_interrupt(timestamp); 00081 } 00082 00083 /** 00084 * Set the low power ticker match time when hardware is ready 00085 * 00086 * This event is scheduled to set the lp timer after the previous write 00087 * has taken effect and it is safe to write a new value without blocking. 00088 * If the time has already passed then this function fires and interrupt 00089 * immediately. 00090 */ 00091 static void set_interrupt_later() 00092 { 00093 core_util_critical_section_enter(); 00094 00095 timestamp_t current = lp_ticker_read(); 00096 if (_ticker_match_interval_passed(last_request, current, next)) { 00097 lp_ticker_fire_interrupt(); 00098 } else { 00099 set_interrupt_safe(current, next); 00100 last_set_interrupt = lp_ticker_read(); 00101 } 00102 timeout_pending = false; 00103 00104 core_util_critical_section_exit(); 00105 } 00106 00107 /** 00108 * Wrapper around lp_ticker_set_interrupt to prevent blocking 00109 * 00110 * Problems this function is solving: 00111 * 1. Interrupt may not fire if set earlier than LPTICKER_DELAY_TICKS low power clock cycles 00112 * 2. Setting the interrupt back-to-back will block 00113 * 00114 * This wrapper function prevents lp_ticker_set_interrupt from being called 00115 * back-to-back and blocking while the first write is in progress. This function 00116 * avoids that problem by scheduling a timeout event if the lp ticker is in the 00117 * middle of a write operation. 00118 * 00119 * @param timestamp Time to call ticker irq 00120 * @note this is a utility function and it's not required part of HAL implementation 00121 */ 00122 extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp) 00123 { 00124 core_util_critical_section_enter(); 00125 00126 if (!init) { 00127 init_local(); 00128 init = true; 00129 } 00130 00131 timestamp_t current = lp_ticker_read(); 00132 if (pending) { 00133 // Check if pending should be cleared 00134 if (((current - last_set_interrupt) & mask) >= min_delta) { 00135 pending = false; 00136 } 00137 } 00138 00139 if (pending || timeout_pending) { 00140 next = timestamp; 00141 last_request = current; 00142 if (!timeout_pending) { 00143 timeout->attach_us(set_interrupt_later, reschedule_us); 00144 timeout_pending = true; 00145 } 00146 } else { 00147 // Schedule immediately if nothing is pending 00148 set_interrupt_safe(current, timestamp); 00149 last_set_interrupt = lp_ticker_read(); 00150 pending = true; 00151 } 00152 00153 core_util_critical_section_exit(); 00154 } 00155 00156 #endif
Generated on Tue Jul 12 2022 12:45:29 by
 1.7.2
 1.7.2