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 OmniWheels 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 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 Fri Jul 22 2022 04:53:56 by
1.7.2
