mirror of https://github.com/ARMmbed/mbed-os.git
392 lines
13 KiB
C
392 lines
13 KiB
C
/*
|
|
* 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
|