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