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 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 Thu Jul 14 2022 14:36:20 by
