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->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 19:10:22 by
 1.7.2
 1.7.2 
    