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