forked
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Tue Jul 12 2022 16:02:32 by 1.7.2