| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 | /* mbed Microcontroller Library * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include <stdio.h>#include <stddef.h>#include "hal/ticker_api.h"#include "platform/mbed_critical.h"#include "mbed_assert.h"static void schedule_interrupt(const ticker_data_t *const ticker);static void update_present_time(const ticker_data_t *const ticker);/* * Initialize a ticker instance. */static void initialize(const ticker_data_t *ticker){    // return if the queue has already been initialized, in that case the    // interface used by the queue is already initialized.    if (ticker->queue->initialized) {        return;    }    ticker->interface->init();    const ticker_info_t *info = ticker->interface->get_info();    uint32_t frequency = info->frequency;    if (info->frequency == 0) {        MBED_ASSERT(0);        frequency = 1000000;    }    uint8_t frequency_shifts = 0;    for (uint8_t i = 31; i > 0; --i) {        if ((1 << i) == frequency) {            frequency_shifts = i;            break;        }    }    uint32_t bits = info->bits;    if ((info->bits > 32) || (info->bits < 4)) {        MBED_ASSERT(0);        bits = 32;    }    uint32_t max_delta = 0x7 << (bits - 4); // 7/16th    uint64_t max_delta_us =        ((uint64_t)max_delta * 1000000 + frequency - 1) / frequency;    ticker->queue->event_handler = NULL;    ticker->queue->head = NULL;    ticker->queue->tick_last_read = ticker->interface->read();    ticker->queue->tick_remainder = 0;    ticker->queue->frequency = frequency;    ticker->queue->frequency_shifts = frequency_shifts;    ticker->queue->bitmask = ((uint64_t)1 << bits) - 1;    ticker->queue->max_delta = max_delta;    ticker->queue->max_delta_us = max_delta_us;    ticker->queue->present_time = 0;    ticker->queue->dispatching = false;    ticker->queue->initialized = true;    update_present_time(ticker);    schedule_interrupt(ticker);}/** * Set the event handler function of a ticker instance. */static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler){    ticker->queue->event_handler = handler;}/* * Convert a 32 bit timestamp into a 64 bit timestamp. * * A 64 bit timestamp is used as the point of time of reference while the * timestamp to convert is relative to this point of time. * * The lower 32 bits of the timestamp returned will be equal to the timestamp to * convert. * * If the timestamp to convert is less than the lower 32 bits of the time * reference then the timestamp to convert is seen as an overflowed value and * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit * of the reference point + 1. * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the * reference point. * * @param ref: The 64 bit timestamp of reference. * @param timestamp: The timestamp to convert. */static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp){    bool overflow = timestamp < ((timestamp_t) ref) ? true : false;    us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;    if (overflow) {        result += (1ULL << 32);    }    return result;}/** * Update the present timestamp value of a ticker. */static void update_present_time(const ticker_data_t *const ticker){    ticker_event_queue_t *queue = ticker->queue;    uint32_t ticker_time = ticker->interface->read();    if (ticker_time == ticker->queue->tick_last_read) {        // No work to do        return;    }    uint64_t elapsed_ticks = (ticker_time - queue->tick_last_read) & queue->bitmask;    queue->tick_last_read = ticker_time;    uint64_t elapsed_us;    if (1000000 == queue->frequency) {        // Optimized for 1MHz        elapsed_us = elapsed_ticks;    } else if (0 != queue->frequency_shifts) {        // Optimized for frequencies divisible by 2        uint64_t us_x_ticks = elapsed_ticks * 1000000;        elapsed_us = us_x_ticks >> queue->frequency_shifts;        // Update remainder        queue->tick_remainder += us_x_ticks - (elapsed_us << queue->frequency_shifts);        if (queue->tick_remainder >= queue->frequency) {            elapsed_us += 1;            queue->tick_remainder -= queue->frequency;        }    } else {        // General case        uint64_t us_x_ticks = elapsed_ticks * 1000000;        elapsed_us = us_x_ticks / queue->frequency;        // Update remainder        queue->tick_remainder += us_x_ticks - elapsed_us * queue->frequency;        if (queue->tick_remainder >= queue->frequency) {            elapsed_us += 1;            queue->tick_remainder -= queue->frequency;        }    }    // Update current time    queue->present_time += elapsed_us;}/** * Given the absolute timestamp compute the hal tick timestamp. */static timestamp_t compute_tick(const ticker_data_t *const ticker, us_timestamp_t timestamp){    ticker_event_queue_t *queue = ticker->queue;    us_timestamp_t delta_us = timestamp - queue->present_time;    timestamp_t delta = ticker->queue->max_delta;    if (delta_us <=  ticker->queue->max_delta_us) {        // Checking max_delta_us ensures the operation will not overflow        if (1000000 == queue->frequency) {            // Optimized for 1MHz            delta = delta_us;            if (delta > ticker->queue->max_delta) {                delta = ticker->queue->max_delta;            }        } else if (0 != queue->frequency_shifts) {            // Optimized frequencies divisible by 2            delta = (delta_us << ticker->queue->frequency_shifts) / 1000000;            if (delta > ticker->queue->max_delta) {                delta = ticker->queue->max_delta;            }        } else {            // General case            delta = delta_us * queue->frequency / 1000000;            if (delta > ticker->queue->max_delta) {                delta = ticker->queue->max_delta;            }        }    }    return (queue->tick_last_read + delta) & queue->bitmask;}/** * Return 1 if the tick has incremented to or past match_tick, otherwise 0. */int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, timestamp_t match_tick){    if (match_tick > prev_tick) {        return (cur_tick >= match_tick) || (cur_tick < prev_tick);    } else {        return (cur_tick < prev_tick) && (cur_tick >= match_tick);    }}/** * Compute the time when the interrupt has to be triggered and schedule it. * * If there is no event in the queue or the next event to execute is in more * than ticker.queue.max_delta ticks from now then the ticker irq will be * scheduled in ticker.queue.max_delta ticks. Otherwise the irq will be * scheduled to happen when the running counter reach the timestamp of the * first event in the queue. * * @note If there is no event in the queue then the interrupt is scheduled to * in ticker.queue.max_delta. This is necessary to keep track * of the timer overflow. */static void schedule_interrupt(const ticker_data_t *const ticker){    ticker_event_queue_t *queue = ticker->queue;    if (ticker->queue->dispatching) {        // Don't schedule the next interrupt until dispatching is        // finished. This prevents repeated calls to interface->set_interrupt        return;    }    update_present_time(ticker);    if (ticker->queue->head) {        us_timestamp_t present = ticker->queue->present_time;        us_timestamp_t match_time = ticker->queue->head->timestamp;        // if the event at the head of the queue is in the past then schedule        // it immediately.        if (match_time <= present) {            ticker->interface->fire_interrupt();            return;        }        timestamp_t match_tick = compute_tick(ticker, match_time);        // The time has been checked to be future, but it could still round        // to the last tick as a result of us to ticks conversion        if (match_tick == queue->tick_last_read) {            // Match time has already expired so fire immediately            ticker->interface->fire_interrupt();            return;        }        ticker->interface->set_interrupt(match_tick);        timestamp_t cur_tick = ticker->interface->read();        if (_ticker_match_interval_passed(queue->tick_last_read, cur_tick, match_tick)) {            ticker->interface->fire_interrupt();        }    } else {        uint32_t match_tick =            (queue->tick_last_read + queue->max_delta) & queue->bitmask;        ticker->interface->set_interrupt(match_tick);    }}void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler){    initialize(ticker);    core_util_critical_section_enter();    set_handler(ticker, handler);    core_util_critical_section_exit();}void ticker_irq_handler(const ticker_data_t *const ticker){    core_util_critical_section_enter();    ticker->interface->clear_interrupt();    /* Go through all the pending TimerEvents */    ticker->queue->dispatching = true;    while (1) {        if (ticker->queue->head == NULL) {            break;        }        // update the current timestamp used by the queue        update_present_time(ticker);        if (ticker->queue->head->timestamp <= ticker->queue->present_time) {            // This event was in the past:            //      point to the following one and execute its handler            ticker_event_t *p = ticker->queue->head;            ticker->queue->head = ticker->queue->head->next;            if (ticker->queue->event_handler != NULL) {                (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events            }            /* Note: We continue back to examining the head because calling the             * event handler may have altered the chain of pending events. */        } else {            break;        }    }    ticker->queue->dispatching = false;    schedule_interrupt(ticker);    core_util_critical_section_exit();}void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id){    core_util_critical_section_enter();    // update the current timestamp    update_present_time(ticker);    us_timestamp_t absolute_timestamp = convert_timestamp(                                            ticker->queue->present_time,                                            timestamp                                        );    // defer to ticker_insert_event_us    ticker_insert_event_us(        ticker,        obj, absolute_timestamp, id    );    core_util_critical_section_exit();}void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id){    core_util_critical_section_enter();    // update the current timestamp    update_present_time(ticker);    // initialise our data    obj->timestamp = timestamp;    obj->id = id;    /* Go through the list until we either reach the end, or find       an element this should come before (which is possibly the       head). */    ticker_event_t *prev = NULL, *p = ticker->queue->head;    while (p != NULL) {        /* check if we come before p */        if (timestamp < p->timestamp) {            break;        }        /* go to the next element */        prev = p;        p = p->next;    }    /* if we're at the end p will be NULL, which is correct */    obj->next = p;    /* if prev is NULL we're at the head */    if (prev == NULL) {        ticker->queue->head = obj;        schedule_interrupt(ticker);    } else {        prev->next = obj;    }    core_util_critical_section_exit();}void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj){    core_util_critical_section_enter();    // remove this object from the list    if (ticker->queue->head == obj) {        // first in the list, so just drop me        ticker->queue->head = obj->next;        schedule_interrupt(ticker);    } else {        // find the object before me, then drop me        ticker_event_t *p = ticker->queue->head;        while (p != NULL) {            if (p->next == obj) {                p->next = obj->next;                break;            }            p = p->next;        }    }    core_util_critical_section_exit();}timestamp_t ticker_read(const ticker_data_t *const ticker){    return ticker_read_us(ticker);}us_timestamp_t ticker_read_us(const ticker_data_t *const ticker){    initialize(ticker);    core_util_critical_section_enter();    update_present_time(ticker);    core_util_critical_section_exit();    return ticker->queue->present_time;}int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp){    int ret = 0;    /* if head is NULL, there are no pending events */    core_util_critical_section_enter();    if (data->queue->head != NULL) {        *timestamp = data->queue->head->timestamp;        ret = 1;    }    core_util_critical_section_exit();    return ret;}
 |