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