t
Fork of mbed-dev by
Diff: targets/TARGET_ONSEMI/TARGET_NCS36510/ncs36510_us_ticker_api.c
- Revision:
- 149:156823d33999
- Child:
- 159:612c381a210f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/TARGET_ONSEMI/TARGET_NCS36510/ncs36510_us_ticker_api.c Fri Oct 28 11:17:30 2016 +0100 @@ -0,0 +1,204 @@ +/** + ****************************************************************************** + * @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.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_int_counter = 0; +static volatile uint32_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 - Add msb_counter rollover protection at 16 bits count? */ +/* 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() +{ + uint32_t retval, tim0cval; + + if (!us_ticker_inited) { + us_timer_init(); + } + + /* Get the current tick from the hw and sw timers */ + tim0cval = TIM0REG->VALUE; /* read current time */ + retval = (0xFFFF - tim0cval); /* subtract down count */ + + NVIC_DisableIRQ(Tim0_IRQn); + if (TIM0REG->CONTROL.BITS.INT) { + TIM0REG->CLEAR = 0; + msb_counter++; + tim0cval = TIM0REG->VALUE; /* read current time again after interrupt */ + retval = (0xFFFF - tim0cval); + } + retval |= msb_counter << 16; /* add software bits */ + NVIC_EnableIRQ(Tim0_IRQn); + return retval; +} + +/******************************************************************************* + * 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 this is a longer timer it will take multiple full hw counter cycles */ + if (us_ticker_int_counter > 0) { + ticker_set(0xFFFF); + us_ticker_int_counter--; + } else { + TIM1REG->CONTROL.BITS.ENABLE = False; + us_ticker_irq_handler(); + } +} + +/* Set timer 1 ticker interrupt */ +void us_ticker_set_interrupt(timestamp_t timestamp) +{ + int32_t delta = (uint32_t)timestamp - us_ticker_read(); + + 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; + } + + /* Calculate how much delta falls outside the 16-bit counter range. */ + /* You will have to perform a full timer overflow for each bit above */ + /* that range. */ + us_ticker_int_counter = (uint32_t)(delta >> 16); + + ticker_set(delta); +}