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: TYBLE16_simple_data_logger TYBLE16_MP3_Air
mbed_ticker_api.c
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2015 ARM Limited 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 #include <stdio.h> 00018 #include <stddef.h> 00019 #include "hal/ticker_api.h" 00020 #include "platform/mbed_critical.h" 00021 #include "platform/mbed_assert.h" 00022 #include "platform/mbed_error.h" 00023 00024 static void schedule_interrupt(const ticker_data_t *const ticker); 00025 static void update_present_time(const ticker_data_t *const ticker); 00026 00027 /* 00028 * Initialize a ticker instance. 00029 */ 00030 static void initialize(const ticker_data_t *ticker) 00031 { 00032 // return if the queue has already been initialized, in that case the 00033 // interface used by the queue is already initialized. 00034 if (ticker->queue->initialized) { 00035 return; 00036 } 00037 if (ticker->queue->suspended) { 00038 return; 00039 } 00040 00041 ticker->interface->init(); 00042 00043 const ticker_info_t *info = ticker->interface->get_info(); 00044 uint32_t frequency = info->frequency; 00045 if (info->frequency == 0) { 00046 #if MBED_TRAP_ERRORS_ENABLED 00047 MBED_ERROR( 00048 MBED_MAKE_ERROR( 00049 MBED_MODULE_HAL, 00050 MBED_ERROR_CODE_NOT_READY 00051 ), 00052 "Ticker frequency is zero" 00053 ); 00054 #else 00055 frequency = 1000000; 00056 #endif // MBED_TRAP_ERRORS_ENABLED 00057 } 00058 00059 uint8_t frequency_shifts = 0; 00060 for (uint8_t i = 31; i > 0; --i) { 00061 if ((1U << i) == frequency) { 00062 frequency_shifts = i; 00063 break; 00064 } 00065 } 00066 00067 uint32_t bits = info->bits; 00068 if ((info->bits > 32) || (info->bits < 4)) { 00069 #if MBED_TRAP_ERRORS_ENABLED 00070 MBED_ERROR( 00071 MBED_MAKE_ERROR( 00072 MBED_MODULE_HAL, 00073 MBED_ERROR_CODE_INVALID_SIZE 00074 ), 00075 "Ticker number of bit is greater than 32 or less than 4 bits" 00076 ); 00077 #else 00078 bits = 32; 00079 #endif // MBED_TRAP_ERRORS_ENABLED 00080 } 00081 uint32_t max_delta = 0x7 << (bits - 4); // 7/16th 00082 uint64_t max_delta_us = 00083 ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency; 00084 00085 ticker->queue->event_handler = NULL; 00086 ticker->queue->head = NULL; 00087 ticker->queue->tick_last_read = ticker->interface->read(); 00088 ticker->queue->tick_remainder = 0; 00089 ticker->queue->frequency = frequency; 00090 ticker->queue->frequency_shifts = frequency_shifts; 00091 ticker->queue->bitmask = ((uint64_t)1 << bits) - 1; 00092 ticker->queue->max_delta = max_delta; 00093 ticker->queue->max_delta_us = max_delta_us; 00094 ticker->queue->present_time = 0; 00095 ticker->queue->dispatching = false; 00096 ticker->queue->suspended = false; 00097 ticker->queue->initialized = true; 00098 00099 update_present_time(ticker); 00100 schedule_interrupt(ticker); 00101 } 00102 00103 /** 00104 * Set the event handler function of a ticker instance. 00105 */ 00106 static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler) 00107 { 00108 ticker->queue->event_handler = handler; 00109 } 00110 00111 /* 00112 * Convert a 32 bit timestamp into a 64 bit timestamp. 00113 * 00114 * A 64 bit timestamp is used as the point of time of reference while the 00115 * timestamp to convert is relative to this point of time. 00116 * 00117 * The lower 32 bits of the timestamp returned will be equal to the timestamp to 00118 * convert. 00119 * 00120 * If the timestamp to convert is less than the lower 32 bits of the time 00121 * reference then the timestamp to convert is seen as an overflowed value and 00122 * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit 00123 * of the reference point + 1. 00124 * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the 00125 * reference point. 00126 * 00127 * @param ref: The 64 bit timestamp of reference. 00128 * @param timestamp: The timestamp to convert. 00129 */ 00130 static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp) 00131 { 00132 bool overflow = timestamp < ((timestamp_t) ref) ? true : false; 00133 00134 us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp; 00135 if (overflow) { 00136 result += (1ULL << 32); 00137 } 00138 00139 return result; 00140 } 00141 00142 /** 00143 * Update the present timestamp value of a ticker. 00144 */ 00145 static void update_present_time(const ticker_data_t *const ticker) 00146 { 00147 ticker_event_queue_t *queue = ticker->queue; 00148 if (queue->suspended) { 00149 return; 00150 } 00151 uint32_t ticker_time = ticker->interface->read(); 00152 if (ticker_time == ticker->queue->tick_last_read) { 00153 // No work to do 00154 return; 00155 } 00156 00157 uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask; 00158 queue->tick_last_read = ticker_time; 00159 00160 uint64_t elapsed_us; 00161 if (1000000 == queue->frequency) { 00162 // Optimized for 1MHz 00163 00164 elapsed_us = elapsed_ticks; 00165 } else if (0 != queue->frequency_shifts) { 00166 // Optimized for frequencies divisible by 2 00167 uint64_t us_x_ticks = elapsed_ticks * 1000000; 00168 elapsed_us = us_x_ticks >> queue->frequency_shifts; 00169 00170 // Update remainder 00171 queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts); 00172 if (queue->tick_remainder >= queue->frequency) { 00173 elapsed_us += 1; 00174 queue->tick_remainder -= queue->frequency; 00175 } 00176 } else { 00177 // General case 00178 00179 uint64_t us_x_ticks = elapsed_ticks * 1000000; 00180 elapsed_us = us_x_ticks / queue->frequency; 00181 00182 // Update remainder 00183 queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency; 00184 if (queue->tick_remainder >= queue->frequency) { 00185 elapsed_us += 1; 00186 queue->tick_remainder -= queue->frequency; 00187 } 00188 } 00189 00190 // Update current time 00191 queue->present_time += elapsed_us; 00192 } 00193 00194 /** 00195 * Given the absolute timestamp compute the hal tick timestamp rounded up. 00196 */ 00197 static timestamp_t compute_tick_round_up(const ticker_data_t *const ticker, us_timestamp_t timestamp) 00198 { 00199 ticker_event_queue_t *queue = ticker->queue; 00200 us_timestamp_t delta_us = timestamp - queue->present_time; 00201 00202 timestamp_t delta = ticker->queue->max_delta; 00203 if (delta_us <= ticker->queue->max_delta_us) { 00204 // Checking max_delta_us ensures the operation will not overflow 00205 00206 if (1000000 == queue->frequency) { 00207 // Optimized for 1MHz 00208 00209 delta = delta_us; 00210 if (delta > ticker->queue->max_delta) { 00211 delta = ticker->queue->max_delta; 00212 } 00213 } else if (0 != queue->frequency_shifts) { 00214 // Optimized frequencies divisible by 2 00215 00216 delta = ((delta_us << ticker->queue->frequency_shifts) + 1000000 - 1) / 1000000; 00217 if (delta > ticker->queue->max_delta) { 00218 delta = ticker->queue->max_delta; 00219 } 00220 } else { 00221 // General case 00222 00223 delta = (delta_us * queue->frequency + 1000000 - 1) / 1000000; 00224 if (delta > ticker->queue->max_delta) { 00225 delta = ticker->queue->max_delta; 00226 } 00227 } 00228 } 00229 return (queue->tick_last_read + delta) & queue->bitmask; 00230 } 00231 00232 /** 00233 * Return 1 if the tick has incremented to or past match_tick, otherwise 0. 00234 */ 00235 int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick) 00236 { 00237 if (match_tick > prev_tick) { 00238 return (cur_tick >= match_tick) || (cur_tick < prev_tick); 00239 } else { 00240 return (cur_tick < prev_tick) && (cur_tick >= match_tick); 00241 } 00242 } 00243 00244 /** 00245 * Compute the time when the interrupt has to be triggered and schedule it. 00246 * 00247 * If there is no event in the queue or the next event to execute is in more 00248 * than ticker.queue.max_delta ticks from now then the ticker irq will be 00249 * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be 00250 * scheduled to happen when the running counter reach the timestamp of the 00251 * first event in the queue. 00252 * 00253 * @note If there is no event in the queue then the interrupt is scheduled to 00254 * in ticker.queue.max_delta. This is necessary to keep track 00255 * of the timer overflow. 00256 */ 00257 static void schedule_interrupt(const ticker_data_t *const ticker) 00258 { 00259 ticker_event_queue_t *queue = ticker->queue; 00260 if (queue->suspended || ticker->queue->dispatching) { 00261 // Don't schedule the next interrupt until dispatching is 00262 // finished. This prevents repeated calls to interface->set_interrupt 00263 return; 00264 } 00265 00266 update_present_time(ticker); 00267 00268 if (ticker->queue->head) { 00269 us_timestamp_t present = ticker->queue->present_time; 00270 us_timestamp_t match_time = ticker->queue->head->timestamp; 00271 00272 // if the event at the head of the queue is in the past then schedule 00273 // it immediately. 00274 if (match_time <= present) { 00275 ticker->interface->fire_interrupt(); 00276 return; 00277 } 00278 00279 timestamp_t match_tick = compute_tick_round_up(ticker, match_time); 00280 00281 // The same tick should never occur since match_tick is rounded up. 00282 // If the same tick is returned scheduling will not work correctly. 00283 MBED_ASSERT(match_tick != queue->tick_last_read); 00284 00285 ticker->interface->set_interrupt(match_tick); 00286 timestamp_t cur_tick = ticker->interface->read(); 00287 00288 if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) { 00289 ticker->interface->fire_interrupt(); 00290 } 00291 } else { 00292 uint32_t match_tick = 00293 (queue->tick_last_read + queue->max_delta) & queue->bitmask; 00294 ticker->interface->set_interrupt(match_tick); 00295 } 00296 } 00297 00298 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler) 00299 { 00300 initialize(ticker); 00301 00302 core_util_critical_section_enter(); 00303 set_handler(ticker, handler); 00304 core_util_critical_section_exit(); 00305 } 00306 00307 void ticker_irq_handler(const ticker_data_t *const ticker) 00308 { 00309 core_util_critical_section_enter(); 00310 00311 ticker->interface->clear_interrupt(); 00312 if (ticker->queue->suspended) { 00313 core_util_critical_section_exit(); 00314 return; 00315 } 00316 00317 /* Go through all the pending TimerEvents */ 00318 ticker->queue->dispatching = true; 00319 while (1) { 00320 if (ticker->queue->head == NULL) { 00321 break; 00322 } 00323 00324 // update the current timestamp used by the queue 00325 update_present_time(ticker); 00326 00327 if (ticker->queue->head->timestamp <= ticker->queue->present_time) { 00328 // This event was in the past: 00329 // point to the following one and execute its handler 00330 ticker_event_t *p = ticker->queue->head; 00331 ticker->queue->head = ticker->queue->head->next; 00332 if (ticker->queue->event_handler != NULL) { 00333 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events 00334 } 00335 /* Note: We continue back to examining the head because calling the 00336 * event handler may have altered the chain of pending events. */ 00337 } else { 00338 break; 00339 } 00340 } 00341 ticker->queue->dispatching = false; 00342 00343 schedule_interrupt(ticker); 00344 00345 core_util_critical_section_exit(); 00346 } 00347 00348 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id) 00349 { 00350 core_util_critical_section_enter(); 00351 00352 // update the current timestamp 00353 update_present_time(ticker); 00354 us_timestamp_t absolute_timestamp = convert_timestamp( 00355 ticker->queue->present_time, 00356 timestamp 00357 ); 00358 00359 // defer to ticker_insert_event_us 00360 ticker_insert_event_us( 00361 ticker, 00362 obj, absolute_timestamp, id 00363 ); 00364 00365 core_util_critical_section_exit(); 00366 } 00367 00368 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id) 00369 { 00370 core_util_critical_section_enter(); 00371 00372 // update the current timestamp 00373 update_present_time(ticker); 00374 00375 // initialise our data 00376 obj->timestamp = timestamp; 00377 obj->id = id; 00378 00379 /* Go through the list until we either reach the end, or find 00380 an element this should come before (which is possibly the 00381 head). */ 00382 ticker_event_t *prev = NULL, *p = ticker->queue->head; 00383 while (p != NULL) { 00384 /* check if we come before p */ 00385 if (timestamp < p->timestamp) { 00386 break; 00387 } 00388 /* go to the next element */ 00389 prev = p; 00390 p = p->next; 00391 } 00392 00393 /* if we're at the end p will be NULL, which is correct */ 00394 obj->next = p; 00395 00396 /* if prev is NULL we're at the head */ 00397 if (prev == NULL) { 00398 ticker->queue->head = obj; 00399 } else { 00400 prev->next = obj; 00401 } 00402 00403 if (prev == NULL || timestamp <= ticker->queue->present_time) { 00404 schedule_interrupt(ticker); 00405 } 00406 00407 core_util_critical_section_exit(); 00408 } 00409 00410 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj) 00411 { 00412 core_util_critical_section_enter(); 00413 00414 // remove this object from the list 00415 if (ticker->queue->head == obj) { 00416 // first in the list, so just drop me 00417 ticker->queue->head = obj->next; 00418 schedule_interrupt(ticker); 00419 } else { 00420 // find the object before me, then drop me 00421 ticker_event_t *p = ticker->queue->head; 00422 while (p != NULL) { 00423 if (p->next == obj) { 00424 p->next = obj->next; 00425 break; 00426 } 00427 p = p->next; 00428 } 00429 } 00430 00431 core_util_critical_section_exit(); 00432 } 00433 00434 timestamp_t ticker_read(const ticker_data_t *const ticker) 00435 { 00436 return ticker_read_us(ticker); 00437 } 00438 00439 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker) 00440 { 00441 us_timestamp_t ret; 00442 00443 initialize(ticker); 00444 00445 core_util_critical_section_enter(); 00446 update_present_time(ticker); 00447 ret = ticker->queue->present_time; 00448 core_util_critical_section_exit(); 00449 00450 return ret; 00451 } 00452 00453 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp) 00454 { 00455 int ret = 0; 00456 00457 /* if head is NULL, there are no pending events */ 00458 core_util_critical_section_enter(); 00459 if (data->queue->head != NULL) { 00460 *timestamp = data->queue->head->timestamp; 00461 ret = 1; 00462 } 00463 core_util_critical_section_exit(); 00464 00465 return ret; 00466 } 00467 00468 void ticker_suspend(const ticker_data_t *const ticker) 00469 { 00470 core_util_critical_section_enter(); 00471 00472 ticker->queue->suspended = true; 00473 00474 core_util_critical_section_exit(); 00475 } 00476 00477 void ticker_resume(const ticker_data_t *const ticker) 00478 { 00479 core_util_critical_section_enter(); 00480 00481 ticker->queue->suspended = false; 00482 if (ticker->queue->initialized) { 00483 ticker->queue->tick_last_read = ticker->interface->read(); 00484 00485 update_present_time(ticker); 00486 schedule_interrupt(ticker); 00487 } else { 00488 initialize(ticker); 00489 } 00490 00491 core_util_critical_section_exit(); 00492 }
Generated on Tue Jul 12 2022 13:54:34 by
