Gleb Klochkov / Mbed OS Climatcontroll_Main

Dependencies:   esp8266-driver

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