2016-08-16 11:51:48 +00:00
|
|
|
|
/**
|
|
|
|
|
******************************************************************************
|
|
|
|
|
* @file us_ticker_api.h
|
|
|
|
|
* @brief Implementation of a Timer driver
|
|
|
|
|
* @internal
|
|
|
|
|
* @author ON Semiconductor
|
|
|
|
|
* $Rev: $
|
|
|
|
|
* $Date: 2015-11-15 $
|
|
|
|
|
******************************************************************************
|
2016-09-01 11:25:41 +00:00
|
|
|
|
* Copyright 2016 Semiconductor Components Industries LLC (d/b/a <EFBFBD>ON Semiconductor<EFBFBD>).
|
|
|
|
|
* All rights reserved. This software and/or documentation is licensed by ON Semiconductor
|
|
|
|
|
* under limited terms and conditions. The terms and conditions pertaining to the software
|
|
|
|
|
* and/or documentation are available at http://www.onsemi.com/site/pdf/ONSEMI_T&C.pdf
|
|
|
|
|
* (<EFBFBD>ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software<EFBFBD>) and
|
|
|
|
|
* if applicable the software license agreement. Do not use this software and/or
|
|
|
|
|
* documentation unless you have carefully read and you agree to the limited terms and
|
|
|
|
|
* conditions. By using this software and/or documentation, you agree to the limited
|
|
|
|
|
* terms and conditions.
|
2016-08-16 11:51:48 +00:00
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
|
|
|
|
|
* OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
|
|
|
|
|
* ON SEMICONDUCTOR SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL,
|
|
|
|
|
* INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
|
|
|
|
|
* @endinternal
|
|
|
|
|
*
|
|
|
|
|
* @ingroup timer
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stddef.h>
|
2017-06-15 11:15:00 +00:00
|
|
|
|
#include "timer_ncs36510.h"
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
|
|
|
|
#define US_TIMER TIMER0
|
|
|
|
|
#define US_TICKER TIMER1
|
|
|
|
|
|
|
|
|
|
static int us_ticker_inited = 0;
|
|
|
|
|
|
|
|
|
|
static void us_timer_init(void);
|
|
|
|
|
|
2017-02-15 15:45:52 +00:00
|
|
|
|
static uint32_t us_ticker_target = 0;
|
2017-06-30 11:44:02 +00:00
|
|
|
|
static volatile uint16_t msb_counter = 0;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
|
|
|
|
void us_ticker_init(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
if (!us_ticker_inited) {
|
|
|
|
|
us_timer_init();
|
|
|
|
|
}
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* Timer for us timing reference
|
|
|
|
|
*
|
|
|
|
|
* Uptime counter for scheduling reference. It uses TIMER0.
|
|
|
|
|
* The NCS36510 does not have a 32 bit timer nor the option to chain timers,
|
|
|
|
|
* which is why a software timer is required to get 32-bit word length.
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
/* TODO - Need some sort of load value/prescale calculation for non-32MHz clock */
|
|
|
|
|
/* TODO - How is overflow handled? */
|
|
|
|
|
|
|
|
|
|
/* Timer 0 for free running time */
|
|
|
|
|
extern void us_timer_isr(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
TIM0REG->CLEAR = 0;
|
|
|
|
|
msb_counter++;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initializing TIMER 0(TImer) and TIMER 1(Ticker) */
|
|
|
|
|
static void us_timer_init(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Enable the timer0 periphery clock */
|
|
|
|
|
CLOCK_ENABLE(CLOCK_TIMER0);
|
|
|
|
|
/* Enable the timer0 periphery clock */
|
|
|
|
|
CLOCK_ENABLE(CLOCK_TIMER1);
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Timer init */
|
|
|
|
|
/* load timer value */
|
|
|
|
|
TIM0REG->LOAD = 0xFFFF;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* set timer prescale 32 (1 us), mode & enable */
|
2016-07-11 14:14:03 +00:00
|
|
|
|
TIM0REG->CONTROL.WORD = ((CLK_DIVIDER_32 << TIMER_PRESCALE_BIT_POS) |
|
|
|
|
|
(TIME_MODE_PERIODIC << TIMER_MODE_BIT_POS) |
|
|
|
|
|
(TIMER_ENABLE_BIT << TIMER_ENABLE_BIT_POS));
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Ticker init */
|
|
|
|
|
/* load timer value */
|
|
|
|
|
TIM1REG->LOAD = 0xFFFF;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* set timer prescale 32 (1 us), mode & enable */
|
2016-07-11 14:14:03 +00:00
|
|
|
|
TIM1REG->CONTROL.WORD = ((CLK_DIVIDER_32 << TIMER_PRESCALE_BIT_POS) |
|
|
|
|
|
(TIME_MODE_PERIODIC << TIMER_MODE_BIT_POS));
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Register & enable interrupt associated with the timer */
|
|
|
|
|
NVIC_SetVector(Tim0_IRQn,(uint32_t)us_timer_isr);
|
|
|
|
|
NVIC_SetVector(Tim1_IRQn,(uint32_t)us_ticker_isr);
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Clear pending irqs */
|
|
|
|
|
NVIC_ClearPendingIRQ(Tim0_IRQn);
|
|
|
|
|
NVIC_ClearPendingIRQ(Tim1_IRQn);
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Setup NVIC for timer */
|
|
|
|
|
NVIC_EnableIRQ(Tim0_IRQn);
|
|
|
|
|
NVIC_EnableIRQ(Tim1_IRQn);
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
us_ticker_inited = 1;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reads 32 bit timer's current value (16 bit s/w timer | 16 bit h/w timer) */
|
|
|
|
|
uint32_t us_ticker_read()
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
|
|
|
|
|
if (!us_ticker_inited) {
|
|
|
|
|
us_timer_init();
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-30 11:44:02 +00:00
|
|
|
|
NVIC_DisableIRQ(Tim0_IRQn);
|
|
|
|
|
uint32_t retval, tim0cval;
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Get the current tick from the hw and sw timers */
|
2016-07-11 14:14:03 +00:00
|
|
|
|
tim0cval = TIM0REG->VALUE; /* read current time */
|
|
|
|
|
retval = (0xFFFF - tim0cval); /* subtract down count */
|
2016-08-26 11:22:11 +00:00
|
|
|
|
|
|
|
|
|
if (TIM0REG->CONTROL.BITS.INT) {
|
2017-06-30 11:44:02 +00:00
|
|
|
|
us_timer_isr(); /* handle ISR again */
|
|
|
|
|
NVIC_ClearPendingIRQ(Tim0_IRQn);
|
|
|
|
|
retval = (0xFFFF - TIM0REG->VALUE);
|
2016-08-26 11:22:11 +00:00
|
|
|
|
}
|
2016-07-11 14:14:03 +00:00
|
|
|
|
retval |= msb_counter << 16; /* add software bits */
|
2016-08-26 11:22:11 +00:00
|
|
|
|
NVIC_EnableIRQ(Tim0_IRQn);
|
|
|
|
|
return retval;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
|
void us_ticker_fire_interrupt(void)
|
|
|
|
|
{
|
2017-10-27 16:46:24 +00:00
|
|
|
|
us_ticker_target = 0;
|
|
|
|
|
NVIC_SetPendingIRQ(Tim1_IRQn);
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-16 11:51:48 +00:00
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* Event Timer
|
|
|
|
|
*
|
|
|
|
|
* Schedules interrupts at given (32bit)us interval of time. It uses TIMER1.
|
|
|
|
|
* The NCS36510 does not have a 32 bit timer nor the option to chain timers,
|
|
|
|
|
* which is why a software timer is required to get 32-bit word length.
|
|
|
|
|
*******************************************************************************/
|
|
|
|
|
/* TODO - Need some sort of load value/prescale calculation for non-32MHz clock */
|
|
|
|
|
|
|
|
|
|
/* TImer 1 disbale interrupt */
|
|
|
|
|
void us_ticker_disable_interrupt(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Disable the TIMER1 interrupt */
|
|
|
|
|
TIM1REG->CONTROL.BITS.ENABLE = 0x0;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TImer 1 clear interrupt */
|
|
|
|
|
void us_ticker_clear_interrupt(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Clear the Ticker (TIMER1) interrupt */
|
|
|
|
|
TIM1REG->CLEAR = 0;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setting TImer 1 (ticker) */
|
|
|
|
|
inline static void ticker_set(uint32_t count)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Disable TIMER1, load the new value, and re-enable */
|
|
|
|
|
TIM1REG->CONTROL.BITS.ENABLE = 0;
|
|
|
|
|
TIM1REG->LOAD = count;
|
|
|
|
|
TIM1REG->CONTROL.BITS.ENABLE = 1;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TImer 1 - ticker ISR */
|
|
|
|
|
extern void us_ticker_isr(void)
|
|
|
|
|
{
|
2016-08-26 11:22:11 +00:00
|
|
|
|
/* Clear IRQ flag */
|
|
|
|
|
TIM1REG->CLEAR = 0;
|
|
|
|
|
|
2017-06-30 11:44:02 +00:00
|
|
|
|
if (us_ticker_target > 0) {
|
|
|
|
|
--us_ticker_target;
|
|
|
|
|
ticker_set(0xFFFF);
|
2017-02-15 15:45:52 +00:00
|
|
|
|
} else {
|
2017-06-30 11:44:02 +00:00
|
|
|
|
us_ticker_irq_handler();
|
2016-08-26 11:22:11 +00:00
|
|
|
|
}
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set timer 1 ticker interrupt */
|
|
|
|
|
void us_ticker_set_interrupt(timestamp_t timestamp)
|
|
|
|
|
{
|
2017-06-30 11:44:02 +00:00
|
|
|
|
int32_t delta = timestamp - us_ticker_read();
|
|
|
|
|
// we got 16 bit timer, use upper 16bit as a simple counter how many times
|
|
|
|
|
// we need to schedule full range ticker count
|
|
|
|
|
us_ticker_target = (uint32_t)delta >> 16;
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
if (delta <= 0) {
|
|
|
|
|
/* This event was in the past */
|
|
|
|
|
//us_ticker_irq_handler();
|
2016-08-16 11:51:48 +00:00
|
|
|
|
// This event was in the past.
|
|
|
|
|
// Set the interrupt as pending, but don't process it here.
|
|
|
|
|
// This prevents a recurive loop under heavy load
|
|
|
|
|
// which can lead to a stack overflow.
|
|
|
|
|
NVIC_SetPendingIRQ(Tim1_IRQn);
|
|
|
|
|
|
2016-08-26 11:22:11 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-08-16 11:51:48 +00:00
|
|
|
|
|
2017-06-30 11:44:02 +00:00
|
|
|
|
// we set the full reminder of 16 bit, the next ISR will do the upper part
|
|
|
|
|
ticker_set(delta & 0xFFFF);
|
2016-08-16 11:51:48 +00:00
|
|
|
|
}
|
2018-07-25 06:40:30 +00:00
|
|
|
|
|
|
|
|
|
void us_ticker_free(void)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|