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