/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you 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 <string.h>
|
#include <stdint.h>
|
#include <assert.h>
|
#include <errno.h>
|
#include <hal/nrf_rtc.h>
|
#include "os/mynewt.h"
|
#include "mcu/cmsis_nvic.h"
|
#include "hal/hal_timer.h"
|
#include "nrf.h"
|
#include "mcu/nrf52_hal.h"
|
#include "mcu/nrf52_clock.h"
|
#include "hal/nrf_timer.h"
|
|
/* IRQ prototype */
|
typedef void (*hal_timer_irq_handler_t)(void);
|
|
/* User CC 2 for reading counter, CC 3 for timer isr */
|
#define NRF_TIMER_CC_READ (NRF_TIMER_CC_CHANNEL2)
|
#define NRF_TIMER_CC_INT (3)
|
|
/* Output compare 2 used for RTC timers */
|
#define NRF_RTC_TIMER_CC_INT (2)
|
|
/* Maximum number of hal timers used */
|
#define NRF52_HAL_TIMER_MAX (6)
|
|
/* Maximum timer frequency */
|
#define NRF52_MAX_TIMER_FREQ (16000000)
|
|
struct nrf52_hal_timer {
|
uint8_t tmr_enabled;
|
uint8_t tmr_irq_num;
|
uint8_t tmr_rtc;
|
uint8_t tmr_pad;
|
uint32_t tmr_cntr;
|
uint32_t timer_isrs;
|
uint32_t tmr_freq;
|
void *tmr_reg;
|
TAILQ_HEAD(hal_timer_qhead, hal_timer) hal_timer_q;
|
};
|
|
#if MYNEWT_VAL(TIMER_0)
|
struct nrf52_hal_timer nrf52_hal_timer0;
|
#endif
|
#if MYNEWT_VAL(TIMER_1)
|
struct nrf52_hal_timer nrf52_hal_timer1;
|
#endif
|
#if MYNEWT_VAL(TIMER_2)
|
struct nrf52_hal_timer nrf52_hal_timer2;
|
#endif
|
#if MYNEWT_VAL(TIMER_3)
|
struct nrf52_hal_timer nrf52_hal_timer3;
|
#endif
|
#if MYNEWT_VAL(TIMER_4)
|
struct nrf52_hal_timer nrf52_hal_timer4;
|
#endif
|
#if MYNEWT_VAL(TIMER_5)
|
struct nrf52_hal_timer nrf52_hal_timer5;
|
#endif
|
|
static const struct nrf52_hal_timer *nrf52_hal_timers[NRF52_HAL_TIMER_MAX] = {
|
#if MYNEWT_VAL(TIMER_0)
|
&nrf52_hal_timer0,
|
#else
|
NULL,
|
#endif
|
#if MYNEWT_VAL(TIMER_1)
|
&nrf52_hal_timer1,
|
#else
|
NULL,
|
#endif
|
#if MYNEWT_VAL(TIMER_2)
|
&nrf52_hal_timer2,
|
#else
|
NULL,
|
#endif
|
#if MYNEWT_VAL(TIMER_3)
|
&nrf52_hal_timer3,
|
#else
|
NULL,
|
#endif
|
#if MYNEWT_VAL(TIMER_4)
|
&nrf52_hal_timer4,
|
#else
|
NULL,
|
#endif
|
#if MYNEWT_VAL(TIMER_5)
|
&nrf52_hal_timer5
|
#else
|
NULL
|
#endif
|
};
|
|
/* Resolve timer number into timer structure */
|
#define NRF52_HAL_TIMER_RESOLVE(__n, __v) \
|
if ((__n) >= NRF52_HAL_TIMER_MAX) { \
|
rc = EINVAL; \
|
goto err; \
|
} \
|
(__v) = (struct nrf52_hal_timer *) nrf52_hal_timers[(__n)]; \
|
if ((__v) == NULL) { \
|
rc = EINVAL; \
|
goto err; \
|
}
|
|
/* Interrupt mask for interrupt enable/clear */
|
#define NRF_TIMER_INT_MASK(x) ((1 << (uint32_t)(x)) << 16)
|
|
static uint32_t
|
nrf_read_timer_cntr(NRF_TIMER_Type *hwtimer)
|
{
|
uint32_t tcntr;
|
|
/* Force a capture of the timer into 'cntr' capture channel; read it */
|
nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_CAPTURE2);
|
tcntr = hwtimer->CC[NRF_TIMER_CC_READ];
|
|
return tcntr;
|
}
|
|
/**
|
* nrf timer set ocmp
|
*
|
* Set the OCMP used by the timer to the desired expiration tick
|
*
|
* NOTE: Must be called with interrupts disabled.
|
*
|
* @param timer Pointer to timer.
|
*/
|
static void
|
nrf_timer_set_ocmp(struct nrf52_hal_timer *bsptimer, uint32_t expiry)
|
{
|
int32_t delta_t;
|
uint32_t temp;
|
uint32_t cntr;
|
NRF_TIMER_Type *hwtimer;
|
NRF_RTC_Type *rtctimer;
|
|
if (bsptimer->tmr_rtc) {
|
rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg;
|
nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT));
|
temp = bsptimer->tmr_cntr;
|
cntr = nrf_rtc_counter_get(rtctimer);
|
if (rtctimer->EVENTS_OVRFLW) {
|
temp += (1UL << 24);
|
cntr = nrf_rtc_counter_get(rtctimer);
|
}
|
temp |= cntr;
|
delta_t = (int32_t)(expiry - temp);
|
|
/*
|
* The nRF52xxx documentation states that COMPARE event is guaranteed
|
* only if value written to CC register is at least 2 greater than the
|
* current counter value. We also need to account for possible extra
|
* tick during calculations so effectively any delta less than 3 needs
|
* to be handled differently. TICK event is used to have interrupt on
|
* each subsequent tick so we won't miss any and in case we detected
|
* mentioned extra tick during calculations, interrupt is triggered
|
* immediately. Delta 0 or less means we should always fire immediately.
|
*/
|
if (delta_t < 1) {
|
nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk);
|
NVIC_SetPendingIRQ(bsptimer->tmr_irq_num);
|
} else if (delta_t < 3 && 0) {
|
nrf_rtc_int_enable(rtctimer, RTC_INTENSET_TICK_Msk);
|
if (nrf_rtc_counter_get(rtctimer) != cntr) {
|
NVIC_SetPendingIRQ(bsptimer->tmr_irq_num);
|
}
|
} else {
|
nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk);
|
|
if (delta_t < (1UL << 24)) {
|
nrf_rtc_cc_set(rtctimer, NRF_RTC_TIMER_CC_INT, expiry & 0x00ffffff);
|
} else {
|
/* CC too far ahead. Just make sure we set compare far ahead */
|
nrf_rtc_cc_set(rtctimer, NRF_RTC_TIMER_CC_INT, cntr + (1UL << 23));
|
}
|
nrf_rtc_int_enable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT));
|
}
|
} else {
|
hwtimer = bsptimer->tmr_reg;
|
|
/* Disable ocmp interrupt and set new value */
|
nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT));
|
|
/* Set output compare register to timer expiration */
|
nrf_timer_cc_set(hwtimer, NRF_TIMER_CC_INT, expiry);
|
|
/* Clear interrupt flag */
|
hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT] = 0;
|
|
/* Enable the output compare interrupt */
|
nrf_timer_int_enable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT));
|
|
/* Force interrupt to occur as we may have missed it */
|
if ((int32_t)(nrf_read_timer_cntr(hwtimer) - expiry) >= 0) {
|
NVIC_SetPendingIRQ(bsptimer->tmr_irq_num);
|
}
|
}
|
}
|
|
/* Disable output compare used for timer */
|
static void
|
nrf_timer_disable_ocmp(NRF_TIMER_Type *hwtimer)
|
{
|
nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT));
|
}
|
|
static void
|
nrf_rtc_disable_ocmp(NRF_RTC_Type *rtctimer)
|
{
|
nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT));
|
nrf_rtc_int_disable(rtctimer, RTC_INTENCLR_TICK_Msk);
|
}
|
|
static uint32_t
|
hal_timer_read_bsptimer(struct nrf52_hal_timer *bsptimer)
|
{
|
uint32_t low32;
|
uint32_t ctx;
|
uint32_t tcntr;
|
NRF_RTC_Type *rtctimer;
|
|
rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg;
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
tcntr = bsptimer->tmr_cntr;
|
low32 = nrf_rtc_counter_get(rtctimer);
|
if (rtctimer->EVENTS_OVRFLW) {
|
tcntr += (1UL << 24);
|
bsptimer->tmr_cntr = tcntr;
|
low32 = nrf_rtc_counter_get(rtctimer);
|
rtctimer->EVENTS_OVRFLW = 0;
|
NVIC_SetPendingIRQ(bsptimer->tmr_irq_num);
|
}
|
tcntr |= low32;
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
|
return tcntr;
|
}
|
|
#if (MYNEWT_VAL(TIMER_0) || MYNEWT_VAL(TIMER_1) || MYNEWT_VAL(TIMER_2) || \
|
MYNEWT_VAL(TIMER_3) || MYNEWT_VAL(TIMER_4) || MYNEWT_VAL(TIMER_5))
|
/**
|
* hal timer chk queue
|
*
|
*
|
* @param bsptimer
|
*/
|
static void
|
hal_timer_chk_queue(struct nrf52_hal_timer *bsptimer)
|
{
|
uint32_t tcntr;
|
uint32_t ctx;
|
struct hal_timer *timer;
|
|
/* disable interrupts */
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
while ((timer = TAILQ_FIRST(&bsptimer->hal_timer_q)) != NULL) {
|
if (bsptimer->tmr_rtc) {
|
tcntr = hal_timer_read_bsptimer(bsptimer);
|
} else {
|
tcntr = nrf_read_timer_cntr(bsptimer->tmr_reg);
|
}
|
if ((int32_t)(tcntr - timer->expiry) >= 0) {
|
TAILQ_REMOVE(&bsptimer->hal_timer_q, timer, link);
|
timer->link.tqe_prev = NULL;
|
timer->cb_func(timer->cb_arg);
|
} else {
|
break;
|
}
|
}
|
|
/* Any timers left on queue? If so, we need to set OCMP */
|
timer = TAILQ_FIRST(&bsptimer->hal_timer_q);
|
if (timer) {
|
nrf_timer_set_ocmp(bsptimer, timer->expiry);
|
} else {
|
if (bsptimer->tmr_rtc) {
|
nrf_rtc_disable_ocmp((NRF_RTC_Type *)bsptimer->tmr_reg);
|
} else {
|
nrf_timer_disable_ocmp(bsptimer->tmr_reg);
|
}
|
}
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
}
|
#endif
|
|
/**
|
* hal timer irq handler
|
*
|
* Generic HAL timer irq handler.
|
*
|
* @param tmr
|
*/
|
/**
|
* hal timer irq handler
|
*
|
* This is the global timer interrupt routine.
|
*
|
*/
|
#if (MYNEWT_VAL(TIMER_0) || MYNEWT_VAL(TIMER_1) || MYNEWT_VAL(TIMER_2) || \
|
MYNEWT_VAL(TIMER_3) || MYNEWT_VAL(TIMER_4))
|
|
static void
|
hal_timer_irq_handler(struct nrf52_hal_timer *bsptimer)
|
{
|
uint32_t compare;
|
NRF_TIMER_Type *hwtimer;
|
|
os_trace_isr_enter();
|
|
/* Check interrupt source. If set, clear them */
|
hwtimer = bsptimer->tmr_reg;
|
compare = hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT];
|
if (compare) {
|
hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT] = 0;
|
}
|
|
/* XXX: make these stats? */
|
/* Count # of timer isrs */
|
++bsptimer->timer_isrs;
|
|
/*
|
* NOTE: we dont check the 'compare' variable here due to how the timer
|
* is implemented on this chip. There is no way to force an output
|
* compare, so if we are late setting the output compare (i.e. the timer
|
* counter is already passed the output compare value), we use the NVIC
|
* to set a pending interrupt. This means that there will be no compare
|
* flag set, so all we do is check to see if the compare interrupt is
|
* enabled.
|
*/
|
if (hwtimer->INTENCLR & NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT)) {
|
hal_timer_chk_queue(bsptimer);
|
/* XXX: Recommended by nordic to make sure interrupts are cleared */
|
compare = hwtimer->EVENTS_COMPARE[NRF_TIMER_CC_INT];
|
}
|
|
os_trace_isr_exit();
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_5)
|
static void
|
hal_rtc_timer_irq_handler(struct nrf52_hal_timer *bsptimer)
|
{
|
uint32_t overflow;
|
uint32_t compare;
|
uint32_t tick;
|
NRF_RTC_Type *rtctimer;
|
|
os_trace_isr_enter();
|
|
/* Check interrupt source. If set, clear them */
|
rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg;
|
compare = rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT];
|
if (compare) {
|
rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT] = 0;
|
}
|
|
tick = rtctimer->EVENTS_TICK;
|
if (tick) {
|
rtctimer->EVENTS_TICK = 0;
|
}
|
|
overflow = rtctimer->EVENTS_OVRFLW;
|
if (overflow) {
|
rtctimer->EVENTS_OVRFLW = 0;
|
bsptimer->tmr_cntr += (1UL << 24);
|
}
|
|
/* Count # of timer isrs */
|
++bsptimer->timer_isrs;
|
|
/*
|
* NOTE: we dont check the 'compare' variable here due to how the timer
|
* is implemented on this chip. There is no way to force an output
|
* compare, so if we are late setting the output compare (i.e. the timer
|
* counter is already passed the output compare value), we use the NVIC
|
* to set a pending interrupt. This means that there will be no compare
|
* flag set, so all we do is check to see if the compare interrupt is
|
* enabled.
|
*/
|
hal_timer_chk_queue(bsptimer);
|
|
/* Recommended by nordic to make sure interrupts are cleared */
|
compare = rtctimer->EVENTS_COMPARE[NRF_RTC_TIMER_CC_INT];
|
|
os_trace_isr_exit();
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_0)
|
void
|
nrf52_timer0_irq_handler(void)
|
{
|
hal_timer_irq_handler(&nrf52_hal_timer0);
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_1)
|
void
|
nrf52_timer1_irq_handler(void)
|
{
|
hal_timer_irq_handler(&nrf52_hal_timer1);
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_2)
|
void
|
nrf52_timer2_irq_handler(void)
|
{
|
hal_timer_irq_handler(&nrf52_hal_timer2);
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_3)
|
void
|
nrf52_timer3_irq_handler(void)
|
{
|
hal_timer_irq_handler(&nrf52_hal_timer3);
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_4)
|
void
|
nrf52_timer4_irq_handler(void)
|
{
|
hal_timer_irq_handler(&nrf52_hal_timer4);
|
}
|
#endif
|
|
#if MYNEWT_VAL(TIMER_5)
|
void
|
nrf52_timer5_irq_handler(void)
|
{
|
hal_rtc_timer_irq_handler(&nrf52_hal_timer5);
|
}
|
#endif
|
|
/**
|
* hal timer init
|
*
|
* Initialize platform specific timer items
|
*
|
* @param timer_num Timer number to initialize
|
* @param cfg Pointer to platform specific configuration
|
*
|
* @return int 0: success; error code otherwise
|
*/
|
int
|
hal_timer_init(int timer_num, void *cfg)
|
{
|
int rc;
|
uint8_t irq_num;
|
struct nrf52_hal_timer *bsptimer;
|
void *hwtimer;
|
hal_timer_irq_handler_t irq_isr;
|
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
|
/* If timer is enabled do not allow init */
|
if (bsptimer->tmr_enabled) {
|
rc = EINVAL;
|
goto err;
|
}
|
|
switch (timer_num) {
|
#if MYNEWT_VAL(TIMER_0)
|
case 0:
|
irq_num = TIMER0_IRQn;
|
hwtimer = NRF_TIMER0;
|
irq_isr = nrf52_timer0_irq_handler;
|
break;
|
#endif
|
#if MYNEWT_VAL(TIMER_1)
|
case 1:
|
irq_num = TIMER1_IRQn;
|
hwtimer = NRF_TIMER1;
|
irq_isr = nrf52_timer1_irq_handler;
|
break;
|
#endif
|
#if MYNEWT_VAL(TIMER_2)
|
case 2:
|
irq_num = TIMER2_IRQn;
|
hwtimer = NRF_TIMER2;
|
irq_isr = nrf52_timer2_irq_handler;
|
break;
|
#endif
|
#if MYNEWT_VAL(TIMER_3)
|
case 3:
|
irq_num = TIMER3_IRQn;
|
hwtimer = NRF_TIMER3;
|
irq_isr = nrf52_timer3_irq_handler;
|
break;
|
#endif
|
#if MYNEWT_VAL(TIMER_4)
|
case 4:
|
irq_num = TIMER4_IRQn;
|
hwtimer = NRF_TIMER4;
|
irq_isr = nrf52_timer4_irq_handler;
|
break;
|
#endif
|
#if MYNEWT_VAL(TIMER_5)
|
case 5:
|
irq_num = RTC0_IRQn;
|
hwtimer = NRF_RTC0;
|
irq_isr = nrf52_timer5_irq_handler;
|
bsptimer->tmr_rtc = 1;
|
break;
|
#endif
|
default:
|
hwtimer = NULL;
|
break;
|
}
|
|
if (hwtimer == NULL) {
|
rc = EINVAL;
|
goto err;
|
}
|
|
bsptimer->tmr_reg = hwtimer;
|
bsptimer->tmr_irq_num = irq_num;
|
|
/* Disable IRQ, set priority and set vector in table */
|
NVIC_DisableIRQ(irq_num);
|
NVIC_SetPriority(irq_num, (1 << __NVIC_PRIO_BITS) - 1);
|
NVIC_SetVector(irq_num, (uint32_t)irq_isr);
|
|
return 0;
|
|
err:
|
return rc;
|
}
|
|
/**
|
* hal timer config
|
*
|
* Configure a timer to run at the desired frequency. This starts the timer.
|
*
|
* @param timer_num
|
* @param freq_hz
|
*
|
* @return int
|
*/
|
int
|
hal_timer_config(int timer_num, uint32_t freq_hz)
|
{
|
int rc;
|
uint8_t prescaler;
|
uint32_t ctx;
|
uint32_t div;
|
uint32_t min_delta;
|
uint32_t max_delta;
|
struct nrf52_hal_timer *bsptimer;
|
NRF_TIMER_Type *hwtimer;
|
#if MYNEWT_VAL(TIMER_5)
|
NRF_RTC_Type *rtctimer;
|
#endif
|
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
|
#if MYNEWT_VAL(TIMER_5)
|
if (timer_num == 5) {
|
/* NOTE: we only allow the RTC frequency to be set at 32768 */
|
if (bsptimer->tmr_enabled || (freq_hz != 32768) ||
|
(bsptimer->tmr_reg == NULL)) {
|
rc = EINVAL;
|
goto err;
|
}
|
|
bsptimer->tmr_freq = freq_hz;
|
bsptimer->tmr_enabled = 1;
|
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
|
rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg;
|
|
/* Stop the timer first */
|
nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_STOP);
|
nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_CLEAR);
|
|
/* Always no prescaler */
|
rtctimer->PRESCALER = 0;
|
|
/* Clear overflow events and set overflow interrupt */
|
rtctimer->EVENTS_OVRFLW = 0;
|
nrf_rtc_int_enable(rtctimer, RTC_INTENSET_OVRFLW_Msk);
|
|
/* Start the timer */
|
nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_START);
|
/* Set isr in vector table and enable interrupt */
|
NVIC_EnableIRQ(bsptimer->tmr_irq_num);
|
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
return 0;
|
}
|
#endif
|
|
/* Set timer to desired frequency */
|
div = NRF52_MAX_TIMER_FREQ / freq_hz;
|
|
/*
|
* Largest prescaler is 2^9 and must make sure frequency not too high.
|
* If hwtimer is NULL it means that the timer was not initialized prior
|
* to call.
|
*/
|
if (bsptimer->tmr_enabled || (div == 0) || (div > 512) ||
|
(bsptimer->tmr_reg == NULL)) {
|
rc = EINVAL;
|
goto err;
|
}
|
|
if (div == 1) {
|
prescaler = 0;
|
} else {
|
/* Find closest prescaler */
|
for (prescaler = 1; prescaler < 10; ++prescaler) {
|
if (div <= (1 << prescaler)) {
|
min_delta = div - (1 << (prescaler - 1));
|
max_delta = (1 << prescaler) - div;
|
if (min_delta < max_delta) {
|
prescaler -= 1;
|
}
|
break;
|
}
|
}
|
}
|
|
/* Now set the actual frequency */
|
bsptimer->tmr_freq = NRF52_MAX_TIMER_FREQ / (1 << prescaler);
|
bsptimer->tmr_enabled = 1;
|
|
/* disable interrupts */
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
|
#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFXO)
|
/* Make sure HFXO is started */
|
nrf52_clock_hfxo_request();
|
#endif
|
hwtimer = bsptimer->tmr_reg;
|
|
/* Stop the timer first */
|
nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_STOP);
|
nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_CLEAR);
|
|
/* Put the timer in timer mode using 32 bits. */
|
nrf_timer_mode_set(hwtimer, NRF_TIMER_MODE_TIMER);
|
hwtimer->BITMODE = TIMER_BITMODE_BITMODE_32Bit;
|
|
/* Set the pre-scalar */
|
hwtimer->PRESCALER = prescaler;
|
|
/* Start the timer */
|
nrf_timer_task_trigger(hwtimer, NRF_TIMER_TASK_START);
|
|
NVIC_EnableIRQ(bsptimer->tmr_irq_num);
|
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
|
return 0;
|
|
err:
|
return rc;
|
}
|
|
/**
|
* hal timer deinit
|
*
|
* De-initialize a HW timer.
|
*
|
* @param timer_num
|
*
|
* @return int
|
*/
|
int
|
hal_timer_deinit(int timer_num)
|
{
|
int rc;
|
uint32_t ctx;
|
struct nrf52_hal_timer *bsptimer;
|
NRF_TIMER_Type *hwtimer;
|
NRF_RTC_Type *rtctimer;
|
|
rc = 0;
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
if (bsptimer->tmr_rtc) {
|
rtctimer = (NRF_RTC_Type *)bsptimer->tmr_reg;
|
nrf_rtc_int_disable(rtctimer, NRF_TIMER_INT_MASK(NRF_RTC_TIMER_CC_INT));
|
nrf_rtc_task_trigger(rtctimer, NRF_RTC_TASK_STOP);
|
} else {
|
hwtimer = (NRF_TIMER_Type *)bsptimer->tmr_reg;
|
nrf_timer_int_disable(hwtimer, NRF_TIMER_INT_MASK(NRF_TIMER_CC_INT));
|
hwtimer->TASKS_SHUTDOWN = 1;
|
}
|
bsptimer->tmr_enabled = 0;
|
bsptimer->tmr_reg = NULL;
|
|
#if MYNEWT_VAL_CHOICE(MCU_HFCLK_SOURCE, HFXO)
|
if (timer_num != 5) {
|
nrf52_clock_hfxo_release();
|
}
|
#endif
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
|
err:
|
return rc;
|
}
|
|
/**
|
* hal timer get resolution
|
*
|
* Get the resolution of the timer. This is the timer period, in nanoseconds
|
*
|
* @param timer_num
|
*
|
* @return uint32_t The
|
*/
|
uint32_t
|
hal_timer_get_resolution(int timer_num)
|
{
|
int rc;
|
uint32_t resolution;
|
struct nrf52_hal_timer *bsptimer;
|
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
|
resolution = 1000000000 / bsptimer->tmr_freq;
|
return resolution;
|
|
err:
|
rc = 0;
|
return rc;
|
}
|
|
/**
|
* hal timer read
|
*
|
* Returns the timer counter. NOTE: if the timer is a 16-bit timer, only
|
* the lower 16 bits are valid. If the timer is a 64-bit timer, only the
|
* low 32-bits are returned.
|
*
|
* @return uint32_t The timer counter register.
|
*/
|
uint32_t
|
hal_timer_read(int timer_num)
|
{
|
int rc;
|
uint32_t tcntr;
|
struct nrf52_hal_timer *bsptimer;
|
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
if (bsptimer->tmr_rtc) {
|
tcntr = hal_timer_read_bsptimer(bsptimer);
|
} else {
|
tcntr = nrf_read_timer_cntr(bsptimer->tmr_reg);
|
}
|
|
return tcntr;
|
|
/* Assert here since there is no invalid return code */
|
err:
|
assert(0);
|
rc = 0;
|
return rc;
|
}
|
|
/**
|
* hal timer delay
|
*
|
* Blocking delay for n ticks
|
*
|
* @param timer_num
|
* @param ticks
|
*
|
* @return int 0 on success; error code otherwise.
|
*/
|
int
|
hal_timer_delay(int timer_num, uint32_t ticks)
|
{
|
uint32_t until;
|
|
until = hal_timer_read(timer_num) + ticks;
|
while ((int32_t)(hal_timer_read(timer_num) - until) <= 0) {
|
/* Loop here till finished */
|
}
|
|
return 0;
|
}
|
|
/**
|
*
|
* Initialize the HAL timer structure with the callback and the callback
|
* argument. Also initializes the HW specific timer pointer.
|
*
|
* @param cb_func
|
*
|
* @return int
|
*/
|
int
|
hal_timer_set_cb(int timer_num, struct hal_timer *timer, hal_timer_cb cb_func,
|
void *arg)
|
{
|
int rc;
|
struct nrf52_hal_timer *bsptimer;
|
|
NRF52_HAL_TIMER_RESOLVE(timer_num, bsptimer);
|
|
timer->cb_func = cb_func;
|
timer->cb_arg = arg;
|
timer->link.tqe_prev = NULL;
|
timer->bsp_timer = bsptimer;
|
|
rc = 0;
|
|
err:
|
return rc;
|
}
|
|
int
|
hal_timer_start(struct hal_timer *timer, uint32_t ticks)
|
{
|
int rc;
|
uint32_t tick;
|
struct nrf52_hal_timer *bsptimer;
|
|
/* Set the tick value at which the timer should expire */
|
bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer;
|
if (bsptimer->tmr_rtc) {
|
tick = hal_timer_read_bsptimer(bsptimer) + ticks;
|
} else {
|
tick = nrf_read_timer_cntr(bsptimer->tmr_reg) + ticks;
|
}
|
rc = hal_timer_start_at(timer, tick);
|
return rc;
|
}
|
|
int
|
hal_timer_start_at(struct hal_timer *timer, uint32_t tick)
|
{
|
uint32_t ctx;
|
struct hal_timer *entry;
|
struct nrf52_hal_timer *bsptimer;
|
|
if ((timer == NULL) || (timer->link.tqe_prev != NULL) ||
|
(timer->cb_func == NULL)) {
|
return EINVAL;
|
}
|
bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer;
|
timer->expiry = tick;
|
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
|
if (TAILQ_EMPTY(&bsptimer->hal_timer_q)) {
|
TAILQ_INSERT_HEAD(&bsptimer->hal_timer_q, timer, link);
|
} else {
|
TAILQ_FOREACH(entry, &bsptimer->hal_timer_q, link) {
|
if ((int32_t)(timer->expiry - entry->expiry) < 0) {
|
TAILQ_INSERT_BEFORE(entry, timer, link);
|
break;
|
}
|
}
|
if (!entry) {
|
TAILQ_INSERT_TAIL(&bsptimer->hal_timer_q, timer, link);
|
}
|
}
|
|
/* If this is the head, we need to set new OCMP */
|
if (timer == TAILQ_FIRST(&bsptimer->hal_timer_q)) {
|
nrf_timer_set_ocmp(bsptimer, timer->expiry);
|
}
|
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
|
return 0;
|
}
|
|
/**
|
* hal timer stop
|
*
|
* Stop a timer.
|
*
|
* @param timer
|
*
|
* @return int
|
*/
|
int
|
hal_timer_stop(struct hal_timer *timer)
|
{
|
uint32_t ctx;
|
int reset_ocmp;
|
struct hal_timer *entry;
|
struct nrf52_hal_timer *bsptimer;
|
|
if (timer == NULL) {
|
return EINVAL;
|
}
|
|
bsptimer = (struct nrf52_hal_timer *)timer->bsp_timer;
|
|
__HAL_DISABLE_INTERRUPTS(ctx);
|
|
if (timer->link.tqe_prev != NULL) {
|
reset_ocmp = 0;
|
if (timer == TAILQ_FIRST(&bsptimer->hal_timer_q)) {
|
/* If first on queue, we will need to reset OCMP */
|
entry = TAILQ_NEXT(timer, link);
|
reset_ocmp = 1;
|
}
|
TAILQ_REMOVE(&bsptimer->hal_timer_q, timer, link);
|
timer->link.tqe_prev = NULL;
|
if (reset_ocmp) {
|
if (entry) {
|
nrf_timer_set_ocmp((struct nrf52_hal_timer *)entry->bsp_timer,
|
entry->expiry);
|
} else {
|
if (bsptimer->tmr_rtc) {
|
nrf_rtc_disable_ocmp((NRF_RTC_Type *)bsptimer->tmr_reg);
|
} else {
|
nrf_timer_disable_ocmp(bsptimer->tmr_reg);
|
}
|
}
|
}
|
}
|
|
__HAL_ENABLE_INTERRUPTS(ctx);
|
|
return 0;
|
}
|