| /**************************************************************************** |
| * arch/arm/src/stm32wb/stm32wb_tickless.c |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Tickless OS Support. |
| * |
| * When CONFIG_SCHED_TICKLESS is enabled, all support for timer interrupts |
| * is suppressed and the platform specific code is expected to provide the |
| * following custom functions. |
| * |
| * void up_timer_initialize(void): Initializes the timer facilities. |
| * Called early in the initialization sequence (by up_initialize()). |
| * int up_timer_gettime(struct timespec *ts): Returns the current |
| * time from the platform specific time source. |
| * int up_timer_cancel(void): Cancels the interval timer. |
| * int up_timer_start(const struct timespec *ts): Start (or re-starts) |
| * the interval timer. |
| * |
| * The RTOS will provide the following interfaces for use by the platform- |
| * specific interval timer implementation: |
| * |
| * void nxsched_timer_expiration(void): Called by the platform-specific |
| * logic when the interval timer expires. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * STM32WB Timer Usage |
| * |
| * This implementation uses one timer: A free running timer to provide |
| * the current time and a capture/compare channel for timed-events. |
| * |
| * There are two interrupts generated from our timer, the overflow interrupt |
| * which drives the timing handler and the capture/compare interrupt which |
| * drives the interval handler. There are some low level timer control |
| * functions implemented here because the API of stm32wb_tim.c does not |
| * provide adequate control over capture/compare interrupts. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| #include <nuttx/arch.h> |
| #include <debug.h> |
| |
| #include "arm_internal.h" |
| #include "stm32wb_tim.h" |
| |
| #ifdef CONFIG_SCHED_TICKLESS |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Only TIM2 is a 32-bit timer */ |
| |
| #undef HAVE_32BIT_TICKLESS |
| |
| #ifdef CONFIG_STM32WB_TICKLESS_TIMER |
| # if CONFIG_STM32WB_TICKLESS_TIMER == 2 |
| # define HAVE_32BIT_TICKLESS 1 |
| # endif |
| #else |
| # error "STM32WB_TICKLESS_TIMER must be defined for tickless configuration" |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct stm32wb_tickless_s |
| { |
| uint8_t timer; /* The timer/counter in use */ |
| uint8_t channel; /* The timer channel to use |
| * for intervals */ |
| struct stm32wb_tim_dev_s *tch; /* Pointer returned by |
| * stm32wb_tim_init() */ |
| uint32_t frequency; |
| uint32_t overflow; /* Timer counter overflow */ |
| volatile bool pending; /* True: pending task */ |
| uint32_t period; /* Interval period */ |
| uint32_t base; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static struct stm32wb_tickless_s g_tickless; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32wb_getreg16 |
| * |
| * Description: |
| * Get a 16-bit register value by offset |
| * |
| ****************************************************************************/ |
| |
| static inline uint16_t stm32wb_getreg16(uint8_t offset) |
| { |
| return getreg16(g_tickless.base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_putreg16 |
| * |
| * Description: |
| * Put a 16-bit register value by offset |
| * |
| ****************************************************************************/ |
| |
| static inline void stm32wb_putreg16(uint8_t offset, uint16_t value) |
| { |
| putreg16(value, g_tickless.base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_modifyreg16 |
| * |
| * Description: |
| * Modify a 16-bit register value by offset |
| * |
| ****************************************************************************/ |
| |
| static inline void stm32wb_modifyreg16(uint8_t offset, uint16_t clearbits, |
| uint16_t setbits) |
| { |
| modifyreg16(g_tickless.base + offset, clearbits, setbits); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_enableint |
| ****************************************************************************/ |
| |
| static inline void stm32wb_tickless_enableint(int channel) |
| { |
| stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 0, 1 << channel); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_disableint |
| ****************************************************************************/ |
| |
| static inline void stm32wb_tickless_disableint(int channel) |
| { |
| stm32wb_modifyreg16(STM32WB_TIM_DIER_OFFSET, 1 << channel, 0); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_ackint |
| ****************************************************************************/ |
| |
| static inline void stm32wb_tickless_ackint(int channel) |
| { |
| stm32wb_putreg16(STM32WB_TIM_SR_OFFSET, ~(1 << channel)); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_getint |
| ****************************************************************************/ |
| |
| static inline uint16_t stm32wb_tickless_getint(void) |
| { |
| return stm32wb_getreg16(STM32WB_TIM_SR_OFFSET); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_setchannel |
| ****************************************************************************/ |
| |
| static int stm32wb_tickless_setchannel(uint8_t channel) |
| { |
| uint16_t ccmr_orig = 0; |
| uint16_t ccmr_val = 0; |
| uint16_t ccer_val; |
| uint8_t ccmr_offset = STM32WB_TIM_CCMR1_OFFSET; |
| |
| /* Further we use range as 0..3; if channel=0 it will also overflow here */ |
| |
| if (--channel > 3) |
| { |
| return -EINVAL; |
| } |
| |
| /* Assume that channel is disabled and polarity is active high */ |
| |
| ccer_val = stm32wb_getreg16(STM32WB_TIM_CCER_OFFSET); |
| ccer_val &= ~(GTIM_CCER_CCXE(channel) | GTIM_CCER_CCXP(channel)); |
| |
| /* Frozen mode because we don't want to change the GPIO, preload register |
| * disabled. |
| */ |
| |
| ccmr_val = GTIM_CCMR_OCXM_FRZN(channel); |
| |
| /* Set polarity */ |
| |
| ccer_val |= GTIM_CCER_CCXP(channel); |
| |
| if (channel > 1) |
| { |
| ccmr_offset = STM32WB_TIM_CCMR2_OFFSET; |
| } |
| |
| ccmr_orig = stm32wb_getreg16(ccmr_offset); |
| ccmr_orig &= ~(GTIM_CCMR_OCXM_MASK(channel) | GTIM_CCMR_OCXPE(channel)); |
| ccmr_orig |= ccmr_val; |
| stm32wb_putreg16(ccmr_offset, ccmr_orig); |
| stm32wb_putreg16(STM32WB_TIM_CCER_OFFSET, ccer_val); |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_interval_handler |
| * |
| * Description: |
| * Called when the timer counter matches the compare register |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * Called early in the initialization sequence before any special |
| * concurrency protections are required. |
| * |
| ****************************************************************************/ |
| |
| static void stm32wb_interval_handler(void) |
| { |
| tmrinfo("Expired...\n"); |
| |
| /* Disable the compare interrupt now. */ |
| |
| stm32wb_tickless_disableint(g_tickless.channel); |
| stm32wb_tickless_ackint(g_tickless.channel); |
| |
| g_tickless.pending = false; |
| |
| nxsched_timer_expiration(); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_timing_handler |
| * |
| * Description: |
| * Timer interrupt callback. When the freerun timer counter overflows, |
| * this interrupt will occur. We will just increment an overflow count. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void stm32wb_timing_handler(void) |
| { |
| g_tickless.overflow++; |
| |
| STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); |
| } |
| |
| /**************************************************************************** |
| * Name: stm32wb_tickless_handler |
| * |
| * Description: |
| * Generic interrupt handler for this timer. It checks the source of the |
| * interrupt and fires the appropriate handler. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static int stm32wb_tickless_handler(int irq, void *context, void *arg) |
| { |
| int interrupt_flags = stm32wb_tickless_getint(); |
| |
| if (interrupt_flags & GTIM_SR_UIF) |
| { |
| stm32wb_timing_handler(); |
| } |
| |
| if (interrupt_flags & (1 << g_tickless.channel)) |
| { |
| stm32wb_interval_handler(); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: up_timer_initialize |
| * |
| * Description: |
| * Initializes all platform-specific timer facilities. This function is |
| * called early in the initialization sequence by up_initialize(). |
| * On return, the current up-time should be available from |
| * up_timer_gettime() and the interval timer is ready for use (but not |
| * actively timing. |
| * |
| * Provided by platform-specific code and called from the architecture- |
| * specific logic. |
| * |
| * Input Parameters: |
| * None |
| * |
| * Returned Value: |
| * None |
| * |
| * Assumptions: |
| * Called early in the initialization sequence before any special |
| * concurrency protections are required. |
| * |
| ****************************************************************************/ |
| |
| void up_timer_initialize(void) |
| { |
| switch (CONFIG_STM32WB_TICKLESS_TIMER) |
| { |
| #ifdef CONFIG_STM32WB_TIM1 |
| case 1: |
| g_tickless.base = STM32WB_TIM1_BASE; |
| break; |
| #endif |
| |
| #ifdef CONFIG_STM32WB_TIM2 |
| case 2: |
| g_tickless.base = STM32WB_TIM2_BASE; |
| break; |
| #endif |
| |
| #ifdef CONFIG_STM32WB_TIM16 |
| case 16: |
| g_tickless.base = STM32WB_TIM16_BASE; |
| break; |
| #endif |
| |
| #ifdef CONFIG_STM32WB_TIM17 |
| case 17: |
| g_tickless.base = STM32WB_TIM17_BASE; |
| break; |
| #endif |
| |
| default: |
| DEBUGPANIC(); |
| } |
| |
| /* Get the TC frequency that corresponds to the requested resolution */ |
| |
| g_tickless.frequency = USEC_PER_SEC / (uint32_t)CONFIG_USEC_PER_TICK; |
| g_tickless.timer = CONFIG_STM32WB_TICKLESS_TIMER; |
| g_tickless.channel = CONFIG_STM32WB_TICKLESS_CHANNEL; |
| g_tickless.pending = false; |
| g_tickless.period = 0; |
| g_tickless.overflow = 0; |
| |
| tmrinfo("timer=%d channel=%d frequency=%lu Hz\n", |
| g_tickless.timer, g_tickless.channel, g_tickless.frequency); |
| |
| g_tickless.tch = stm32wb_tim_init(g_tickless.timer); |
| if (!g_tickless.tch) |
| { |
| tmrerr("ERROR: Failed to allocate TIM%d\n", g_tickless.timer); |
| DEBUGPANIC(); |
| } |
| |
| STM32WB_TIM_SETCLOCK(g_tickless.tch, g_tickless.frequency); |
| |
| /* Set up to receive the callback when the counter overflow occurs */ |
| |
| STM32WB_TIM_SETISR(g_tickless.tch, stm32wb_tickless_handler, NULL, 0); |
| |
| /* Initialize interval to zero */ |
| |
| STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, 0); |
| |
| /* Setup compare channel for the interval timing */ |
| |
| stm32wb_tickless_setchannel(g_tickless.channel); |
| |
| /* Set timer period */ |
| |
| #ifdef HAVE_32BIT_TICKLESS |
| STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT32_MAX); |
| #ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP |
| g_oneshot_maxticks = UINT32_MAX; |
| #endif |
| #else |
| STM32WB_TIM_SETPERIOD(g_tickless.tch, UINT16_MAX); |
| #ifdef CONFIG_SCHED_TICKLESS_LIMIT_MAX_SLEEP |
| g_oneshot_maxticks = UINT16_MAX; |
| #endif |
| #endif |
| |
| /* Initialize the counter */ |
| |
| STM32WB_TIM_SETMODE(g_tickless.tch, STM32WB_TIM_MODE_UP); |
| |
| /* Start the timer */ |
| |
| STM32WB_TIM_ACKINT(g_tickless.tch, ~0); |
| STM32WB_TIM_ENABLEINT(g_tickless.tch, GTIM_DIER_UIE); |
| } |
| |
| /**************************************************************************** |
| * Name: up_timer_gettime |
| * |
| * Description: |
| * Return the elapsed time since power-up (or, more correctly, since |
| * up_timer_initialize() was called). This function is functionally |
| * equivalent to: |
| * |
| * int clock_gettime(clockid_t clockid, struct timespec *ts); |
| * |
| * when clockid is CLOCK_MONOTONIC. |
| * |
| * This function provides the basis for reporting the current time and |
| * also is used to eliminate error build-up from small errors in interval |
| * time calculations. |
| * |
| * Provided by platform-specific code and called from the RTOS base code. |
| * |
| * Input Parameters: |
| * ts - Provides the location in which to return the up-time. |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; a negated errno value is returned on |
| * any failure. |
| * |
| * Assumptions: |
| * Called from the normal tasking context. The implementation must |
| * provide whatever mutual exclusion is necessary for correct operation. |
| * This can include disabling interrupts in order to assure atomic register |
| * operations. |
| * |
| ****************************************************************************/ |
| |
| int up_timer_gettime(struct timespec *ts) |
| { |
| uint64_t usec; |
| uint32_t counter; |
| uint32_t verify; |
| uint32_t overflow; |
| uint32_t sec; |
| int pending; |
| irqstate_t flags; |
| |
| DEBUGASSERT(g_tickless.tch && ts); |
| |
| /* Temporarily disable the overflow counter. NOTE that we have to be |
| * careful here because stm32wb_tc_getpending() will reset the pending |
| * interrupt status. If we do not handle the overflow here then, it will |
| * be lost. |
| */ |
| |
| flags = enter_critical_section(); |
| |
| overflow = g_tickless.overflow; |
| counter = STM32WB_TIM_GETCOUNTER(g_tickless.tch); |
| pending = STM32WB_TIM_CHECKINT(g_tickless.tch, GTIM_SR_UIF); |
| verify = STM32WB_TIM_GETCOUNTER(g_tickless.tch); |
| |
| /* If an interrupt was pending before we re-enabled interrupts, |
| * then the overflow needs to be incremented. |
| */ |
| |
| if (pending) |
| { |
| STM32WB_TIM_ACKINT(g_tickless.tch, GTIM_SR_UIF); |
| |
| /* Increment the overflow count and use the value of the |
| * guaranteed to be AFTER the overflow occurred. |
| */ |
| |
| overflow++; |
| counter = verify; |
| |
| /* Update tickless overflow counter. */ |
| |
| g_tickless.overflow = overflow; |
| } |
| |
| leave_critical_section(flags); |
| |
| tmrinfo("counter=%lu (%lu) overflow=%lu, pending=%i\n", |
| (unsigned long)counter, (unsigned long)verify, |
| (unsigned long)overflow, pending); |
| tmrinfo("frequency=%lu\n", g_tickless.frequency); |
| |
| /* Convert the whole thing to units of microseconds. |
| * |
| * frequency = ticks / second |
| * seconds = ticks * frequency |
| * usecs = (ticks * USEC_PER_SEC) / frequency; |
| */ |
| #ifdef HAVE_32BIT_TICKLESS |
| usec = ((((uint64_t)overflow << 32) + (uint64_t)counter) * USEC_PER_SEC) / |
| g_tickless.frequency; |
| #else |
| usec = ((((uint64_t)overflow << 16) + (uint64_t)counter) * USEC_PER_SEC) / |
| g_tickless.frequency; |
| #endif |
| |
| /* And return the value of the timer */ |
| |
| sec = (uint32_t)(usec / USEC_PER_SEC); |
| ts->tv_sec = sec; |
| ts->tv_nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; |
| |
| tmrinfo("usec=%llu ts=(%lu, %lu)\n", |
| usec, (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); |
| |
| return OK; |
| } |
| |
| #ifdef CONFIG_CLOCK_TIMEKEEPING |
| |
| /**************************************************************************** |
| * Name: up_timer_gettick |
| * |
| * Description: |
| * To be provided |
| * |
| * Input Parameters: |
| * cycles - 64-bit return value |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| int up_timer_gettick(clock_t *ticks) |
| { |
| *ticks = (clock_t)STM32WB_TIM_GETCOUNTER(g_tickless.tch); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: up_timer_getmask |
| * |
| * Description: |
| * To be provided |
| * |
| * Input Parameters: |
| * mask - Location to return the 64-bit mask |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void up_timer_getmask(clock_t *mask) |
| { |
| DEBUGASSERT(mask != NULL); |
| #ifdef HAVE_32BIT_TICKLESS |
| *mask = UINT32_MAX; |
| #else |
| *mask = UINT16_MAX; |
| #endif |
| } |
| |
| #endif /* CONFIG_CLOCK_TIMEKEEPING */ |
| |
| /**************************************************************************** |
| * Name: up_timer_cancel |
| * |
| * Description: |
| * Cancel the interval timer and return the time remaining on the timer. |
| * These two steps need to be as nearly atomic as possible. |
| * nxsched_timer_expiration() will not be called unless the timer is |
| * restarted with up_timer_start(). |
| * |
| * If, as a race condition, the timer has already expired when this |
| * function is called, then that pending interrupt must be cleared so |
| * that up_timer_start() and the remaining time of zero should be |
| * returned. |
| * |
| * NOTE: This function may execute at a high rate with no timer running (as |
| * when pre-emption is enabled and disabled). |
| * |
| * Provided by platform-specific code and called from the RTOS base code. |
| * |
| * Input Parameters: |
| * ts - Location to return the remaining time. Zero should be returned |
| * if the timer is not active. ts may be zero in which case the |
| * time remaining is not returned. |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success. A call to up_timer_cancel() when |
| * the timer is not active should also return success; a negated errno |
| * value is returned on any failure. |
| * |
| * Assumptions: |
| * May be called from interrupt level handling or from the normal tasking |
| * level. Interrupts may need to be disabled internally to assure |
| * non-reentrancy. |
| * |
| ****************************************************************************/ |
| |
| int up_timer_cancel(struct timespec *ts) |
| { |
| irqstate_t flags; |
| uint64_t usec; |
| uint64_t sec; |
| uint64_t nsec; |
| uint32_t count; |
| uint32_t period; |
| |
| /* Was the timer running? */ |
| |
| flags = enter_critical_section(); |
| if (!g_tickless.pending) |
| { |
| /* No.. Just return zero timer remaining and successful cancellation. |
| * This function may execute at a high rate with no timer running |
| * (as when pre-emption is enabled and disabled). |
| */ |
| |
| if (ts) |
| { |
| ts->tv_sec = 0; |
| ts->tv_nsec = 0; |
| } |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /* Yes.. Get the timer counter and period registers and disable the compare |
| * interrupt. |
| */ |
| |
| tmrinfo("Cancelling...\n"); |
| |
| /* Disable the interrupt. */ |
| |
| stm32wb_tickless_disableint(g_tickless.channel); |
| |
| count = STM32WB_TIM_GETCOUNTER(g_tickless.tch); |
| period = g_tickless.period; |
| |
| g_tickless.pending = false; |
| leave_critical_section(flags); |
| |
| /* Did the caller provide us with a location to return the time |
| * remaining? |
| */ |
| |
| if (ts != NULL) |
| { |
| /* Yes.. then calculate and return the time remaining on the |
| * oneshot timer. |
| */ |
| |
| tmrinfo("period=%lu count=%lu\n", |
| (unsigned long)period, (unsigned long)count); |
| |
| #ifndef HAVE_32BIT_TICKLESS |
| if (count > period) |
| { |
| /* Handle rollover */ |
| |
| period += UINT16_MAX; |
| } |
| else if (count == period) |
| #else |
| if (count >= period) |
| #endif |
| { |
| /* No time remaining */ |
| |
| ts->tv_sec = 0; |
| ts->tv_nsec = 0; |
| return OK; |
| } |
| |
| /* The total time remaining is the difference. Convert that |
| * to units of microseconds. |
| * |
| * frequency = ticks / second |
| * seconds = ticks * frequency |
| * usecs = (ticks * USEC_PER_SEC) / frequency; |
| */ |
| |
| usec = (((uint64_t)(period - count)) * USEC_PER_SEC) / |
| g_tickless.frequency; |
| |
| /* Return the time remaining in the correct form */ |
| |
| sec = usec / USEC_PER_SEC; |
| nsec = (usec - (sec * USEC_PER_SEC)) * NSEC_PER_USEC; |
| |
| ts->tv_sec = (time_t)sec; |
| ts->tv_nsec = (unsigned long)nsec; |
| |
| tmrinfo("remaining (%lu, %lu)\n", |
| (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: up_timer_start |
| * |
| * Description: |
| * Start the interval timer. nxsched_timer_expiration() will be |
| * called at the completion of the timeout (unless up_timer_cancel |
| * is called to stop the timing. |
| * |
| * Provided by platform-specific code and called from the RTOS base code. |
| * |
| * Input Parameters: |
| * ts - Provides the time interval until nxsched_timer_expiration() is |
| * called. |
| * |
| * Returned Value: |
| * Zero (OK) is returned on success; a negated errno value is returned on |
| * any failure. |
| * |
| * Assumptions: |
| * May be called from interrupt level handling or from the normal tasking |
| * level. Interrupts may need to be disabled internally to assure |
| * non-reentrancy. |
| * |
| ****************************************************************************/ |
| |
| int up_timer_start(const struct timespec *ts) |
| { |
| uint64_t usec; |
| uint64_t period; |
| uint32_t count; |
| irqstate_t flags; |
| |
| tmrinfo("ts=(%lu, %lu)\n", |
| (unsigned long)ts->tv_sec, (unsigned long)ts->tv_nsec); |
| DEBUGASSERT(ts); |
| DEBUGASSERT(g_tickless.tch); |
| |
| /* Was an interval already running? */ |
| |
| flags = enter_critical_section(); |
| if (g_tickless.pending) |
| { |
| /* Yes.. then cancel it */ |
| |
| tmrinfo("Already running... cancelling\n"); |
| up_timer_cancel(NULL); |
| } |
| |
| /* Express the delay in microseconds */ |
| |
| usec = (uint64_t)ts->tv_sec * USEC_PER_SEC + |
| (uint64_t)(ts->tv_nsec / NSEC_PER_USEC); |
| |
| /* Get the timer counter frequency and determine the number of counts need |
| * to achieve the requested delay. |
| * |
| * frequency = ticks / second |
| * ticks = seconds * frequency |
| * = (usecs * frequency) / USEC_PER_SEC; |
| */ |
| |
| period = (usec * (uint64_t)g_tickless.frequency) / USEC_PER_SEC; |
| count = STM32WB_TIM_GETCOUNTER(g_tickless.tch); |
| |
| tmrinfo("usec=%llu period=%08llx\n", usec, period); |
| |
| /* Set interval compare value. Rollover is fine, |
| * channel will trigger on the next period. |
| */ |
| #ifdef HAVE_32BIT_TICKLESS |
| DEBUGASSERT(period <= UINT32_MAX); |
| g_tickless.period = (uint32_t)(period + count); |
| #else |
| DEBUGASSERT(period <= UINT16_MAX); |
| g_tickless.period = (uint16_t)(period + count); |
| #endif |
| |
| STM32WB_TIM_SETCOMPARE(g_tickless.tch, g_tickless.channel, |
| g_tickless.period); |
| |
| /* Enable interrupts. We should get the callback when the interrupt |
| * occurs. |
| */ |
| |
| stm32wb_tickless_ackint(g_tickless.channel); |
| stm32wb_tickless_enableint(g_tickless.channel); |
| |
| g_tickless.pending = true; |
| leave_critical_section(flags); |
| return OK; |
| } |
| #endif /* CONFIG_SCHED_TICKLESS */ |