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.
Fork of mbed-dev by
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 20:37:46 by
