mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mbed_ticker_api.c Source File

mbed_ticker_api.c

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2015 ARM Limited
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 #include <stdio.h>
00018 #include <stddef.h>
00019 #include "hal/ticker_api.h"
00020 #include "platform/mbed_critical.h"
00021 #include "platform/mbed_assert.h"
00022 
00023 static void schedule_interrupt(const ticker_data_t *const ticker);
00024 static void update_present_time(const ticker_data_t *const ticker);
00025 
00026 /*
00027  * Initialize a ticker instance.
00028  */
00029 static void initialize(const ticker_data_t *ticker)
00030 {
00031     // return if the queue has already been initialized, in that case the
00032     // interface used by the queue is already initialized.
00033     if (ticker->queue->initialized) {
00034         return;
00035     }
00036     if (ticker->queue->suspended) {
00037         return;
00038     }
00039 
00040     ticker->interface->init();
00041 
00042     const ticker_info_t *info = ticker->interface->get_info();
00043     uint32_t frequency = info->frequency;
00044     if (info->frequency == 0) {
00045         MBED_ASSERT(0);
00046         frequency = 1000000;
00047     }
00048 
00049     uint8_t frequency_shifts = 0;
00050     for (uint8_t i = 31; i > 0; --i) {
00051         if ((1U << i) == frequency) {
00052             frequency_shifts = i;
00053             break;
00054         }
00055     }
00056 
00057     uint32_t bits = info->bits;
00058     if ((info->bits > 32) || (info->bits < 4)) {
00059         MBED_ASSERT(0);
00060         bits = 32;
00061     }
00062     uint32_t max_delta = 0x7 << (bits - 4); // 7/16th
00063     uint64_t max_delta_us =
00064         ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency;
00065 
00066     ticker->queue->event_handler = NULL;
00067     ticker->queue->head = NULL;
00068     ticker->queue->tick_last_read = ticker->interface->read();
00069     ticker->queue->tick_remainder = 0;
00070     ticker->queue->frequency = frequency;
00071     ticker->queue->frequency_shifts = frequency_shifts;
00072     ticker->queue->bitmask = ((uint64_t)1 << bits) - 1;
00073     ticker->queue->max_delta = max_delta;
00074     ticker->queue->max_delta_us = max_delta_us;
00075     ticker->queue->present_time = 0;
00076     ticker->queue->dispatching = false;
00077     ticker->queue->suspended = false;
00078     ticker->queue->initialized = true;
00079 
00080     update_present_time(ticker);
00081     schedule_interrupt(ticker);
00082 }
00083 
00084 /**
00085  * Set the event handler function of a ticker instance.
00086  */
00087 static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
00088 {
00089     ticker->queue->event_handler = handler;
00090 }
00091 
00092 /*
00093  * Convert a 32 bit timestamp into a 64 bit timestamp.
00094  *
00095  * A 64 bit timestamp is used as the point of time of reference while the
00096  * timestamp to convert is relative to this point of time.
00097  *
00098  * The lower 32 bits of the timestamp returned will be equal to the timestamp to
00099  * convert.
00100  *
00101  * If the timestamp to convert is less than the lower 32 bits of the time
00102  * reference then the timestamp to convert is seen as an overflowed value and
00103  * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit
00104  * of the reference point + 1.
00105  * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the
00106  * reference point.
00107  *
00108  * @param ref: The 64 bit timestamp of reference.
00109  * @param timestamp: The timestamp to convert.
00110  */
00111 static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp)
00112 {
00113     bool overflow = timestamp < ((timestamp_t) ref) ? true : false;
00114 
00115     us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;
00116     if (overflow) {
00117         result += (1ULL << 32);
00118     }
00119 
00120     return result;
00121 }
00122 
00123 /**
00124  * Update the present timestamp value of a ticker.
00125  */
00126 static void update_present_time(const ticker_data_t *const ticker)
00127 {
00128     ticker_event_queue_t *queue = ticker->queue;
00129     if (queue->suspended) {
00130         return;
00131     }
00132     uint32_t ticker_time = ticker->interface->read();
00133     if (ticker_time == ticker->queue->tick_last_read) {
00134         // No work to do
00135         return;
00136     }
00137 
00138     uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask;
00139     queue->tick_last_read = ticker_time;
00140 
00141     uint64_t elapsed_us;
00142     if (1000000 == queue->frequency) {
00143         // Optimized for 1MHz
00144 
00145         elapsed_us = elapsed_ticks;
00146     } else if (0 != queue->frequency_shifts) {
00147         // Optimized for frequencies divisible by 2
00148         uint64_t us_x_ticks = elapsed_ticks * 1000000;
00149         elapsed_us = us_x_ticks >> queue->frequency_shifts;
00150 
00151         // Update remainder
00152         queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts);
00153         if (queue->tick_remainder >= queue->frequency) {
00154             elapsed_us += 1;
00155             queue->tick_remainder -= queue->frequency;
00156         }
00157     } else {
00158         // General case
00159 
00160         uint64_t us_x_ticks = elapsed_ticks * 1000000;
00161         elapsed_us = us_x_ticks / queue->frequency;
00162 
00163         // Update remainder
00164         queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency;
00165         if (queue->tick_remainder >= queue->frequency) {
00166             elapsed_us += 1;
00167             queue->tick_remainder -= queue->frequency;
00168         }
00169     }
00170 
00171     // Update current time
00172     queue->present_time += elapsed_us;
00173 }
00174 
00175 /**
00176  * Given the absolute timestamp compute the hal tick timestamp rounded up.
00177  */
00178 static timestamp_t compute_tick_round_up(const ticker_data_t *const ticker, us_timestamp_t timestamp)
00179 {
00180     ticker_event_queue_t *queue = ticker->queue;
00181     us_timestamp_t delta_us = timestamp - queue->present_time;
00182 
00183     timestamp_t delta = ticker->queue->max_delta;
00184     if (delta_us <=  ticker->queue->max_delta_us) {
00185         // Checking max_delta_us ensures the operation will not overflow
00186 
00187         if (1000000 == queue->frequency) {
00188             // Optimized for 1MHz
00189 
00190             delta = delta_us;
00191             if (delta > ticker->queue->max_delta) {
00192                 delta = ticker->queue->max_delta;
00193             }
00194         } else if (0 != queue->frequency_shifts) {
00195             // Optimized frequencies divisible by 2
00196 
00197             delta = ((delta_us << ticker->queue->frequency_shifts) + 1000000 - 1) / 1000000;
00198             if (delta > ticker->queue->max_delta) {
00199                 delta = ticker->queue->max_delta;
00200             }
00201         } else {
00202             // General case
00203 
00204             delta = (delta_us * queue->frequency + 1000000 - 1) / 1000000;
00205             if (delta > ticker->queue->max_delta) {
00206                 delta = ticker->queue->max_delta;
00207             }
00208         }
00209     }
00210     return (queue->tick_last_read + delta) & queue->bitmask;
00211 }
00212 
00213 /**
00214  * Return 1 if the tick has incremented to or past match_tick, otherwise 0.
00215  */
00216 int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick)
00217 {
00218     if (match_tick > prev_tick) {
00219         return (cur_tick >= match_tick) || (cur_tick < prev_tick);
00220     } else {
00221         return (cur_tick < prev_tick) && (cur_tick >= match_tick);
00222     }
00223 }
00224 
00225 /**
00226  * Compute the time when the interrupt has to be triggered and schedule it.
00227  *
00228  * If there is no event in the queue or the next event to execute is in more
00229  * than ticker.queue.max_delta ticks from now then the ticker irq will be
00230  * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be
00231  * scheduled to happen when the running counter reach the timestamp of the
00232  * first event in the queue.
00233  *
00234  * @note If there is no event in the queue then the interrupt is scheduled to
00235  * in ticker.queue.max_delta. This is necessary to keep track
00236  * of the timer overflow.
00237  */
00238 static void schedule_interrupt(const ticker_data_t *const ticker)
00239 {
00240     ticker_event_queue_t *queue = ticker->queue;
00241     if (queue->suspended || ticker->queue->dispatching) {
00242         // Don't schedule the next interrupt until dispatching is
00243         // finished. This prevents repeated calls to interface->set_interrupt
00244         return;
00245     }
00246 
00247     update_present_time(ticker);
00248 
00249     if (ticker->queue->head) {
00250         us_timestamp_t present = ticker->queue->present_time;
00251         us_timestamp_t match_time = ticker->queue->head->timestamp;
00252 
00253         // if the event at the head of the queue is in the past then schedule
00254         // it immediately.
00255         if (match_time <= present) {
00256             ticker->interface->fire_interrupt();
00257             return;
00258         }
00259 
00260         timestamp_t match_tick = compute_tick_round_up(ticker, match_time);
00261 
00262         // The same tick should never occur since match_tick is rounded up.
00263         // If the same tick is returned scheduling will not work correctly.
00264         MBED_ASSERT(match_tick != queue->tick_last_read);
00265 
00266         ticker->interface->set_interrupt(match_tick);
00267         timestamp_t cur_tick = ticker->interface->read();
00268 
00269         if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) {
00270             ticker->interface->fire_interrupt();
00271         }
00272     } else {
00273         uint32_t match_tick =
00274             (queue->tick_last_read + queue->max_delta) & queue->bitmask;
00275         ticker->interface->set_interrupt(match_tick);
00276     }
00277 }
00278 
00279 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
00280 {
00281     initialize(ticker);
00282 
00283     core_util_critical_section_enter();
00284     set_handler(ticker, handler);
00285     core_util_critical_section_exit();
00286 }
00287 
00288 void ticker_irq_handler(const ticker_data_t *const ticker)
00289 {
00290     core_util_critical_section_enter();
00291 
00292     ticker->interface->clear_interrupt();
00293     if (ticker->queue->suspended) {
00294         core_util_critical_section_exit();
00295         return;
00296     }
00297 
00298     /* Go through all the pending TimerEvents */
00299     ticker->queue->dispatching = true;
00300     while (1) {
00301         if (ticker->queue->head == NULL) {
00302             break;
00303         }
00304 
00305         // update the current timestamp used by the queue
00306         update_present_time(ticker);
00307 
00308         if (ticker->queue->head->timestamp <= ticker->queue->present_time) {
00309             // This event was in the past:
00310             //      point to the following one and execute its handler
00311             ticker_event_t *p = ticker->queue->head;
00312             ticker->queue->head = ticker->queue->head->next;
00313             if (ticker->queue->event_handler != NULL) {
00314                 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
00315             }
00316             /* Note: We continue back to examining the head because calling the
00317              * event handler may have altered the chain of pending events. */
00318         } else {
00319             break;
00320         }
00321     }
00322     ticker->queue->dispatching = false;
00323 
00324     schedule_interrupt(ticker);
00325 
00326     core_util_critical_section_exit();
00327 }
00328 
00329 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
00330 {
00331     core_util_critical_section_enter();
00332 
00333     // update the current timestamp
00334     update_present_time(ticker);
00335     us_timestamp_t absolute_timestamp = convert_timestamp(
00336                                             ticker->queue->present_time,
00337                                             timestamp
00338                                         );
00339 
00340     // defer to ticker_insert_event_us
00341     ticker_insert_event_us(
00342         ticker,
00343         obj, absolute_timestamp, id
00344     );
00345 
00346     core_util_critical_section_exit();
00347 }
00348 
00349 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
00350 {
00351     core_util_critical_section_enter();
00352 
00353     // update the current timestamp
00354     update_present_time(ticker);
00355 
00356     // initialise our data
00357     obj->timestamp = timestamp;
00358     obj->id = id;
00359 
00360     /* Go through the list until we either reach the end, or find
00361        an element this should come before (which is possibly the
00362        head). */
00363     ticker_event_t *prev = NULL, *p = ticker->queue->head;
00364     while (p != NULL) {
00365         /* check if we come before p */
00366         if (timestamp < p->timestamp) {
00367             break;
00368         }
00369         /* go to the next element */
00370         prev = p;
00371         p = p->next;
00372     }
00373 
00374     /* if we're at the end p will be NULL, which is correct */
00375     obj->next = p;
00376 
00377     /* if prev is NULL we're at the head */
00378     if (prev == NULL) {
00379         ticker->queue->head = obj;
00380         schedule_interrupt(ticker);
00381     } else {
00382         prev->next = obj;
00383     }
00384 
00385     core_util_critical_section_exit();
00386 }
00387 
00388 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
00389 {
00390     core_util_critical_section_enter();
00391 
00392     // remove this object from the list
00393     if (ticker->queue->head == obj) {
00394         // first in the list, so just drop me
00395         ticker->queue->head = obj->next;
00396         schedule_interrupt(ticker);
00397     } else {
00398         // find the object before me, then drop me
00399         ticker_event_t *p = ticker->queue->head;
00400         while (p != NULL) {
00401             if (p->next == obj) {
00402                 p->next = obj->next;
00403                 break;
00404             }
00405             p = p->next;
00406         }
00407     }
00408 
00409     core_util_critical_section_exit();
00410 }
00411 
00412 timestamp_t ticker_read(const ticker_data_t *const ticker)
00413 {
00414     return ticker_read_us(ticker);
00415 }
00416 
00417 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
00418 {
00419     us_timestamp_t ret;
00420 
00421     initialize(ticker);
00422 
00423     core_util_critical_section_enter();
00424     update_present_time(ticker);
00425     ret = ticker->queue->present_time;
00426     core_util_critical_section_exit();
00427 
00428     return ret;
00429 }
00430 
00431 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
00432 {
00433     int ret = 0;
00434 
00435     /* if head is NULL, there are no pending events */
00436     core_util_critical_section_enter();
00437     if (data->queue->head != NULL) {
00438         *timestamp = data->queue->head->timestamp;
00439         ret = 1;
00440     }
00441     core_util_critical_section_exit();
00442 
00443     return ret;
00444 }
00445 
00446 void ticker_suspend(const ticker_data_t *const ticker)
00447 {
00448     core_util_critical_section_enter();
00449 
00450     ticker->queue->suspended = true;
00451 
00452     core_util_critical_section_exit();
00453 }
00454 
00455 void ticker_resume(const ticker_data_t *const ticker)
00456 {
00457     core_util_critical_section_enter();
00458 
00459     ticker->queue->suspended = false;
00460     if (ticker->queue->initialized) {
00461         ticker->queue->tick_last_read = ticker->interface->read();
00462 
00463         update_present_time(ticker);
00464         schedule_interrupt(ticker);
00465     } else {
00466         initialize(ticker);
00467     }
00468 
00469     core_util_critical_section_exit();
00470 }