Elijah Stanger-Jones / mbed-dev-f303
Committer:
elijahsj
Date:
Mon Nov 09 00:02:47 2020 -0500
Revision:
1:8a094db1347f
test

Who changed what in which revision?

UserRevisionLine numberNew contents of line
elijahsj 1:8a094db1347f 1 /* mbed Microcontroller Library
elijahsj 1:8a094db1347f 2 * Copyright (c) 2015 ARM Limited
elijahsj 1:8a094db1347f 3 *
elijahsj 1:8a094db1347f 4 * Licensed under the Apache License, Version 2.0 (the "License");
elijahsj 1:8a094db1347f 5 * you may not use this file except in compliance with the License.
elijahsj 1:8a094db1347f 6 * You may obtain a copy of the License at
elijahsj 1:8a094db1347f 7 *
elijahsj 1:8a094db1347f 8 * http://www.apache.org/licenses/LICENSE-2.0
elijahsj 1:8a094db1347f 9 *
elijahsj 1:8a094db1347f 10 * Unless required by applicable law or agreed to in writing, software
elijahsj 1:8a094db1347f 11 * distributed under the License is distributed on an "AS IS" BASIS,
elijahsj 1:8a094db1347f 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
elijahsj 1:8a094db1347f 13 * See the License for the specific language governing permissions and
elijahsj 1:8a094db1347f 14 * limitations under the License.
elijahsj 1:8a094db1347f 15 */
elijahsj 1:8a094db1347f 16 #include <stdio.h>
elijahsj 1:8a094db1347f 17 #include <stddef.h>
elijahsj 1:8a094db1347f 18 #include "hal/ticker_api.h"
elijahsj 1:8a094db1347f 19 #include "platform/mbed_critical.h"
elijahsj 1:8a094db1347f 20
elijahsj 1:8a094db1347f 21 static void schedule_interrupt(const ticker_data_t *const ticker);
elijahsj 1:8a094db1347f 22 static void update_present_time(const ticker_data_t *const ticker);
elijahsj 1:8a094db1347f 23
elijahsj 1:8a094db1347f 24 /*
elijahsj 1:8a094db1347f 25 * Initialize a ticker instance.
elijahsj 1:8a094db1347f 26 */
elijahsj 1:8a094db1347f 27 static void initialize(const ticker_data_t *ticker)
elijahsj 1:8a094db1347f 28 {
elijahsj 1:8a094db1347f 29 // return if the queue has already been initialized, in that case the
elijahsj 1:8a094db1347f 30 // interface used by the queue is already initialized.
elijahsj 1:8a094db1347f 31 if (ticker->queue->initialized) {
elijahsj 1:8a094db1347f 32 return;
elijahsj 1:8a094db1347f 33 }
elijahsj 1:8a094db1347f 34
elijahsj 1:8a094db1347f 35 ticker->interface->init();
elijahsj 1:8a094db1347f 36
elijahsj 1:8a094db1347f 37 ticker->queue->event_handler = NULL;
elijahsj 1:8a094db1347f 38 ticker->queue->head = NULL;
elijahsj 1:8a094db1347f 39 ticker->queue->present_time = 0;
elijahsj 1:8a094db1347f 40 ticker->queue->initialized = true;
elijahsj 1:8a094db1347f 41
elijahsj 1:8a094db1347f 42 update_present_time(ticker);
elijahsj 1:8a094db1347f 43 schedule_interrupt(ticker);
elijahsj 1:8a094db1347f 44 }
elijahsj 1:8a094db1347f 45
elijahsj 1:8a094db1347f 46 /**
elijahsj 1:8a094db1347f 47 * Set the event handler function of a ticker instance.
elijahsj 1:8a094db1347f 48 */
elijahsj 1:8a094db1347f 49 static void set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
elijahsj 1:8a094db1347f 50 {
elijahsj 1:8a094db1347f 51 ticker->queue->event_handler = handler;
elijahsj 1:8a094db1347f 52 }
elijahsj 1:8a094db1347f 53
elijahsj 1:8a094db1347f 54 /*
elijahsj 1:8a094db1347f 55 * Convert a 32 bit timestamp into a 64 bit timestamp.
elijahsj 1:8a094db1347f 56 *
elijahsj 1:8a094db1347f 57 * A 64 bit timestamp is used as the point of time of reference while the
elijahsj 1:8a094db1347f 58 * timestamp to convert is relative to this point of time.
elijahsj 1:8a094db1347f 59 *
elijahsj 1:8a094db1347f 60 * The lower 32 bits of the timestamp returned will be equal to the timestamp to
elijahsj 1:8a094db1347f 61 * convert.
elijahsj 1:8a094db1347f 62 *
elijahsj 1:8a094db1347f 63 * If the timestamp to convert is less than the lower 32 bits of the time
elijahsj 1:8a094db1347f 64 * reference then the timestamp to convert is seen as an overflowed value and
elijahsj 1:8a094db1347f 65 * the upper 32 bit of the timestamp returned will be equal to the upper 32 bit
elijahsj 1:8a094db1347f 66 * of the reference point + 1.
elijahsj 1:8a094db1347f 67 * Otherwise, the upper 32 bit returned will be equal to the upper 32 bit of the
elijahsj 1:8a094db1347f 68 * reference point.
elijahsj 1:8a094db1347f 69 *
elijahsj 1:8a094db1347f 70 * @param ref: The 64 bit timestamp of reference.
elijahsj 1:8a094db1347f 71 * @param timestamp: The timestamp to convert.
elijahsj 1:8a094db1347f 72 */
elijahsj 1:8a094db1347f 73 static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestamp)
elijahsj 1:8a094db1347f 74 {
elijahsj 1:8a094db1347f 75 bool overflow = timestamp < ((timestamp_t) ref) ? true : false;
elijahsj 1:8a094db1347f 76
elijahsj 1:8a094db1347f 77 us_timestamp_t result = (ref & ~((us_timestamp_t)UINT32_MAX)) | timestamp;
elijahsj 1:8a094db1347f 78 if (overflow) {
elijahsj 1:8a094db1347f 79 result += (1ULL<<32);
elijahsj 1:8a094db1347f 80 }
elijahsj 1:8a094db1347f 81
elijahsj 1:8a094db1347f 82 return result;
elijahsj 1:8a094db1347f 83 }
elijahsj 1:8a094db1347f 84
elijahsj 1:8a094db1347f 85 /**
elijahsj 1:8a094db1347f 86 * Update the present timestamp value of a ticker.
elijahsj 1:8a094db1347f 87 */
elijahsj 1:8a094db1347f 88 static void update_present_time(const ticker_data_t *const ticker)
elijahsj 1:8a094db1347f 89 {
elijahsj 1:8a094db1347f 90 ticker->queue->present_time = convert_timestamp(
elijahsj 1:8a094db1347f 91 ticker->queue->present_time,
elijahsj 1:8a094db1347f 92 ticker->interface->read()
elijahsj 1:8a094db1347f 93 );
elijahsj 1:8a094db1347f 94 }
elijahsj 1:8a094db1347f 95
elijahsj 1:8a094db1347f 96 /**
elijahsj 1:8a094db1347f 97 * Compute the time when the interrupt has to be triggered and schedule it.
elijahsj 1:8a094db1347f 98 *
elijahsj 1:8a094db1347f 99 * If there is no event in the queue or the next event to execute is in more
elijahsj 1:8a094db1347f 100 * than MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us from now then the ticker
elijahsj 1:8a094db1347f 101 * irq will be scheduled in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA us.
elijahsj 1:8a094db1347f 102 * Otherwise the irq will be scheduled to happen when the running counter reach
elijahsj 1:8a094db1347f 103 * the timestamp of the first event in the queue.
elijahsj 1:8a094db1347f 104 *
elijahsj 1:8a094db1347f 105 * @note If there is no event in the queue then the interrupt is scheduled to
elijahsj 1:8a094db1347f 106 * in MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA. This is necessary to keep track
elijahsj 1:8a094db1347f 107 * of the timer overflow.
elijahsj 1:8a094db1347f 108 */
elijahsj 1:8a094db1347f 109 static void schedule_interrupt(const ticker_data_t *const ticker)
elijahsj 1:8a094db1347f 110 {
elijahsj 1:8a094db1347f 111 update_present_time(ticker);
elijahsj 1:8a094db1347f 112 uint32_t relative_timeout = MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA;
elijahsj 1:8a094db1347f 113
elijahsj 1:8a094db1347f 114 if (ticker->queue->head) {
elijahsj 1:8a094db1347f 115 us_timestamp_t present = ticker->queue->present_time;
elijahsj 1:8a094db1347f 116 us_timestamp_t next_event_timestamp = ticker->queue->head->timestamp;
elijahsj 1:8a094db1347f 117
elijahsj 1:8a094db1347f 118 // if the event at the head of the queue is in the past then schedule
elijahsj 1:8a094db1347f 119 // it immediately.
elijahsj 1:8a094db1347f 120 if (next_event_timestamp <= present) {
elijahsj 1:8a094db1347f 121 ticker->interface->fire_interrupt();
elijahsj 1:8a094db1347f 122 return;
elijahsj 1:8a094db1347f 123 } else if ((next_event_timestamp - present) < MBED_TICKER_INTERRUPT_TIMESTAMP_MAX_DELTA) {
elijahsj 1:8a094db1347f 124 relative_timeout = next_event_timestamp - present;
elijahsj 1:8a094db1347f 125 }
elijahsj 1:8a094db1347f 126 }
elijahsj 1:8a094db1347f 127
elijahsj 1:8a094db1347f 128 us_timestamp_t new_match_time = ticker->queue->present_time + relative_timeout;
elijahsj 1:8a094db1347f 129 ticker->interface->set_interrupt(new_match_time);
elijahsj 1:8a094db1347f 130 // there could be a delay, reread the time, check if it was set in the past
elijahsj 1:8a094db1347f 131 // As result, if it is already in the past, we fire it immediately
elijahsj 1:8a094db1347f 132 update_present_time(ticker);
elijahsj 1:8a094db1347f 133 us_timestamp_t present = ticker->queue->present_time;
elijahsj 1:8a094db1347f 134 if (present >= new_match_time) {
elijahsj 1:8a094db1347f 135 ticker->interface->fire_interrupt();
elijahsj 1:8a094db1347f 136 }
elijahsj 1:8a094db1347f 137 }
elijahsj 1:8a094db1347f 138
elijahsj 1:8a094db1347f 139 void ticker_set_handler(const ticker_data_t *const ticker, ticker_event_handler handler)
elijahsj 1:8a094db1347f 140 {
elijahsj 1:8a094db1347f 141 initialize(ticker);
elijahsj 1:8a094db1347f 142 set_handler(ticker, handler);
elijahsj 1:8a094db1347f 143 }
elijahsj 1:8a094db1347f 144
elijahsj 1:8a094db1347f 145 void ticker_irq_handler(const ticker_data_t *const ticker)
elijahsj 1:8a094db1347f 146 {
elijahsj 1:8a094db1347f 147 ticker->interface->clear_interrupt();
elijahsj 1:8a094db1347f 148
elijahsj 1:8a094db1347f 149 /* Go through all the pending TimerEvents */
elijahsj 1:8a094db1347f 150 while (1) {
elijahsj 1:8a094db1347f 151 if (ticker->queue->head == NULL) {
elijahsj 1:8a094db1347f 152 break;
elijahsj 1:8a094db1347f 153 }
elijahsj 1:8a094db1347f 154
elijahsj 1:8a094db1347f 155 // update the current timestamp used by the queue
elijahsj 1:8a094db1347f 156 update_present_time(ticker);
elijahsj 1:8a094db1347f 157
elijahsj 1:8a094db1347f 158 if (ticker->queue->head->timestamp <= ticker->queue->present_time) {
elijahsj 1:8a094db1347f 159 // This event was in the past:
elijahsj 1:8a094db1347f 160 // point to the following one and execute its handler
elijahsj 1:8a094db1347f 161 ticker_event_t *p = ticker->queue->head;
elijahsj 1:8a094db1347f 162 ticker->queue->head = ticker->queue->head->next;
elijahsj 1:8a094db1347f 163 if (ticker->queue->event_handler != NULL) {
elijahsj 1:8a094db1347f 164 (*ticker->queue->event_handler)(p->id); // NOTE: the handler can set new events
elijahsj 1:8a094db1347f 165 }
elijahsj 1:8a094db1347f 166 /* Note: We continue back to examining the head because calling the
elijahsj 1:8a094db1347f 167 * event handler may have altered the chain of pending events. */
elijahsj 1:8a094db1347f 168 } else {
elijahsj 1:8a094db1347f 169 break;
elijahsj 1:8a094db1347f 170 }
elijahsj 1:8a094db1347f 171 }
elijahsj 1:8a094db1347f 172
elijahsj 1:8a094db1347f 173 schedule_interrupt(ticker);
elijahsj 1:8a094db1347f 174 }
elijahsj 1:8a094db1347f 175
elijahsj 1:8a094db1347f 176 void ticker_insert_event(const ticker_data_t *const ticker, ticker_event_t *obj, timestamp_t timestamp, uint32_t id)
elijahsj 1:8a094db1347f 177 {
elijahsj 1:8a094db1347f 178 core_util_critical_section_enter();
elijahsj 1:8a094db1347f 179
elijahsj 1:8a094db1347f 180 // update the current timestamp
elijahsj 1:8a094db1347f 181 update_present_time(ticker);
elijahsj 1:8a094db1347f 182 us_timestamp_t absolute_timestamp = convert_timestamp(
elijahsj 1:8a094db1347f 183 ticker->queue->present_time,
elijahsj 1:8a094db1347f 184 timestamp
elijahsj 1:8a094db1347f 185 );
elijahsj 1:8a094db1347f 186 core_util_critical_section_exit();
elijahsj 1:8a094db1347f 187
elijahsj 1:8a094db1347f 188 // defer to ticker_insert_event_us
elijahsj 1:8a094db1347f 189 ticker_insert_event_us(
elijahsj 1:8a094db1347f 190 ticker,
elijahsj 1:8a094db1347f 191 obj, absolute_timestamp, id
elijahsj 1:8a094db1347f 192 );
elijahsj 1:8a094db1347f 193 }
elijahsj 1:8a094db1347f 194
elijahsj 1:8a094db1347f 195 void ticker_insert_event_us(const ticker_data_t *const ticker, ticker_event_t *obj, us_timestamp_t timestamp, uint32_t id)
elijahsj 1:8a094db1347f 196 {
elijahsj 1:8a094db1347f 197 core_util_critical_section_enter();
elijahsj 1:8a094db1347f 198
elijahsj 1:8a094db1347f 199 // update the current timestamp
elijahsj 1:8a094db1347f 200 update_present_time(ticker);
elijahsj 1:8a094db1347f 201
elijahsj 1:8a094db1347f 202 // initialise our data
elijahsj 1:8a094db1347f 203 obj->timestamp = timestamp;
elijahsj 1:8a094db1347f 204 obj->id = id;
elijahsj 1:8a094db1347f 205
elijahsj 1:8a094db1347f 206 /* Go through the list until we either reach the end, or find
elijahsj 1:8a094db1347f 207 an element this should come before (which is possibly the
elijahsj 1:8a094db1347f 208 head). */
elijahsj 1:8a094db1347f 209 ticker_event_t *prev = NULL, *p = ticker->queue->head;
elijahsj 1:8a094db1347f 210 while (p != NULL) {
elijahsj 1:8a094db1347f 211 /* check if we come before p */
elijahsj 1:8a094db1347f 212 if (timestamp < p->timestamp) {
elijahsj 1:8a094db1347f 213 break;
elijahsj 1:8a094db1347f 214 }
elijahsj 1:8a094db1347f 215 /* go to the next element */
elijahsj 1:8a094db1347f 216 prev = p;
elijahsj 1:8a094db1347f 217 p = p->next;
elijahsj 1:8a094db1347f 218 }
elijahsj 1:8a094db1347f 219
elijahsj 1:8a094db1347f 220 /* if we're at the end p will be NULL, which is correct */
elijahsj 1:8a094db1347f 221 obj->next = p;
elijahsj 1:8a094db1347f 222
elijahsj 1:8a094db1347f 223 /* if prev is NULL we're at the head */
elijahsj 1:8a094db1347f 224 if (prev == NULL) {
elijahsj 1:8a094db1347f 225 ticker->queue->head = obj;
elijahsj 1:8a094db1347f 226 } else {
elijahsj 1:8a094db1347f 227 prev->next = obj;
elijahsj 1:8a094db1347f 228 }
elijahsj 1:8a094db1347f 229
elijahsj 1:8a094db1347f 230 schedule_interrupt(ticker);
elijahsj 1:8a094db1347f 231
elijahsj 1:8a094db1347f 232 core_util_critical_section_exit();
elijahsj 1:8a094db1347f 233 }
elijahsj 1:8a094db1347f 234
elijahsj 1:8a094db1347f 235 void ticker_remove_event(const ticker_data_t *const ticker, ticker_event_t *obj)
elijahsj 1:8a094db1347f 236 {
elijahsj 1:8a094db1347f 237 core_util_critical_section_enter();
elijahsj 1:8a094db1347f 238
elijahsj 1:8a094db1347f 239 // remove this object from the list
elijahsj 1:8a094db1347f 240 if (ticker->queue->head == obj) {
elijahsj 1:8a094db1347f 241 // first in the list, so just drop me
elijahsj 1:8a094db1347f 242 ticker->queue->head = obj->next;
elijahsj 1:8a094db1347f 243 schedule_interrupt(ticker);
elijahsj 1:8a094db1347f 244 } else {
elijahsj 1:8a094db1347f 245 // find the object before me, then drop me
elijahsj 1:8a094db1347f 246 ticker_event_t* p = ticker->queue->head;
elijahsj 1:8a094db1347f 247 while (p != NULL) {
elijahsj 1:8a094db1347f 248 if (p->next == obj) {
elijahsj 1:8a094db1347f 249 p->next = obj->next;
elijahsj 1:8a094db1347f 250 break;
elijahsj 1:8a094db1347f 251 }
elijahsj 1:8a094db1347f 252 p = p->next;
elijahsj 1:8a094db1347f 253 }
elijahsj 1:8a094db1347f 254 }
elijahsj 1:8a094db1347f 255
elijahsj 1:8a094db1347f 256 core_util_critical_section_exit();
elijahsj 1:8a094db1347f 257 }
elijahsj 1:8a094db1347f 258
elijahsj 1:8a094db1347f 259 timestamp_t ticker_read(const ticker_data_t *const ticker)
elijahsj 1:8a094db1347f 260 {
elijahsj 1:8a094db1347f 261 return ticker_read_us(ticker);
elijahsj 1:8a094db1347f 262 }
elijahsj 1:8a094db1347f 263
elijahsj 1:8a094db1347f 264 us_timestamp_t ticker_read_us(const ticker_data_t *const ticker)
elijahsj 1:8a094db1347f 265 {
elijahsj 1:8a094db1347f 266 initialize(ticker);
elijahsj 1:8a094db1347f 267 update_present_time(ticker);
elijahsj 1:8a094db1347f 268 return ticker->queue->present_time;
elijahsj 1:8a094db1347f 269 }
elijahsj 1:8a094db1347f 270
elijahsj 1:8a094db1347f 271 int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *timestamp)
elijahsj 1:8a094db1347f 272 {
elijahsj 1:8a094db1347f 273 int ret = 0;
elijahsj 1:8a094db1347f 274
elijahsj 1:8a094db1347f 275 /* if head is NULL, there are no pending events */
elijahsj 1:8a094db1347f 276 core_util_critical_section_enter();
elijahsj 1:8a094db1347f 277 if (data->queue->head != NULL) {
elijahsj 1:8a094db1347f 278 *timestamp = data->queue->head->timestamp;
elijahsj 1:8a094db1347f 279 ret = 1;
elijahsj 1:8a094db1347f 280 }
elijahsj 1:8a094db1347f 281 core_util_critical_section_exit();
elijahsj 1:8a094db1347f 282
elijahsj 1:8a094db1347f 283 return ret;
elijahsj 1:8a094db1347f 284 }