mbed
Fork of mbed-dev by
Embed:
(wiki syntax)
Show/hide line numbers
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 ticker->interface->set_interrupt(match_tick); 00239 timestamp_t cur_tick = ticker->interface->read(); 00240 00241 if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) { 00242 ticker->interface->fire_interrupt(); 00243 } 00244 } else { 00245 uint32_t match_tick = 00246 (queue->tick_last_read + queue->max_delta) & queue->bitmask; 00247 ticker->interface->set_interrupt(match_tick); 00248 } 00249 } 00250 00251 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler) 00252 { 00253 initialize(ticker); 00254 00255 core_util_critical_section_enter(); 00256 set_handler(ticker, handler); 00257 core_util_critical_section_exit(); 00258 } 00259 00260 void ticker_irq_handler(const ticker_data_t *const ticker) 00261 { 00262 core_util_critical_section_enter(); 00263 00264 ticker->interface->clear_interrupt(); 00265 00266 /* Go through all the pending TimerEvents */ 00267 while (1) { 00268 if (ticker->queue->head == NULL) { 00269 break; 00270 } 00271 00272 // update the current timestamp used by the queue 00273 update_present_time(ticker); 00274 00275 if (ticker->queue->head->timestamp <= ticker->queue->present_time) { 00276 // This event was in the past: 00277 // point to the following one and execute its handler 00278 ticker_event_t *p = ticker->queue->head; 00279 ticker->queue->head = ticker->queue->head->next; 00280 if (ticker->queue->event_handler != NULL) { 00281 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events 00282 } 00283 /* Note: We continue back to examining the head because calling the 00284 * event handler may have altered the chain of pending events. */ 00285 } else { 00286 break; 00287 } 00288 } 00289 00290 schedule_interrupt(ticker); 00291 00292 core_util_critical_section_exit(); 00293 } 00294 00295 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id) 00296 { 00297 core_util_critical_section_enter(); 00298 00299 // update the current timestamp 00300 update_present_time(ticker); 00301 us_timestamp_t absolute_timestamp = convert_timestamp( 00302 ticker->queue->present_time, 00303 timestamp 00304 ); 00305 00306 // defer to ticker_insert_event_us 00307 ticker_insert_event_us( 00308 ticker, 00309 obj, absolute_timestamp, id 00310 ); 00311 00312 core_util_critical_section_exit(); 00313 } 00314 00315 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id) 00316 { 00317 core_util_critical_section_enter(); 00318 00319 // update the current timestamp 00320 update_present_time(ticker); 00321 00322 // initialise our data 00323 obj->timestamp = timestamp; 00324 obj->id = id; 00325 00326 /* Go through the list until we either reach the end, or find 00327 an element this should come before (which is possibly the 00328 head). */ 00329 ticker_event_t *prev = NULL, *p = ticker->queue->head; 00330 while (p != NULL) { 00331 /* check if we come before p */ 00332 if (timestamp < p->timestamp) { 00333 break; 00334 } 00335 /* go to the next element */ 00336 prev = p; 00337 p = p->next; 00338 } 00339 00340 /* if we're at the end p will be NULL, which is correct */ 00341 obj->next = p; 00342 00343 /* if prev is NULL we're at the head */ 00344 if (prev == NULL) { 00345 ticker->queue->head = obj; 00346 } else { 00347 prev->next = obj; 00348 } 00349 00350 schedule_interrupt(ticker); 00351 00352 core_util_critical_section_exit(); 00353 00354 } 00355 00356 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj) 00357 { 00358 core_util_critical_section_enter(); 00359 00360 // remove this object from the list 00361 if (ticker->queue->head == obj) { 00362 // first in the list, so just drop me 00363 ticker->queue->head = obj->next; 00364 schedule_interrupt(ticker); 00365 } else { 00366 // find the object before me, then drop me 00367 ticker_event_t* p = ticker->queue->head; 00368 while (p != NULL) { 00369 if (p->next == obj) { 00370 p->next = obj->next; 00371 break; 00372 } 00373 p = p->next; 00374 } 00375 } 00376 00377 core_util_critical_section_exit(); 00378 } 00379 00380 timestamp_t ticker_read(const ticker_data_t *const ticker) 00381 { 00382 return ticker_read_us(ticker); 00383 } 00384 00385 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker) 00386 { 00387 initialize(ticker); 00388 00389 core_util_critical_section_enter(); 00390 update_present_time(ticker); 00391 core_util_critical_section_exit(); 00392 00393 return ticker->queue->present_time; 00394 } 00395 00396 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp) 00397 { 00398 int ret = 0; 00399 00400 /* if head is NULL, there are no pending events */ 00401 core_util_critical_section_enter(); 00402 if (data->queue->head != NULL) { 00403 *timestamp = data->queue->head->timestamp; 00404 ret = 1; 00405 } 00406 core_util_critical_section_exit(); 00407 00408 return ret; 00409 }
Generated on Tue Jul 12 2022 18:02:51 by
1.7.2
