Marco Mayer / Mbed OS Queue
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  *
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 }