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.
Fork of mbed-dev by
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:59:33 by
1.7.2
