Simple interface for Mbed Cloud Client
Embed:
(wiki syntax)
Show/hide line numbers
ns_timer.c
00001 /* 00002 * Copyright (c) 2014-2015 ARM Limited. All rights reserved. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * Licensed under the Apache License, Version 2.0 (the License); you may 00005 * 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, WITHOUT 00012 * 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 00017 #include "ns_types.h" 00018 #include "ns_list.h" 00019 #include "ns_timer.h" 00020 #include "eventOS_callback_timer.h" 00021 #include "platform/arm_hal_interrupt.h" 00022 #include "platform/arm_hal_timer.h" 00023 #include "nsdynmemLIB.h" 00024 00025 #ifndef NS_EXCLUDE_HIGHRES_TIMER 00026 typedef enum ns_timer_state_e { 00027 NS_TIMER_ACTIVE = 0, // Will run on the next HAL interrupt 00028 NS_TIMER_HOLD, // Will run on a later HAL interrupt 00029 NS_TIMER_RUN_INTERRUPT, // Running on the interrupt we're currently handling 00030 NS_TIMER_STOP // Timer not scheduled ("start" not called since last callback) 00031 } ns_timer_state_e; 00032 00033 typedef struct ns_timer_struct { 00034 int8_t ns_timer_id; 00035 ns_timer_state_e timer_state; 00036 uint16_t slots; 00037 uint16_t remaining_slots; 00038 void (*interrupt_handler)(int8_t, uint16_t); 00039 ns_list_link_t link; 00040 } ns_timer_struct; 00041 00042 static NS_LIST_DEFINE(ns_timer_list, ns_timer_struct, link); 00043 00044 #define NS_TIMER_RUNNING 1 00045 static uint8_t ns_timer_state = 0; 00046 00047 #ifdef ATMEGA256RFR2 00048 #define COMPENSATION 3 00049 #define COMPENSATION_TUNE 1 00050 #else 00051 #define COMPENSATION 0 00052 #define COMPENSATION_TUNE 0 00053 #endif 00054 00055 static void ns_timer_interrupt_handler(void); 00056 static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id); 00057 static bool ns_timer_initialized = 0; 00058 00059 int8_t eventOS_callback_timer_register(void (*timer_interrupt_handler)(int8_t, uint16_t)) 00060 { 00061 int8_t retval = -1; 00062 00063 if (!ns_timer_initialized) { 00064 /*Set interrupt handler in HAL driver*/ 00065 platform_timer_set_cb(ns_timer_interrupt_handler); 00066 ns_timer_initialized = 1; 00067 } 00068 00069 /*Find first free timer ID in timer list*/ 00070 /*(Note use of uint8_t to avoid overflow if we reach 0x7F)*/ 00071 for (uint8_t i = 0; i <= INT8_MAX; i++) { 00072 if (!ns_timer_get_pointer_to_timer_struct(i)) { 00073 retval = i; 00074 break; 00075 } 00076 } 00077 00078 if (retval == -1) { 00079 return -1; 00080 } 00081 00082 ns_timer_struct *new_timer = ns_dyn_mem_alloc(sizeof(ns_timer_struct)); 00083 if (!new_timer) { 00084 return -1; 00085 } 00086 00087 /*Initialise new timer*/ 00088 new_timer->ns_timer_id = retval; 00089 new_timer->timer_state = NS_TIMER_STOP; 00090 new_timer->remaining_slots = 0; 00091 new_timer->interrupt_handler = timer_interrupt_handler; 00092 00093 // Critical section sufficient as long as list can't be reordered from 00094 // interrupt, otherwise will need to cover whole routine 00095 platform_enter_critical(); 00096 ns_list_add_to_end(&ns_timer_list, new_timer); 00097 platform_exit_critical(); 00098 00099 /*Return timer ID*/ 00100 return retval; 00101 } 00102 00103 int8_t eventOS_callback_timer_unregister(int8_t ns_timer_id) 00104 { 00105 ns_timer_struct *current_timer; 00106 00107 current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); 00108 if (!current_timer) { 00109 return -1; 00110 } 00111 00112 // Critical section sufficient as long as list can't be reordered from 00113 // interrupt, otherwise will need to cover whole routine 00114 platform_enter_critical(); 00115 ns_list_remove(&ns_timer_list, current_timer); 00116 platform_exit_critical(); 00117 00118 ns_dyn_mem_free(current_timer); 00119 return 0; 00120 } 00121 00122 00123 static int8_t ns_timer_start_pl_timer(uint16_t pl_timer_start_slots) 00124 { 00125 /*Don't start timer with 0 slots*/ 00126 if (!pl_timer_start_slots) { 00127 pl_timer_start_slots = 1; 00128 } 00129 00130 /*Start HAL timer*/ 00131 platform_timer_start(pl_timer_start_slots); 00132 /*Set HAL timer state to running*/ 00133 ns_timer_state |= NS_TIMER_RUNNING; 00134 return 0; 00135 } 00136 00137 int8_t ns_timer_sleep(void) 00138 { 00139 int8_t ret_val = -1; 00140 if (ns_timer_state & NS_TIMER_RUNNING) { 00141 /*Start HAL timer*/ 00142 platform_timer_disable(); 00143 /*Set HAL timer state to running*/ 00144 ns_timer_state &= ~NS_TIMER_RUNNING; 00145 ret_val = 0; 00146 } 00147 return ret_val; 00148 } 00149 00150 static int8_t ns_timer_get_next_running_to(void) 00151 { 00152 uint8_t hold_count = 0; 00153 ns_timer_struct *first_timer = NULL; 00154 00155 /*Find hold-labelled timer with the least remaining slots*/ 00156 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00157 if (current_timer->timer_state == NS_TIMER_HOLD) { 00158 if (!first_timer || current_timer->remaining_slots < first_timer->remaining_slots) { 00159 first_timer = current_timer; 00160 } 00161 /*For optimisation, count the found timers*/ 00162 hold_count++; 00163 } 00164 } 00165 00166 if (!first_timer) { 00167 return 0; 00168 } 00169 00170 /*If hold-labelled timer found, set it active and start the HAL driver*/ 00171 hold_count--; 00172 first_timer->timer_state = NS_TIMER_ACTIVE; 00173 /*Compensate time spent in timer function*/ 00174 if (first_timer->remaining_slots > COMPENSATION) { 00175 first_timer->remaining_slots -= COMPENSATION; 00176 } 00177 /*Start HAL timer*/ 00178 ns_timer_start_pl_timer(first_timer->remaining_slots); 00179 00180 /*Update other hold-labelled timers*/ 00181 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00182 if (hold_count == 0) { // early termination optimisation 00183 break; 00184 } 00185 if (current_timer->timer_state == NS_TIMER_HOLD) { 00186 if (current_timer->remaining_slots == first_timer->remaining_slots) { 00187 current_timer->timer_state = NS_TIMER_ACTIVE; 00188 } else { 00189 current_timer->remaining_slots -= first_timer->remaining_slots; 00190 /*Compensate time spent in timer function*/ 00191 if (current_timer->remaining_slots > COMPENSATION) { 00192 current_timer->remaining_slots -= COMPENSATION; 00193 } 00194 } 00195 hold_count--; 00196 } 00197 } 00198 00199 return 0; 00200 } 00201 00202 00203 static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id) 00204 { 00205 /*Find timer with the given ID*/ 00206 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00207 if (current_timer->ns_timer_id == timer_id) { 00208 return current_timer; 00209 } 00210 } 00211 return NULL; 00212 } 00213 00214 int8_t eventOS_callback_timer_start(int8_t ns_timer_id, uint16_t slots) 00215 { 00216 int8_t ret_val = 0; 00217 uint16_t pl_timer_remaining_slots; 00218 ns_timer_struct *timer; 00219 platform_enter_critical(); 00220 00221 /*Find timer to be activated*/ 00222 timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); 00223 if (!timer) { 00224 ret_val = -1; 00225 goto exit; 00226 } 00227 00228 // XXX this assumes the timer currently isn't running? 00229 // Is event.c relying on this restarting HAL timer after ns_timer_sleep()? 00230 00231 /*If any timers are active*/ 00232 if (ns_timer_state & NS_TIMER_RUNNING) { 00233 /*Get remaining slots of the currently activated timeout*/ 00234 pl_timer_remaining_slots = platform_timer_get_remaining_slots(); 00235 00236 /*New timeout is shorter than currently enabled timeout*/ 00237 if (pl_timer_remaining_slots > slots) { 00238 /*Start HAL timer*/ 00239 ns_timer_start_pl_timer(slots - 0); 00240 00241 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00242 /*Switch active timers to hold*/ 00243 if (current_timer->timer_state == NS_TIMER_ACTIVE) { 00244 current_timer->timer_state = NS_TIMER_HOLD; 00245 current_timer->remaining_slots = 0; 00246 } 00247 /*Update hold-labelled timers*/ 00248 if (current_timer->timer_state == NS_TIMER_HOLD) { 00249 current_timer->remaining_slots += (pl_timer_remaining_slots - slots); 00250 /*Compensate time spent in timer function*/ 00251 if (current_timer->remaining_slots > (COMPENSATION - COMPENSATION_TUNE)) { 00252 current_timer->remaining_slots -= (COMPENSATION - COMPENSATION_TUNE); 00253 } 00254 } 00255 } 00256 /*Mark active and start the timer*/ 00257 timer->timer_state = NS_TIMER_ACTIVE; 00258 timer->slots = slots; 00259 timer->remaining_slots = slots; 00260 } 00261 00262 /*New timeout is longer than currently enabled timeout*/ 00263 else if (pl_timer_remaining_slots < slots) { 00264 /*Mark hold and update remaining slots*/ 00265 timer->timer_state = NS_TIMER_HOLD; 00266 timer->slots = slots; 00267 timer->remaining_slots = (slots - pl_timer_remaining_slots); 00268 } 00269 /*New timeout is equal to currently enabled timeout*/ 00270 else { 00271 /*Mark it active and it will be handled in next interrupt*/ 00272 timer->timer_state = NS_TIMER_ACTIVE; 00273 timer->slots = slots; 00274 timer->remaining_slots = slots; 00275 } 00276 } else { 00277 /*No timers running*/ 00278 timer->timer_state = NS_TIMER_HOLD; 00279 timer->slots = slots; 00280 timer->remaining_slots = slots; 00281 /*Start next timeout*/ 00282 ns_timer_get_next_running_to(); 00283 } 00284 exit: 00285 platform_exit_critical(); 00286 return ret_val; 00287 } 00288 00289 static void ns_timer_interrupt_handler(void) 00290 { 00291 uint8_t i = 0; 00292 00293 platform_enter_critical(); 00294 /*Clear timer running state*/ 00295 ns_timer_state &= ~NS_TIMER_RUNNING; 00296 /*Mark active timers as NS_TIMER_RUN_INTERRUPT, interrupt functions are called at the end of this function*/ 00297 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00298 if (current_timer->timer_state == NS_TIMER_ACTIVE) { 00299 current_timer->timer_state = NS_TIMER_RUN_INTERRUPT; 00300 /*For optimisation, count the found timers*/ 00301 i++; 00302 } 00303 } 00304 00305 /*Start next timeout*/ 00306 ns_timer_get_next_running_to(); 00307 00308 /*Call interrupt functions*/ 00309 ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) { 00310 if (i == 0) { 00311 break; 00312 } 00313 if (current_timer->timer_state == NS_TIMER_RUN_INTERRUPT) { 00314 current_timer->timer_state = NS_TIMER_STOP; 00315 current_timer->interrupt_handler(current_timer->ns_timer_id, current_timer->slots); 00316 i--; 00317 } 00318 } 00319 00320 platform_exit_critical(); 00321 } 00322 00323 int8_t eventOS_callback_timer_stop(int8_t ns_timer_id) 00324 { 00325 uint16_t pl_timer_remaining_slots; 00326 bool active_timer_found = false; 00327 ns_timer_struct *current_timer; 00328 ns_timer_struct *first_timer = NULL; 00329 int8_t retval = -1; 00330 00331 platform_enter_critical(); 00332 /*Find timer with given timer ID*/ 00333 current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id); 00334 if (!current_timer) { 00335 goto exit; 00336 } 00337 00338 retval = 0; 00339 00340 /*Check if already stopped*/ 00341 if (current_timer->timer_state == NS_TIMER_STOP) { 00342 goto exit; 00343 } 00344 00345 current_timer->timer_state = NS_TIMER_STOP; 00346 current_timer->remaining_slots = 0; 00347 00348 /*Check if some timer is already active*/ 00349 ns_list_foreach(ns_timer_struct, curr_timer, &ns_timer_list) { 00350 if (curr_timer->timer_state == NS_TIMER_ACTIVE) { 00351 active_timer_found = true; 00352 break; 00353 } 00354 } 00355 /*If no active timers found, start one*/ 00356 if (!active_timer_found) { 00357 pl_timer_remaining_slots = platform_timer_get_remaining_slots(); 00358 /*Find hold-labelled timer with the least remaining slots*/ 00359 ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) { 00360 if (cur_timer->timer_state == NS_TIMER_HOLD) { 00361 cur_timer->remaining_slots += pl_timer_remaining_slots; 00362 00363 if (!first_timer || cur_timer->remaining_slots < first_timer->remaining_slots) { 00364 first_timer = cur_timer; 00365 } 00366 } 00367 } 00368 /*If hold-labelled timer found, set it active and start the HAL driver*/ 00369 if (first_timer) { 00370 first_timer->timer_state = NS_TIMER_ACTIVE; 00371 /*Start HAL timer*/ 00372 ns_timer_start_pl_timer(first_timer->remaining_slots); 00373 /*If some of the other hold-labelled timers have the same remaining slots as the timer_tmp, mark them active*/ 00374 ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) { 00375 if (cur_timer->timer_state == NS_TIMER_HOLD) { 00376 if (cur_timer->remaining_slots == first_timer->remaining_slots) { 00377 cur_timer->timer_state = NS_TIMER_ACTIVE; 00378 } else { 00379 cur_timer->remaining_slots -= first_timer->remaining_slots; 00380 } 00381 } 00382 } 00383 } 00384 } 00385 00386 exit: 00387 platform_exit_critical(); 00388 00389 return retval; 00390 } 00391 #endif // NS_EXCLUDE_HIGHRES_TIMER
Generated on Tue Jul 12 2022 19:01:35 by 1.7.2