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