Example

Dependencies:   FXAS21002 FXOS8700Q

Revision:
0:11cc2b7889af
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/simple-mbed-cloud-client/mbed-cloud-client/sal-stack-nanostack-eventloop/source/ns_timer.c	Tue Nov 19 09:49:38 2019 +0000
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2014-2015 ARM Limited. All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ * Licensed under the Apache License, Version 2.0 (the License); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ns_types.h"
+#include "ns_list.h"
+#include "ns_timer.h"
+#include "eventOS_callback_timer.h"
+#include "platform/arm_hal_interrupt.h"
+#include "platform/arm_hal_timer.h"
+#include "nsdynmemLIB.h"
+
+#ifndef NS_EXCLUDE_HIGHRES_TIMER
+typedef enum ns_timer_state_e {
+    NS_TIMER_ACTIVE = 0,        // Will run on the next HAL interrupt
+    NS_TIMER_HOLD,              // Will run on a later HAL interrupt
+    NS_TIMER_RUN_INTERRUPT,     // Running on the interrupt we're currently handling
+    NS_TIMER_STOP               // Timer not scheduled ("start" not called since last callback)
+} ns_timer_state_e;
+
+typedef struct ns_timer_struct {
+    int8_t ns_timer_id;
+    ns_timer_state_e timer_state;
+    uint16_t slots;
+    uint16_t remaining_slots;
+    void (*interrupt_handler)(int8_t, uint16_t);
+    ns_list_link_t link;
+} ns_timer_struct;
+
+static NS_LIST_DEFINE(ns_timer_list, ns_timer_struct, link);
+
+#define NS_TIMER_RUNNING    1
+static uint8_t ns_timer_state = 0;
+
+#ifdef ATMEGA256RFR2
+#define COMPENSATION 3
+#define COMPENSATION_TUNE 1
+#else
+#define COMPENSATION 0
+#define COMPENSATION_TUNE 0
+#endif
+
+static void ns_timer_interrupt_handler(void);
+static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id);
+static bool ns_timer_initialized = 0;
+
+int8_t eventOS_callback_timer_register(void (*timer_interrupt_handler)(int8_t, uint16_t))
+{
+    int8_t retval = -1;
+
+    if (!ns_timer_initialized) {
+        /*Set interrupt handler in HAL driver*/
+        platform_timer_set_cb(ns_timer_interrupt_handler);
+        ns_timer_initialized = 1;
+    }
+
+    /*Find first free timer ID in timer list*/
+    /*(Note use of uint8_t to avoid overflow if we reach 0x7F)*/
+    for (uint8_t i = 0; i <= INT8_MAX; i++) {
+        if (!ns_timer_get_pointer_to_timer_struct(i)) {
+            retval = i;
+            break;
+        }
+    }
+
+    if (retval == -1) {
+        return -1;
+    }
+
+    ns_timer_struct *new_timer = ns_dyn_mem_alloc(sizeof(ns_timer_struct));
+    if (!new_timer) {
+        return -1;
+    }
+
+    /*Initialise new timer*/
+    new_timer->ns_timer_id = retval;
+    new_timer->timer_state = NS_TIMER_STOP;
+    new_timer->remaining_slots = 0;
+    new_timer->interrupt_handler = timer_interrupt_handler;
+
+    // Critical section sufficient as long as list can't be reordered from
+    // interrupt, otherwise will need to cover whole routine
+    platform_enter_critical();
+    ns_list_add_to_end(&ns_timer_list, new_timer);
+    platform_exit_critical();
+
+    /*Return timer ID*/
+    return retval;
+}
+
+int8_t eventOS_callback_timer_unregister(int8_t ns_timer_id)
+{
+    ns_timer_struct *current_timer;
+
+    current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
+    if (!current_timer) {
+        return -1;
+    }
+
+    // Critical section sufficient as long as list can't be reordered from
+    // interrupt, otherwise will need to cover whole routine
+    platform_enter_critical();
+    ns_list_remove(&ns_timer_list, current_timer);
+    platform_exit_critical();
+
+    ns_dyn_mem_free(current_timer);
+    return 0;
+}
+
+
+static int8_t ns_timer_start_pl_timer(uint16_t pl_timer_start_slots)
+{
+    /*Don't start timer with 0 slots*/
+    if (!pl_timer_start_slots) {
+        pl_timer_start_slots = 1;
+    }
+
+    /*Start HAL timer*/
+    platform_timer_start(pl_timer_start_slots);
+    /*Set HAL timer state to running*/
+    ns_timer_state |= NS_TIMER_RUNNING;
+    return 0;
+}
+
+int8_t ns_timer_sleep(void)
+{
+    int8_t ret_val = -1;
+    if (ns_timer_state & NS_TIMER_RUNNING) {
+        /*Start HAL timer*/
+        platform_timer_disable();
+        /*Set HAL timer state to running*/
+        ns_timer_state &= ~NS_TIMER_RUNNING;
+        ret_val = 0;
+    }
+    return ret_val;
+}
+
+static int8_t ns_timer_get_next_running_to(void)
+{
+    uint8_t hold_count = 0;
+    ns_timer_struct *first_timer = NULL;
+
+    /*Find hold-labelled timer with the least remaining slots*/
+    ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+        if (current_timer->timer_state == NS_TIMER_HOLD) {
+            if (!first_timer || current_timer->remaining_slots < first_timer->remaining_slots) {
+                first_timer = current_timer;
+            }
+            /*For optimisation, count the found timers*/
+            hold_count++;
+        }
+    }
+
+    if (!first_timer) {
+        return 0;
+    }
+
+    /*If hold-labelled timer found, set it active and start the HAL driver*/
+    hold_count--;
+    first_timer->timer_state = NS_TIMER_ACTIVE;
+    /*Compensate time spent in timer function*/
+    if (first_timer->remaining_slots > COMPENSATION) {
+        first_timer->remaining_slots -= COMPENSATION;
+    }
+    /*Start HAL timer*/
+    ns_timer_start_pl_timer(first_timer->remaining_slots);
+
+    /*Update other hold-labelled timers*/
+    ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+        if (hold_count == 0) { // early termination optimisation
+            break;
+        }
+        if (current_timer->timer_state == NS_TIMER_HOLD) {
+            if (current_timer->remaining_slots == first_timer->remaining_slots) {
+                current_timer->timer_state = NS_TIMER_ACTIVE;
+            } else {
+                current_timer->remaining_slots -= first_timer->remaining_slots;
+                /*Compensate time spent in timer function*/
+                if (current_timer->remaining_slots > COMPENSATION) {
+                    current_timer->remaining_slots -= COMPENSATION;
+                }
+            }
+            hold_count--;
+        }
+    }
+
+    return 0;
+}
+
+
+static ns_timer_struct *ns_timer_get_pointer_to_timer_struct(int8_t timer_id)
+{
+    /*Find timer with the given ID*/
+    ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+        if (current_timer->ns_timer_id == timer_id) {
+            return current_timer;
+        }
+    }
+    return NULL;
+}
+
+int8_t eventOS_callback_timer_start(int8_t ns_timer_id, uint16_t slots)
+{
+    int8_t ret_val = 0;
+    uint16_t pl_timer_remaining_slots;
+    ns_timer_struct *timer;
+    platform_enter_critical();
+
+    /*Find timer to be activated*/
+    timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
+    if (!timer) {
+        ret_val = -1;
+        goto exit;
+    }
+
+    // XXX this assumes the timer currently isn't running?
+    // Is event.c relying on this restarting HAL timer after ns_timer_sleep()?
+
+    /*If any timers are active*/
+    if (ns_timer_state & NS_TIMER_RUNNING) {
+        /*Get remaining slots of the currently activated timeout*/
+        pl_timer_remaining_slots = platform_timer_get_remaining_slots();
+
+        /*New timeout is shorter than currently enabled timeout*/
+        if (pl_timer_remaining_slots > slots) {
+            /*Start HAL timer*/
+            ns_timer_start_pl_timer(slots - 0);
+
+            ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+                /*Switch active timers to hold*/
+                if (current_timer->timer_state == NS_TIMER_ACTIVE) {
+                    current_timer->timer_state = NS_TIMER_HOLD;
+                    current_timer->remaining_slots = 0;
+                }
+                /*Update hold-labelled timers*/
+                if (current_timer->timer_state == NS_TIMER_HOLD) {
+                    current_timer->remaining_slots += (pl_timer_remaining_slots - slots);
+                    /*Compensate time spent in timer function*/
+                    if (current_timer->remaining_slots > (COMPENSATION - COMPENSATION_TUNE)) {
+                        current_timer->remaining_slots -= (COMPENSATION - COMPENSATION_TUNE);
+                    }
+                }
+            }
+            /*Mark active and start the timer*/
+            timer->timer_state = NS_TIMER_ACTIVE;
+            timer->slots = slots;
+            timer->remaining_slots = slots;
+        }
+
+        /*New timeout is longer than currently enabled timeout*/
+        else if (pl_timer_remaining_slots < slots) {
+            /*Mark hold and update remaining slots*/
+            timer->timer_state = NS_TIMER_HOLD;
+            timer->slots = slots;
+            timer->remaining_slots = (slots - pl_timer_remaining_slots);
+        }
+        /*New timeout is equal to currently enabled timeout*/
+        else {
+            /*Mark it active and it will be handled in next interrupt*/
+            timer->timer_state = NS_TIMER_ACTIVE;
+            timer->slots = slots;
+            timer->remaining_slots = slots;
+        }
+    } else {
+        /*No timers running*/
+        timer->timer_state = NS_TIMER_HOLD;
+        timer->slots = slots;
+        timer->remaining_slots = slots;
+        /*Start next timeout*/
+        ns_timer_get_next_running_to();
+    }
+exit:
+    platform_exit_critical();
+    return ret_val;
+}
+
+static void ns_timer_interrupt_handler(void)
+{
+    uint8_t i = 0;
+
+    platform_enter_critical();
+    /*Clear timer running state*/
+    ns_timer_state &= ~NS_TIMER_RUNNING;
+    /*Mark active timers as NS_TIMER_RUN_INTERRUPT, interrupt functions are called at the end of this function*/
+    ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+        if (current_timer->timer_state == NS_TIMER_ACTIVE) {
+            current_timer->timer_state = NS_TIMER_RUN_INTERRUPT;
+            /*For optimisation, count the found timers*/
+            i++;
+        }
+    }
+
+    /*Start next timeout*/
+    ns_timer_get_next_running_to();
+
+    /*Call interrupt functions*/
+    ns_list_foreach(ns_timer_struct, current_timer, &ns_timer_list) {
+        if (i == 0) {
+            break;
+        }
+        if (current_timer->timer_state == NS_TIMER_RUN_INTERRUPT) {
+            current_timer->timer_state = NS_TIMER_STOP;
+            current_timer->interrupt_handler(current_timer->ns_timer_id, current_timer->slots);
+            i--;
+        }
+    }
+
+    platform_exit_critical();
+}
+
+int8_t eventOS_callback_timer_stop(int8_t ns_timer_id)
+{
+    uint16_t pl_timer_remaining_slots;
+    bool active_timer_found = false;
+    ns_timer_struct *current_timer;
+    ns_timer_struct *first_timer = NULL;
+    int8_t retval = -1;
+
+    platform_enter_critical();
+    /*Find timer with given timer ID*/
+    current_timer = ns_timer_get_pointer_to_timer_struct(ns_timer_id);
+    if (!current_timer) {
+        goto exit;
+    }
+
+    retval = 0;
+
+    /*Check if already stopped*/
+    if (current_timer->timer_state == NS_TIMER_STOP) {
+        goto exit;
+    }
+
+    current_timer->timer_state = NS_TIMER_STOP;
+    current_timer->remaining_slots = 0;
+
+    /*Check if some timer is already active*/
+    ns_list_foreach(ns_timer_struct, curr_timer, &ns_timer_list) {
+        if (curr_timer->timer_state == NS_TIMER_ACTIVE) {
+            active_timer_found = true;
+            break;
+        }
+    }
+    /*If no active timers found, start one*/
+    if (!active_timer_found) {
+        pl_timer_remaining_slots = platform_timer_get_remaining_slots();
+        /*Find hold-labelled timer with the least remaining slots*/
+        ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) {
+            if (cur_timer->timer_state == NS_TIMER_HOLD) {
+                cur_timer->remaining_slots += pl_timer_remaining_slots;
+
+                if (!first_timer || cur_timer->remaining_slots < first_timer->remaining_slots) {
+                    first_timer = cur_timer;
+                }
+            }
+        }
+        /*If hold-labelled timer found, set it active and start the HAL driver*/
+        if (first_timer) {
+            first_timer->timer_state = NS_TIMER_ACTIVE;
+            /*Start HAL timer*/
+            ns_timer_start_pl_timer(first_timer->remaining_slots);
+            /*If some of the other hold-labelled timers have the same remaining slots as the timer_tmp, mark them active*/
+            ns_list_foreach(ns_timer_struct, cur_timer, &ns_timer_list) {
+                if (cur_timer->timer_state == NS_TIMER_HOLD) {
+                    if (cur_timer->remaining_slots == first_timer->remaining_slots) {
+                        cur_timer->timer_state = NS_TIMER_ACTIVE;
+                    } else {
+                        cur_timer->remaining_slots -= first_timer->remaining_slots;
+                    }
+                }
+            }
+        }
+    }
+
+exit:
+    platform_exit_critical();
+
+    return retval;
+}
+#endif // NS_EXCLUDE_HIGHRES_TIMER