Kenji Arai / mbed-dev4BLE

Fork of mbed-dev by mbed official

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         ticker->interface->set_interrupt(match_tick);
00239         timestamp_t cur_tick = ticker->interface->read();
00240 
00241         if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) {
00242             ticker->interface->fire_interrupt();
00243         }
00244     } else {
00245         uint32_t match_tick =
00246                 (queue->tick_last_read + queue->max_delta) & queue->bitmask;
00247         ticker->interface->set_interrupt(match_tick);
00248     }
00249 }
00250 
00251 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
00252 {
00253     initialize(ticker);
00254 
00255     core_util_critical_section_enter();
00256     set_handler(ticker, handler);
00257     core_util_critical_section_exit();
00258 }
00259 
00260 void ticker_irq_handler(const ticker_data_t *const ticker)
00261 {
00262     core_util_critical_section_enter();
00263 
00264     ticker->interface->clear_interrupt();
00265 
00266     /* Go through all the pending TimerEvents */
00267     while (1) {
00268         if (ticker->queue->head == NULL) {
00269             break;
00270         }
00271 
00272         // update the current timestamp used by the queue 
00273         update_present_time(ticker);
00274 
00275         if (ticker->queue->head->timestamp <= ticker->queue->present_time) { 
00276             // This event was in the past:
00277             //      point to the following one and execute its handler
00278             ticker_event_t *p = ticker->queue->head;
00279             ticker->queue->head = ticker->queue->head->next;
00280             if (ticker->queue->event_handler != NULL) {
00281                 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
00282             }
00283             /* Note: We continue back to examining the head because calling the
00284              * event handler may have altered the chain of pending events. */
00285         } else {
00286             break;
00287         } 
00288     }
00289 
00290     schedule_interrupt(ticker);
00291 
00292     core_util_critical_section_exit();
00293 }
00294 
00295 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
00296 {
00297     core_util_critical_section_enter();
00298 
00299     // update the current timestamp
00300     update_present_time(ticker);
00301     us_timestamp_t absolute_timestamp = convert_timestamp(
00302         ticker->queue->present_time, 
00303         timestamp
00304     );
00305 
00306     // defer to ticker_insert_event_us
00307     ticker_insert_event_us(
00308         ticker, 
00309         obj, absolute_timestamp, id
00310     );
00311 
00312     core_util_critical_section_exit();
00313 }
00314 
00315 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
00316 {
00317     core_util_critical_section_enter();
00318 
00319     // update the current timestamp
00320     update_present_time(ticker);
00321 
00322     // initialise our data
00323     obj->timestamp = timestamp;
00324     obj->id = id;
00325 
00326     /* Go through the list until we either reach the end, or find
00327        an element this should come before (which is possibly the
00328        head). */
00329     ticker_event_t *prev = NULL, *p = ticker->queue->head;
00330     while (p != NULL) {
00331         /* check if we come before p */
00332         if (timestamp < p->timestamp) {
00333             break;
00334         }
00335         /* go to the next element */
00336         prev = p;
00337         p = p->next;
00338     }
00339     
00340     /* if we're at the end p will be NULL, which is correct */
00341     obj->next = p;
00342 
00343     /* if prev is NULL we're at the head */
00344     if (prev == NULL) {
00345         ticker->queue->head = obj;
00346     } else {
00347         prev->next = obj;
00348     }
00349 
00350     schedule_interrupt(ticker);
00351 
00352     core_util_critical_section_exit();
00353 
00354 }
00355 
00356 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
00357 {
00358     core_util_critical_section_enter();
00359 
00360     // remove this object from the list
00361     if (ticker->queue->head == obj) {
00362         // first in the list, so just drop me
00363         ticker->queue->head = obj->next;
00364         schedule_interrupt(ticker);
00365     } else {
00366         // find the object before me, then drop me
00367         ticker_event_t* p = ticker->queue->head;
00368         while (p != NULL) {
00369             if (p->next == obj) {
00370                 p->next = obj->next;
00371                 break;
00372             }
00373             p = p->next;
00374         }
00375     }
00376 
00377     core_util_critical_section_exit();
00378 }
00379 
00380 timestamp_t ticker_read(const ticker_data_t *const ticker)
00381 {
00382     return ticker_read_us(ticker);
00383 }
00384 
00385 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
00386 {
00387     initialize(ticker);
00388 
00389     core_util_critical_section_enter();
00390     update_present_time(ticker);
00391     core_util_critical_section_exit();
00392 
00393     return ticker->queue->present_time;
00394 }
00395 
00396 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
00397 {
00398     int ret = 0;
00399 
00400     /* if head is NULL, there are no pending events */
00401     core_util_critical_section_enter();
00402     if (data->queue->head != NULL) {
00403         *timestamp = data->queue->head->timestamp;
00404         ret = 1;
00405     }
00406     core_util_critical_section_exit();
00407 
00408     return ret;
00409 }