ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ns_timer.c Source File

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