mirror of https://github.com/ARMmbed/mbed-os.git
211 lines
6.5 KiB
C
211 lines
6.5 KiB
C
/**
|
|
******************************************************************************
|
|
* @file us_ticker_api.h
|
|
* @brief Implementation of a Timer driver
|
|
* @internal
|
|
* @author ON Semiconductor
|
|
* $Rev: $
|
|
* $Date: 2015-11-15 $
|
|
******************************************************************************
|
|
* Copyright 2016 Semiconductor Components Industries LLC (d/b/a “ON Semiconductor”).
|
|
* 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
|
|
* (“ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software”) 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.
|
|
*
|
|
* 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>
|
|
#include "timer_ncs36510.h"
|
|
|
|
#define US_TIMER TIMER0
|
|
#define US_TICKER TIMER1
|
|
|
|
static int us_ticker_inited = 0;
|
|
|
|
static void us_timer_init(void);
|
|
|
|
static uint32_t us_ticker_target = 0;
|
|
static volatile uint16_t msb_counter = 0;
|
|
|
|
void us_ticker_init(void)
|
|
{
|
|
if (!us_ticker_inited) {
|
|
us_timer_init();
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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)
|
|
{
|
|
TIM0REG->CLEAR = 0;
|
|
msb_counter++;
|
|
}
|
|
|
|
/* Initializing TIMER 0(TImer) and TIMER 1(Ticker) */
|
|
static void us_timer_init(void)
|
|
{
|
|
/* Enable the timer0 periphery clock */
|
|
CLOCK_ENABLE(CLOCK_TIMER0);
|
|
/* Enable the timer0 periphery clock */
|
|
CLOCK_ENABLE(CLOCK_TIMER1);
|
|
|
|
/* Timer init */
|
|
/* load timer value */
|
|
TIM0REG->LOAD = 0xFFFF;
|
|
|
|
/* set timer prescale 32 (1 us), mode & enable */
|
|
TIM0REG->CONTROL.WORD = ((CLK_DIVIDER_32 << TIMER_PRESCALE_BIT_POS) |
|
|
(TIME_MODE_PERIODIC << TIMER_MODE_BIT_POS) |
|
|
(TIMER_ENABLE_BIT << TIMER_ENABLE_BIT_POS));
|
|
|
|
/* Ticker init */
|
|
/* load timer value */
|
|
TIM1REG->LOAD = 0xFFFF;
|
|
|
|
/* set timer prescale 32 (1 us), mode & enable */
|
|
TIM1REG->CONTROL.WORD = ((CLK_DIVIDER_32 << TIMER_PRESCALE_BIT_POS) |
|
|
(TIME_MODE_PERIODIC << TIMER_MODE_BIT_POS));
|
|
|
|
/* 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);
|
|
|
|
/* Clear pending irqs */
|
|
NVIC_ClearPendingIRQ(Tim0_IRQn);
|
|
NVIC_ClearPendingIRQ(Tim1_IRQn);
|
|
|
|
/* Setup NVIC for timer */
|
|
NVIC_EnableIRQ(Tim0_IRQn);
|
|
NVIC_EnableIRQ(Tim1_IRQn);
|
|
|
|
us_ticker_inited = 1;
|
|
}
|
|
|
|
/* Reads 32 bit timer's current value (16 bit s/w timer | 16 bit h/w timer) */
|
|
uint32_t us_ticker_read()
|
|
{
|
|
|
|
if (!us_ticker_inited) {
|
|
us_timer_init();
|
|
}
|
|
|
|
NVIC_DisableIRQ(Tim0_IRQn);
|
|
uint32_t retval, tim0cval;
|
|
/* Get the current tick from the hw and sw timers */
|
|
tim0cval = TIM0REG->VALUE; /* read current time */
|
|
retval = (0xFFFF - tim0cval); /* subtract down count */
|
|
|
|
if (TIM0REG->CONTROL.BITS.INT) {
|
|
us_timer_isr(); /* handle ISR again */
|
|
NVIC_ClearPendingIRQ(Tim0_IRQn);
|
|
retval = (0xFFFF - TIM0REG->VALUE);
|
|
}
|
|
retval |= msb_counter << 16; /* add software bits */
|
|
NVIC_EnableIRQ(Tim0_IRQn);
|
|
return retval;
|
|
}
|
|
|
|
void us_ticker_fire_interrupt(void)
|
|
{
|
|
us_ticker_target = 0;
|
|
NVIC_SetPendingIRQ(Tim1_IRQn);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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)
|
|
{
|
|
/* Disable the TIMER1 interrupt */
|
|
TIM1REG->CONTROL.BITS.ENABLE = 0x0;
|
|
}
|
|
|
|
/* TImer 1 clear interrupt */
|
|
void us_ticker_clear_interrupt(void)
|
|
{
|
|
/* Clear the Ticker (TIMER1) interrupt */
|
|
TIM1REG->CLEAR = 0;
|
|
}
|
|
|
|
/* Setting TImer 1 (ticker) */
|
|
inline static void ticker_set(uint32_t count)
|
|
{
|
|
/* Disable TIMER1, load the new value, and re-enable */
|
|
TIM1REG->CONTROL.BITS.ENABLE = 0;
|
|
TIM1REG->LOAD = count;
|
|
TIM1REG->CONTROL.BITS.ENABLE = 1;
|
|
}
|
|
|
|
/* TImer 1 - ticker ISR */
|
|
extern void us_ticker_isr(void)
|
|
{
|
|
/* Clear IRQ flag */
|
|
TIM1REG->CLEAR = 0;
|
|
|
|
if (us_ticker_target > 0) {
|
|
--us_ticker_target;
|
|
ticker_set(0xFFFF);
|
|
} else {
|
|
us_ticker_irq_handler();
|
|
}
|
|
}
|
|
|
|
/* Set timer 1 ticker interrupt */
|
|
void us_ticker_set_interrupt(timestamp_t timestamp)
|
|
{
|
|
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;
|
|
|
|
if (delta <= 0) {
|
|
/* This event was in the past */
|
|
//us_ticker_irq_handler();
|
|
// 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);
|
|
|
|
return;
|
|
}
|
|
|
|
// we set the full reminder of 16 bit, the next ISR will do the upper part
|
|
ticker_set(delta & 0xFFFF);
|
|
}
|
|
|
|
void us_ticker_free(void)
|
|
{
|
|
|
|
}
|