/* * 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 #include #include #include #include #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; }