forked

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 
00021 static void schedule_interrupt(const ticker_data_t *const ticker);
00022 static void update_present_time(const ticker_data_t *const ticker);
00023 
00024 /*
00025  * Initialize a ticker instance.  
00026  */
00027 static void initialize(const ticker_data_t *ticker)
00028 {
00029     // return if the queue has already been initialized, in that case the 
00030     // interface used by the queue is already initialized.
00031     if (ticker->queue->initialized) { 
00032         return;
00033     }
00034 
00035     ticker->interface->init();
00036     
00037     ticker->queue->event_handler = NULL;
00038     ticker->queue->head = NULL;
00039     ticker->queue->present_time = 0;
00040     ticker->queue->initialized = true;
00041     
00042     update_present_time(ticker);
00043     schedule_interrupt(ticker);
00044 }
00045 
00046 /**
00047  * Set the event handler function of a ticker instance. 
00048  */
00049 static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
00050 {
00051     ticker->queue->event_handler = handler;
00052 }
00053 
00054 /*
00055  * Convert a 32 bit timestamp into a 64 bit timestamp.
00056  *
00057  * A 64 bit timestamp is used as the point of time of reference while the 
00058  * timestamp to convert is relative to this point of time. 
00059  *
00060  * The lower 32 bits of the timestamp returned will be equal to the timestamp to 
00061  * convert. 
00062  * 
00063  * If the timestamp to convert is less than the lower 32 bits of the time 
00064  * reference then the timestamp to convert is seen as an overflowed value and 
00065  * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit 
00066  * of the reference point + 1. 
00067  * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the 
00068  * reference point. 
00069  *
00070  * @param ref: The 64 bit timestamp of reference.
00071  * @param timestamp: The timestamp to convert.
00072  */
00073 static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp)
00074 {
00075     bool overflow = timestamp < ((timestamp_t) ref) ? true : false;
00076 
00077     us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;
00078     if (overflow) { 
00079         result += (1ULL<<32);
00080     }
00081 
00082     return result;
00083 }
00084 
00085 /**
00086  * Update the present timestamp value of a ticker.
00087  */
00088 static void update_present_time(const ticker_data_t *const ticker)
00089 { 
00090     ticker->queue->present_time = convert_timestamp(
00091         ticker->queue->present_time, 
00092         ticker->interface->read()
00093     );
00094 }
00095 
00096 /**
00097  * Compute the time when the interrupt has to be triggered and schedule it.  
00098  * 
00099  * If there is no event in the queue or the next event to execute is in more 
00100  * than MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us from now then the ticker 
00101  * irq will be scheduled in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us.
00102  * Otherwise the irq will be scheduled to happen when the running counter reach 
00103  * the timestamp of the first event in the queue.
00104  * 
00105  * @note If there is no event in the queue then the interrupt is scheduled to 
00106  * in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA. This is necessary to keep track 
00107  * of the timer overflow.
00108  */
00109 static void schedule_interrupt(const ticker_data_t *const ticker)
00110 {
00111     update_present_time(ticker);
00112     uint32_t relative_timeout = MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA;
00113 
00114     if (ticker->queue->head) {
00115         us_timestamp_t present = ticker->queue->present_time;
00116         us_timestamp_t next_event_timestamp = ticker->queue->head->timestamp;
00117 
00118         // if the event at the head of the queue is in the past then schedule
00119         // it immediately.
00120         if (next_event_timestamp < present) {
00121             relative_timeout = 0;
00122         } else if ((next_event_timestamp - present) < MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA) {
00123             relative_timeout = next_event_timestamp - present;
00124         }
00125     } 
00126 
00127     ticker->interface->set_interrupt(ticker->queue->present_time + relative_timeout);
00128 }
00129 
00130 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
00131 {
00132     initialize(ticker);
00133     set_handler(ticker, handler);
00134 }
00135 
00136 void ticker_irq_handler(const ticker_data_t *const ticker)
00137 {
00138     ticker->interface->clear_interrupt();
00139 
00140     /* Go through all the pending TimerEvents */
00141     while (1) {
00142         if (ticker->queue->head == NULL) {
00143             break;
00144         }
00145 
00146         // update the current timestamp used by the queue 
00147         update_present_time(ticker);
00148 
00149         if (ticker->queue->head->timestamp <= ticker->queue->present_time) { 
00150             // This event was in the past:
00151             //      point to the following one and execute its handler
00152             ticker_event_t *p = ticker->queue->head;
00153             ticker->queue->head = ticker->queue->head->next;
00154             if (ticker->queue->event_handler != NULL) {
00155                 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
00156             }
00157             /* Note: We continue back to examining the head because calling the
00158              * event handler may have altered the chain of pending events. */
00159         } else {
00160             break;
00161         } 
00162     }
00163 
00164     schedule_interrupt(ticker);
00165 }
00166 
00167 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
00168 {
00169     core_util_critical_section_enter();
00170 
00171     // update the current timestamp
00172     update_present_time(ticker);
00173     us_timestamp_t absolute_timestamp = convert_timestamp(
00174         ticker->queue->present_time, 
00175         timestamp
00176     );
00177     core_util_critical_section_exit();
00178 
00179     // defer to ticker_insert_event_us
00180     ticker_insert_event_us(
00181         ticker, 
00182         obj, absolute_timestamp, id
00183     );
00184 }
00185 
00186 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
00187 {
00188     core_util_critical_section_enter();
00189 
00190     // update the current timestamp
00191     update_present_time(ticker);
00192 
00193     // initialise our data
00194     obj->timestamp = timestamp;
00195     obj->id = id;
00196 
00197     /* Go through the list until we either reach the end, or find
00198        an element this should come before (which is possibly the
00199        head). */
00200     ticker_event_t *prev = NULL, *p = ticker->queue->head;
00201     while (p != NULL) {
00202         /* check if we come before p */
00203         if (timestamp < p->timestamp) {
00204             break;
00205         }
00206         /* go to the next element */
00207         prev = p;
00208         p = p->next;
00209     }
00210     
00211     /* if we're at the end p will be NULL, which is correct */
00212     obj->next = p;
00213 
00214     /* if prev is NULL we're at the head */
00215     if (prev == NULL) {
00216         ticker->queue->head = obj;
00217     } else {
00218         prev->next = obj;
00219     }
00220 
00221     schedule_interrupt(ticker);
00222 
00223     core_util_critical_section_exit();
00224 }
00225 
00226 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
00227 {
00228     core_util_critical_section_enter();
00229 
00230     // remove this object from the list
00231     if (ticker->queue->head == obj) {
00232         // first in the list, so just drop me
00233         ticker->queue->head = obj->next;
00234         schedule_interrupt(ticker);
00235     } else {
00236         // find the object before me, then drop me
00237         ticker_event_t* p = ticker->queue->head;
00238         while (p != NULL) {
00239             if (p->next == obj) {
00240                 p->next = obj->next;
00241                 break;
00242             }
00243             p = p->next;
00244         }
00245     }
00246 
00247     core_util_critical_section_exit();
00248 }
00249 
00250 timestamp_t ticker_read(const ticker_data_t *const ticker)
00251 {
00252     return ticker_read_us(ticker);
00253 }
00254 
00255 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
00256 {
00257     update_present_time(ticker);
00258     return ticker->queue->present_time;
00259 }
00260 
00261 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
00262 {
00263     int ret = 0;
00264 
00265     /* if head is NULL, there are no pending events */
00266     core_util_critical_section_enter();
00267     if (data->queue->head != NULL) {
00268         *timestamp = data->queue->head->timestamp;
00269         ret = 1;
00270     }
00271     core_util_critical_section_exit();
00272 
00273     return ret;
00274 }