Thomas Lyp / DevLibMemoryController

Dependencies:   FastPWM

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