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