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