joey shelton / LED_Demo

Dependencies:   MAX44000 PWM_Tone_Library nexpaq_mdk

Fork of LED_Demo by Maxim nexpaq

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