Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

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