diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:08:59 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:12:21 +0200 |
commit | 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a (patch) | |
tree | ab49cc16ed0b853452c5c2ed2d3042416d628986 /thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer | |
download | iot-sensors-master.tar.gz iot-sensors-master.tar.bz2 iot-sensors-master.tar.xz iot-sensors-master.zip |
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer')
7 files changed, 3109 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.c new file mode 100644 index 0000000..0df49b7 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.c @@ -0,0 +1,1075 @@ +/** + * Copyright (c) 2012 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(APP_TIMER) +#include "app_timer.h" +#include <stdlib.h> +#include "nrf.h" +#include "nrf_peripherals.h" +#include "nrf_soc.h" +#include "app_error.h" +#include "nrf_delay.h" +#include "app_util_platform.h" +#if APP_TIMER_CONFIG_USE_SCHEDULER +#include "app_scheduler.h" +#endif + +#define RTC1_IRQ_PRI APP_TIMER_CONFIG_IRQ_PRIORITY /**< Priority of the RTC1 interrupt (used for checking for timeouts and executing timeout handlers). */ +#define SWI_IRQ_PRI APP_TIMER_CONFIG_IRQ_PRIORITY /**< Priority of the SWI interrupt (used for updating the timer list). */ + +// The current design assumes that both interrupt handlers run at the same interrupt level. +// If this is to be changed, protection must be added to prevent them from interrupting each other +// (e.g. by using guard/trigger flags). +STATIC_ASSERT(RTC1_IRQ_PRI == SWI_IRQ_PRI); + +#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */ + +#define RTC_COMPARE_OFFSET_MIN 3 /**< Minimum offset between the current RTC counter value and the Capture Compare register. Although the nRF51 Series User Specification recommends this value to be 2, we use 3 to be safer.*/ + +#define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */ + +#ifdef EGU_PRESENT +#define SWI_PART(_id) CONCAT_2(SWI,_id) +#define EGU_PART(_id) CONCAT_2(_EGU,_id) +#define SWI_IRQ_n(_id) CONCAT_3(SWI_PART(_id), EGU_PART(_id),_IRQn) +#define SWI_IRQ_Handler_n(_id) CONCAT_3(SWI_PART(_id), EGU_PART(_id),_IRQHandler) +#else //EGU_PRESENT +#define SWI_IRQ_n(_id) CONCAT_3(SWI,_id,_IRQn) +#define SWI_IRQ_Handler_n(_id) CONCAT_3(SWI,_id,_IRQHandler) +#endif + +#define SWI_IRQn SWI_IRQ_n(APP_TIMER_CONFIG_SWI_NUMBER) +#define SWI_IRQHandler SWI_IRQ_Handler_n(APP_TIMER_CONFIG_SWI_NUMBER) + + +#define MODULE_INITIALIZED (m_op_queue.size != 0) /**< Macro designating whether the module has been initialized properly. */ + +/**@brief Timer node type. The nodes will be used form a linked list of running timers. */ +typedef struct +{ + uint32_t ticks_to_expire; /**< Number of ticks from previous timer interrupt to timer expiry. */ + uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */ + uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */ + uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */ + bool is_running; /**< True if timer is running, False otherwise. */ + app_timer_mode_t mode; /**< Timer mode. */ + app_timer_timeout_handler_t p_timeout_handler; /**< Pointer to function to be executed when the timer expires. */ + void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */ + void * next; /**< Pointer to the next node. */ +} timer_node_t; + +STATIC_ASSERT(sizeof(timer_node_t) == APP_TIMER_NODE_SIZE); + +/**@brief Set of available timer operation types. */ +typedef enum +{ + TIMER_USER_OP_TYPE_NONE, /**< Invalid timer operation type. */ + TIMER_USER_OP_TYPE_START, /**< Timer operation type Start. */ + TIMER_USER_OP_TYPE_STOP, /**< Timer operation type Stop. */ + TIMER_USER_OP_TYPE_STOP_ALL /**< Timer operation type Stop All. */ +} timer_user_op_type_t; + +/**@brief Structure describing a timer start operation. */ +typedef struct +{ + uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */ + uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */ + uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */ + void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */ +} timer_user_op_start_t; + +/**@brief Structure describing a timer operation. */ +typedef struct +{ + timer_user_op_type_t op_type; /**< Id of timer on which the operation is to be performed. */ + timer_node_t * p_node; + union + { + timer_user_op_start_t start; /**< Structure describing a timer start operation. */ + } params; +} timer_user_op_t; + +/**@brief Structure describing a timer operations queue. + * + * @details This queue will hold timer operations issued by the application + * until the timer interrupt handler processes these operations. + */ +typedef struct +{ + uint8_t first; /**< Index of first entry to have been inserted in the queue (i.e. the next entry to be executed). */ + uint8_t last; /**< Index of last entry to have been inserted in the queue. */ + uint8_t size; /**< Queue size. */ + timer_user_op_t user_op_queue[APP_TIMER_CONFIG_OP_QUEUE_SIZE+1]; /**< Queue buffer. */ +} timer_op_queue_t; + +STATIC_ASSERT(sizeof(timer_op_queue_t) % 4 == 0); + +#define CONTEXT_QUEUE_SIZE_MAX (2) + +static timer_op_queue_t m_op_queue; /**< Timer operations queue. */ +static timer_node_t * mp_timer_id_head; /**< First timer in list of running timers. */ +static uint32_t m_ticks_latest; /**< Last known RTC counter value. */ +static uint32_t m_ticks_elapsed[CONTEXT_QUEUE_SIZE_MAX]; /**< Timer internal elapsed ticks queue. */ +static uint8_t m_ticks_elapsed_q_read_ind; /**< Timer internal elapsed ticks queue read index. */ +static uint8_t m_ticks_elapsed_q_write_ind; /**< Timer internal elapsed ticks queue write index. */ +static bool m_rtc1_running; /**< Boolean indicating if RTC1 is running. */ +static bool m_rtc1_reset; /**< Boolean indicating if RTC1 counter has been reset due to last timer removed from timer list during the timer list handling. */ + +#if APP_TIMER_WITH_PROFILER +static uint8_t m_max_user_op_queue_utilization; /**< Maximum observed timer user operations queue utilization. */ +#endif + +/**@brief Function for initializing the RTC1 counter. + * + * @param[in] prescaler Value of the RTC1 PRESCALER register. Set to 0 for no prescaling. + */ +static void rtc1_init(uint32_t prescaler) +{ + NRF_RTC1->PRESCALER = prescaler; + NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI); +} + + +/**@brief Function for starting the RTC1 timer. + */ +static void rtc1_start(void) +{ + NRF_RTC1->EVTENSET = RTC_EVTEN_COMPARE0_Msk; + NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk; + + NVIC_ClearPendingIRQ(RTC1_IRQn); + NVIC_EnableIRQ(RTC1_IRQn); + + NRF_RTC1->TASKS_START = 1; + nrf_delay_us(MAX_RTC_TASKS_DELAY); + + m_rtc1_running = true; +} + + +/**@brief Function for stopping the RTC1 timer. + */ +static void rtc1_stop(void) +{ + NVIC_DisableIRQ(RTC1_IRQn); + + NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk; + NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk; + + NRF_RTC1->TASKS_STOP = 1; + nrf_delay_us(MAX_RTC_TASKS_DELAY); + + NRF_RTC1->TASKS_CLEAR = 1; + m_ticks_latest = 0; + nrf_delay_us(MAX_RTC_TASKS_DELAY); + + m_rtc1_running = false; +} + + +/**@brief Function for returning the current value of the RTC1 counter. + * + * @return Current value of the RTC1 counter. + */ +static __INLINE uint32_t rtc1_counter_get(void) +{ + return NRF_RTC1->COUNTER; +} + + +/**@brief Function for computing the difference between two RTC1 counter values. + * + * @return Number of ticks elapsed from ticks_old to ticks_now. + */ +static __INLINE uint32_t ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old) +{ + return ((ticks_now - ticks_old) & MAX_RTC_COUNTER_VAL); +} + + +/**@brief Function for setting the RTC1 Capture Compare register 0, and enabling the corresponding + * event. + * + * @param[in] value New value of Capture Compare register 0. + */ +static __INLINE void rtc1_compare0_set(uint32_t value) +{ + NRF_RTC1->CC[0] = value; +} + + +/**@brief Function for inserting a timer in the timer list. + * + * @param[in] timer_id Id of timer to insert. + */ +static void timer_list_insert(timer_node_t * p_timer) +{ + if (mp_timer_id_head == NULL) + { + mp_timer_id_head = p_timer; + } + else + { + if (p_timer->ticks_to_expire <= mp_timer_id_head->ticks_to_expire) + { + mp_timer_id_head->ticks_to_expire -= p_timer->ticks_to_expire; + + p_timer->next = mp_timer_id_head; + mp_timer_id_head = p_timer; + } + else + { + timer_node_t * p_previous; + timer_node_t * p_current; + uint32_t ticks_to_expire; + + ticks_to_expire = p_timer->ticks_to_expire; + p_previous = mp_timer_id_head; + p_current = mp_timer_id_head; + + while ((p_current != NULL) && (ticks_to_expire > p_current->ticks_to_expire)) + { + ticks_to_expire -= p_current->ticks_to_expire; + p_previous = p_current; + p_current = p_current->next; + } + + if (p_current != NULL) + { + p_current->ticks_to_expire -= ticks_to_expire; + } + + p_timer->ticks_to_expire = ticks_to_expire; + p_timer->next = p_current; + p_previous->next = p_timer; + } + } +} + + +/**@brief Function for removing a timer from the timer queue. + * + * @param[in] timer_id Id of timer to remove. + * + * @return TRUE if Capture Compare register must be updated, FALSE otherwise. + */ +static bool timer_list_remove(timer_node_t * p_timer) +{ + timer_node_t * p_old_head; + timer_node_t * p_previous; + timer_node_t * p_current; + uint32_t timeout; + + // Find the timer's position in timer list. + p_old_head = mp_timer_id_head; + p_previous = mp_timer_id_head; + p_current = p_previous; + + while (p_current != NULL) + { + if (p_current == p_timer) + { + break; + } + p_previous = p_current; + p_current = p_current->next; + } + + // Timer not in active list. + if (p_current == NULL) + { + return false; + } + + // Timer is the first in the list + if (p_previous == p_current) + { + mp_timer_id_head = mp_timer_id_head->next; + + // No more timers in the list. Reset RTC1 in case Start timer operations are present in the queue. + if (mp_timer_id_head == NULL) + { + NRF_RTC1->TASKS_CLEAR = 1; + m_ticks_latest = 0; + m_rtc1_reset = true; + nrf_delay_us(MAX_RTC_TASKS_DELAY); + } + } + + // Remaining timeout between next timeout. + timeout = p_current->ticks_to_expire; + + // Link previous timer with next of this timer, i.e. removing the timer from list. + p_previous->next = p_current->next; + + // If this is not the last timer, increment the next timer by this timer timeout. + p_current = p_previous->next; + if (p_current != NULL) + { + p_current->ticks_to_expire += timeout; + } + + return (p_old_head != mp_timer_id_head); +} + + +/**@brief Function for scheduling a check for timeouts by generating a RTC1 interrupt. + */ +static void timer_timeouts_check_sched(void) +{ + NVIC_SetPendingIRQ(RTC1_IRQn); +} + + +/**@brief Function for scheduling a timer list update by generating a SWI interrupt. + */ +static void timer_list_handler_sched(void) +{ + NVIC_SetPendingIRQ(SWI_IRQn); +} + +#if APP_TIMER_CONFIG_USE_SCHEDULER +static void timeout_handler_scheduled_exec(void * p_event_data, uint16_t event_size) +{ + APP_ERROR_CHECK_BOOL(event_size == sizeof(app_timer_event_t)); + app_timer_event_t const * p_timer_event = (app_timer_event_t *)p_event_data; + + p_timer_event->timeout_handler(p_timer_event->p_context); +} +#endif + +/**@brief Function for executing an application timeout handler, either by calling it directly, or + * by passing an event to the @ref app_scheduler. + * + * @param[in] p_timer Pointer to expired timer. + */ +static void timeout_handler_exec(timer_node_t * p_timer) +{ +#if APP_TIMER_CONFIG_USE_SCHEDULER + app_timer_event_t timer_event; + + timer_event.timeout_handler = p_timer->p_timeout_handler; + timer_event.p_context = p_timer->p_context; + uint32_t err_code = app_sched_event_put(&timer_event, sizeof(timer_event), timeout_handler_scheduled_exec); + APP_ERROR_CHECK(err_code); +#else + p_timer->p_timeout_handler(p_timer->p_context); +#endif +} + + +/**@brief Function for checking for expired timers. + */ +static void timer_timeouts_check(void) +{ + // Handle expired of timer + if (mp_timer_id_head != NULL) + { + timer_node_t * p_timer; + timer_node_t * p_previous_timer; + uint32_t ticks_elapsed; + uint32_t ticks_expired; + + // Initialize actual elapsed ticks being consumed to 0. + ticks_expired = 0; + + // ticks_elapsed is collected here, job will use it. + ticks_elapsed = ticks_diff_get(rtc1_counter_get(), m_ticks_latest); + + // Auto variable containing the head of timers expiring. + p_timer = mp_timer_id_head; + + // Expire all timers within ticks_elapsed and collect ticks_expired. + while (p_timer != NULL) + { + // Do nothing if timer did not expire. + if (ticks_elapsed < p_timer->ticks_to_expire) + { + break; + } + + // Decrement ticks_elapsed and collect expired ticks. + ticks_elapsed -= p_timer->ticks_to_expire; + ticks_expired += p_timer->ticks_to_expire; + + // Move to next timer. + p_previous_timer = p_timer; + p_timer = p_timer->next; + + // Execute Task. + if (p_previous_timer->is_running) + { + p_previous_timer->is_running = false; + timeout_handler_exec(p_previous_timer); + } + } + + // Prepare to queue the ticks expired in the m_ticks_elapsed queue. + if (m_ticks_elapsed_q_read_ind == m_ticks_elapsed_q_write_ind) + { + // The read index of the queue is equal to the write index. This means the new + // value of ticks_expired should be stored at a new location in the m_ticks_elapsed + // queue (which is implemented as a double buffer). + + // Check if there will be a queue overflow. + if (++m_ticks_elapsed_q_write_ind == CONTEXT_QUEUE_SIZE_MAX) + { + // There will be a queue overflow. Hence the write index should point to the start + // of the queue. + m_ticks_elapsed_q_write_ind = 0; + } + } + + // Queue the ticks expired. + m_ticks_elapsed[m_ticks_elapsed_q_write_ind] = ticks_expired; + + timer_list_handler_sched(); + } +} + + +/**@brief Function for acquiring the number of ticks elapsed. + * + * @param[out] p_ticks_elapsed Number of ticks elapsed. + * + * @return TRUE if elapsed ticks was read from queue, FALSE otherwise. + */ +static bool elapsed_ticks_acquire(uint32_t * p_ticks_elapsed) +{ + // Pick the elapsed value from queue. + if (m_ticks_elapsed_q_read_ind != m_ticks_elapsed_q_write_ind) + { + // Dequeue elapsed value. + m_ticks_elapsed_q_read_ind++; + if (m_ticks_elapsed_q_read_ind == CONTEXT_QUEUE_SIZE_MAX) + { + m_ticks_elapsed_q_read_ind = 0; + } + + *p_ticks_elapsed = m_ticks_elapsed[m_ticks_elapsed_q_read_ind]; + + m_ticks_latest += *p_ticks_elapsed; + m_ticks_latest &= MAX_RTC_COUNTER_VAL; + + return true; + } + else + { + // No elapsed value in queue. + *p_ticks_elapsed = 0; + return false; + } +} + + +/**@brief Function for updating the timer list for expired timers. + * + * @param[in] ticks_elapsed Number of elapsed ticks. + * @param[in] ticks_previous Previous known value of the RTC counter. + * @param[out] p_restart_list_head List of repeating timers to be restarted. + */ +static void expired_timers_handler(uint32_t ticks_elapsed, + uint32_t ticks_previous, + timer_node_t ** p_restart_list_head) +{ + uint32_t ticks_expired = 0; + + while (mp_timer_id_head != NULL) + { + timer_node_t * p_timer; + timer_node_t * p_timer_expired; + + // Auto variable for current timer node. + p_timer = mp_timer_id_head; + + // Do nothing if timer did not expire + if (ticks_elapsed < p_timer->ticks_to_expire) + { + p_timer->ticks_to_expire -= ticks_elapsed; + break; + } + + // Decrement ticks_elapsed and collect expired ticks. + ticks_elapsed -= p_timer->ticks_to_expire; + ticks_expired += p_timer->ticks_to_expire; + + // Timer expired, set ticks_to_expire zero. + p_timer->ticks_to_expire = 0; + + // Remove the expired timer from head. + p_timer_expired = mp_timer_id_head; + mp_timer_id_head = p_timer->next; + + // Timer will be restarted if periodic. + if (p_timer->ticks_periodic_interval != 0) + { + p_timer->ticks_at_start = (ticks_previous + ticks_expired) & MAX_RTC_COUNTER_VAL; + p_timer->ticks_first_interval = p_timer->ticks_periodic_interval; + p_timer->next = *p_restart_list_head; + *p_restart_list_head = p_timer_expired; + } + } +} + + +/**@brief Function for handling timer list insertions. + * + * @param[in] p_restart_list_head List of repeating timers to be restarted. + * + * @return TRUE if Capture Compare register must be updated, FALSE otherwise. + */ +static bool list_insertions_handler(timer_node_t * p_restart_list_head) +{ + bool compare_update = false; + + timer_node_t * p_timer_id_old_head; + + // Remember the old head, so as to decide if new compare needs to be set. + p_timer_id_old_head = mp_timer_id_head; + + // Handle insertions of timers. + while ((p_restart_list_head != NULL) || (m_op_queue.first != m_op_queue.last)) + { + timer_node_t * p_timer; + + if (p_restart_list_head != NULL) + { + p_timer = p_restart_list_head; + p_restart_list_head = p_timer->next; + } + else + { + timer_user_op_t * p_user_op = &m_op_queue.user_op_queue[m_op_queue.first]; + + m_op_queue.first++; + if (m_op_queue.first == m_op_queue.size) + { + m_op_queue.first = 0; + } + + p_timer = p_user_op->p_node; + + switch (p_user_op->op_type) + { + case TIMER_USER_OP_TYPE_STOP: + // Delete node if timer is running. + if (timer_list_remove(p_user_op->p_node)) + { + compare_update = true; + } + + p_timer->is_running = false; + continue; + + case TIMER_USER_OP_TYPE_STOP_ALL: + // Delete list of running timers, and mark all timers as not running. + while (mp_timer_id_head != NULL) + { + timer_node_t * p_head = mp_timer_id_head; + + p_head->is_running = false; + mp_timer_id_head = p_head->next; + } + continue; + case TIMER_USER_OP_TYPE_START: + break; + default: + // No implementation needed. + continue; + } + + if (p_timer->is_running) + { + continue; + } + + p_timer->ticks_at_start = p_user_op->params.start.ticks_at_start; + p_timer->ticks_first_interval = p_user_op->params.start.ticks_first_interval; + p_timer->ticks_periodic_interval = p_user_op->params.start.ticks_periodic_interval; + p_timer->p_context = p_user_op->params.start.p_context; + + if (m_rtc1_reset) + { + p_timer->ticks_at_start = 0; + } + } + + // Prepare the node to be inserted. + if ( + ((p_timer->ticks_at_start - m_ticks_latest) & MAX_RTC_COUNTER_VAL) + < + (MAX_RTC_COUNTER_VAL / 2) + ) + { + p_timer->ticks_to_expire = ticks_diff_get(p_timer->ticks_at_start, m_ticks_latest) + + p_timer->ticks_first_interval; + } + else + { + uint32_t delta_current_start; + + delta_current_start = ticks_diff_get(m_ticks_latest, p_timer->ticks_at_start); + if (p_timer->ticks_first_interval > delta_current_start) + { + p_timer->ticks_to_expire = p_timer->ticks_first_interval - delta_current_start; + } + else + { + p_timer->ticks_to_expire = 0; + } + } + + p_timer->ticks_at_start = 0; + p_timer->ticks_first_interval = 0; + p_timer->is_running = true; + p_timer->next = NULL; + + // Insert into list + timer_list_insert(p_timer); + } + + return (compare_update || (mp_timer_id_head != p_timer_id_old_head)); +} + + +/**@brief Function for updating the Capture Compare register. + */ +static void compare_reg_update(timer_node_t * p_timer_id_head_old) +{ + // Setup the timeout for timers on the head of the list + if (mp_timer_id_head != NULL) + { + uint32_t ticks_to_expire = mp_timer_id_head->ticks_to_expire; + uint32_t pre_counter_val = rtc1_counter_get(); + uint32_t cc = m_ticks_latest; + uint32_t ticks_elapsed = ticks_diff_get(pre_counter_val, cc) + RTC_COMPARE_OFFSET_MIN; + + if (!m_rtc1_running) + { + // No timers were already running, start RTC + rtc1_start(); + } + + cc += (ticks_elapsed < ticks_to_expire) ? ticks_to_expire : ticks_elapsed; + cc &= MAX_RTC_COUNTER_VAL; + + rtc1_compare0_set(cc); + + uint32_t post_counter_val = rtc1_counter_get(); + + if ( + (ticks_diff_get(post_counter_val, pre_counter_val) + RTC_COMPARE_OFFSET_MIN) + > + ticks_diff_get(cc, pre_counter_val) + ) + { + // When this happens the COMPARE event may not be triggered by the RTC. + // The nRF51 Series User Specification states that if the COUNTER value is N + // (i.e post_counter_val = N), writing N or N + 1 to a CC register may not trigger a + // COMPARE event. Hence the RTC interrupt is forcefully pended by calling the following + // function. + rtc1_compare0_set(rtc1_counter_get()); // this should prevent CC to fire again in the background while the code is in RTC-ISR + nrf_delay_us(MAX_RTC_TASKS_DELAY); + timer_timeouts_check_sched(); + } + } + else + { +#if (APP_TIMER_KEEPS_RTC_ACTIVE == 0) + // No timers are running, stop RTC + rtc1_stop(); +#endif //(APP_TIMER_KEEPS_RTC_ACTIVE == 0) + } +} + + +/**@brief Function for handling changes to the timer list. + */ +static void timer_list_handler(void) +{ + timer_node_t * p_restart_list_head = NULL; + + uint32_t ticks_elapsed; + uint32_t ticks_previous; + bool ticks_have_elapsed; + bool compare_update = false; + timer_node_t * p_timer_id_head_old; + +#if APP_TIMER_WITH_PROFILER + { + uint8_t size = m_op_queue.size; + uint8_t first = m_op_queue.first; + uint8_t last = m_op_queue.last; + uint8_t utilization = (first <= last) ? (last - first) : (size + 1 - first + last); + + if (utilization > m_max_user_op_queue_utilization) + { + m_max_user_op_queue_utilization = utilization; + } + } +#endif + + // Back up the previous known tick and previous list head + ticks_previous = m_ticks_latest; + p_timer_id_head_old = mp_timer_id_head; + + // Get number of elapsed ticks + ticks_have_elapsed = elapsed_ticks_acquire(&ticks_elapsed); + + // Handle expired timers + if (ticks_have_elapsed) + { + expired_timers_handler(ticks_elapsed, ticks_previous, &p_restart_list_head); + compare_update = true; + } + + + // Handle list insertions + if (list_insertions_handler(p_restart_list_head)) + { + compare_update = true; + } + + // Update compare register if necessary + if (compare_update) + { + compare_reg_update(p_timer_id_head_old); + } + m_rtc1_reset = false; +} + + +/**@brief Function for enqueueing a new operations queue entry. + * + * @param[in] last_index Index of the next last index to be enqueued. + */ +static void user_op_enque(uint8_t last_index) +{ + m_op_queue.last = last_index; +} + + +/**@brief Function for allocating a new operations queue entry. + * + * @param[out] p_last_index Index of the next last index to be enqueued. + * + * @return Pointer to allocated queue entry, or NULL if queue is full. + */ +static timer_user_op_t * user_op_alloc( uint8_t * p_last_index) +{ + uint8_t last; + timer_user_op_t * p_user_op; + + last = m_op_queue.last + 1; + if (last == m_op_queue.size) + { + // Overflow case. + last = 0; + } + if (last == m_op_queue.first) + { + // Queue is full. + return NULL; + } + + *p_last_index = last; + p_user_op = &m_op_queue.user_op_queue[m_op_queue.last]; + + return p_user_op; +} + + +/**@brief Function for scheduling a Timer Start operation. + * + * @param[in] timer_id Id of timer to start. + * @param[in] timeout_initial Time (in ticks) to first timer expiry. + * @param[in] timeout_periodic Time (in ticks) between periodic expiries. + * @param[in] p_context General purpose pointer. Will be passed to the timeout handler when + * the timer expires. + * @return NRF_SUCCESS on success, otherwise an error code. + */ + +static uint32_t timer_start_op_schedule(timer_node_t * p_node, + uint32_t timeout_initial, + uint32_t timeout_periodic, + void * p_context) +{ + uint8_t last_index; + uint32_t err_code = NRF_SUCCESS; + + CRITICAL_REGION_ENTER(); + timer_user_op_t * p_user_op = user_op_alloc(&last_index); + if (p_user_op == NULL) + { + err_code = NRF_ERROR_NO_MEM; + } + else + { + p_user_op->op_type = TIMER_USER_OP_TYPE_START; + p_user_op->p_node = p_node; + p_user_op->params.start.ticks_at_start = rtc1_counter_get(); + p_user_op->params.start.ticks_first_interval = timeout_initial; + p_user_op->params.start.ticks_periodic_interval = timeout_periodic; + p_user_op->params.start.p_context = p_context; + + user_op_enque(last_index); + } + CRITICAL_REGION_EXIT(); + + if (err_code == NRF_SUCCESS) + { + timer_list_handler_sched(); + } + + return err_code; +} + + +/**@brief Function for scheduling a Timer Stop operation. + * + * @param[in] timer_id Id of timer to stop. + * @param[in] op_type Type of stop operation + * + * @return NRF_SUCCESS on successful scheduling a timer stop operation. NRF_ERROR_NO_MEM when there + * is no memory left to schedule the timer stop operation. + */ +static uint32_t timer_stop_op_schedule(timer_node_t * p_node, + timer_user_op_type_t op_type) +{ + uint8_t last_index; + uint32_t err_code = NRF_SUCCESS; + + CRITICAL_REGION_ENTER(); + timer_user_op_t * p_user_op = user_op_alloc(&last_index); + if (p_user_op == NULL) + { + err_code = NRF_ERROR_NO_MEM; + } + else + { + p_user_op->op_type = op_type; + p_user_op->p_node = p_node; + + user_op_enque(last_index); + } + CRITICAL_REGION_EXIT(); + + if (err_code == NRF_SUCCESS) + { + timer_list_handler_sched(); + } + + return err_code; +} + +/**@brief Function for handling the RTC1 interrupt. + * + * @details Checks for timeouts, and executes timeout handlers for expired timers. + */ +void RTC1_IRQHandler(void) +{ + // Clear all events (also unexpected ones) + NRF_RTC1->EVENTS_COMPARE[0] = 0; + NRF_RTC1->EVENTS_COMPARE[1] = 0; + NRF_RTC1->EVENTS_COMPARE[2] = 0; + NRF_RTC1->EVENTS_COMPARE[3] = 0; + NRF_RTC1->EVENTS_TICK = 0; + NRF_RTC1->EVENTS_OVRFLW = 0; + + // Check for expired timers + timer_timeouts_check(); +} + + +/**@brief Function for handling the SWI interrupt. + * + * @details Performs all updates to the timer list. + */ +void SWI_IRQHandler(void) +{ + timer_list_handler(); +} + + +ret_code_t app_timer_init(void) +{ + // Stop RTC to prevent any running timers from expiring (in case of reinitialization) + rtc1_stop(); + + // Initialize operation queue + m_op_queue.first = 0; + m_op_queue.last = 0; + m_op_queue.size = APP_TIMER_CONFIG_OP_QUEUE_SIZE+1; + + mp_timer_id_head = NULL; + m_ticks_elapsed_q_read_ind = 0; + m_ticks_elapsed_q_write_ind = 0; + +#if APP_TIMER_WITH_PROFILER + m_max_user_op_queue_utilization = 0; +#endif + + NVIC_ClearPendingIRQ(SWI_IRQn); + NVIC_SetPriority(SWI_IRQn, SWI_IRQ_PRI); + NVIC_EnableIRQ(SWI_IRQn); + + rtc1_init(APP_TIMER_CONFIG_RTC_FREQUENCY); + + m_ticks_latest = rtc1_counter_get(); + + return NRF_SUCCESS; +} + + +ret_code_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler) +{ + // Check state and parameters + VERIFY_MODULE_INITIALIZED(); + + if (timeout_handler == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + if (p_timer_id == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + if (((timer_node_t*)*p_timer_id)->is_running) + { + return NRF_ERROR_INVALID_STATE; + } + + timer_node_t * p_node = (timer_node_t *)*p_timer_id; + p_node->is_running = false; + p_node->mode = mode; + p_node->p_timeout_handler = timeout_handler; + return NRF_SUCCESS; +} + +ret_code_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) +{ + uint32_t timeout_periodic; + timer_node_t * p_node = (timer_node_t*)timer_id; + + // Check state and parameters + VERIFY_MODULE_INITIALIZED(); + + if (timer_id == 0) + { + return NRF_ERROR_INVALID_STATE; + } + if (timeout_ticks < APP_TIMER_MIN_TIMEOUT_TICKS) + { + return NRF_ERROR_INVALID_PARAM; + } + if (p_node->p_timeout_handler == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + + // Schedule timer start operation + timeout_periodic = (p_node->mode == APP_TIMER_MODE_REPEATED) ? timeout_ticks : 0; + + return timer_start_op_schedule(p_node, + timeout_ticks, + timeout_periodic, + p_context); +} + + +ret_code_t app_timer_stop(app_timer_id_t timer_id) +{ + timer_node_t * p_node = (timer_node_t*)timer_id; + // Check state and parameters + VERIFY_MODULE_INITIALIZED(); + + if ((timer_id == NULL) || (p_node->p_timeout_handler == NULL)) + { + return NRF_ERROR_INVALID_STATE; + } + + p_node->is_running = false; + + // Schedule timer stop operation + return timer_stop_op_schedule(p_node, TIMER_USER_OP_TYPE_STOP); +} + + +ret_code_t app_timer_stop_all(void) +{ + // Check state + VERIFY_MODULE_INITIALIZED(); + + return timer_stop_op_schedule(NULL, TIMER_USER_OP_TYPE_STOP_ALL); +} + + +uint32_t app_timer_cnt_get(void) +{ + return rtc1_counter_get(); +} + + +uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, + uint32_t ticks_from) +{ + return ticks_diff_get(ticks_to, ticks_from); +} + +#if APP_TIMER_WITH_PROFILER +uint8_t app_timer_op_queue_utilization_get(void) +{ + return m_max_user_op_queue_utilization; +} +#endif + +void app_timer_pause(void) +{ + NRF_RTC1->TASKS_STOP = 1; +} + +void app_timer_resume(void) +{ + NRF_RTC1->TASKS_START = 1; +} + +#endif //NRF_MODULE_ENABLED(APP_TIMER) diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.h new file mode 100644 index 0000000..64540a9 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer.h @@ -0,0 +1,313 @@ +/** + * Copyright (c) 2012 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/** @file + * + * @defgroup app_timer Application Timer + * @{ + * @ingroup app_common + * + * @brief Application timer functionality. + * + * @details This module enables the application to create multiple timer instances based on the RTC1 + * peripheral. Checking for time-outs and invocation of user time-out handlers is performed + * in the RTC1 interrupt handler. List handling is done using a software interrupt (SWI0). + * Both interrupt handlers are running in APP_LOW priority level. + * + * @details When calling app_timer_start() or app_timer_stop(), the timer operation is just queued, + * and the software interrupt is triggered. The actual timer start/stop operation is + * executed by the SWI0 interrupt handler. Since the SWI0 interrupt is running in APP_LOW, + * if the application code calling the timer function is running in APP_LOW or APP_HIGH, + * the timer operation will not be performed until the application handler has returned. + * This will be the case, for example, when stopping a timer from a time-out handler when not using + * the scheduler. + * + * @details Use the USE_SCHEDULER parameter of the APP_TIMER_INIT() macro to select if the + * @ref app_scheduler should be used or not. Even if the scheduler is + * not used, app_timer.h will include app_scheduler.h, so when + * compiling, app_scheduler.h must be available in one of the compiler include paths. + */ + +#ifndef APP_TIMER_H__ +#define APP_TIMER_H__ +#include "sdk_config.h" +#include "app_error.h" +#include "app_util.h" +#include "compiler_abstraction.h" +#include "nordic_common.h" +#ifdef APP_TIMER_V2 +#include "nrf_log_instance.h" +#include "nrf_sortlist.h" +#endif +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Name of the module used for logger messaging. + */ +#define APP_TIMER_LOG_NAME app_timer + +#define APP_TIMER_CLOCK_FREQ 32768 /**< Clock frequency of the RTC timer used to implement the app timer module. */ +#define APP_TIMER_MIN_TIMEOUT_TICKS 5 /**< Minimum value of the timeout_ticks parameter of app_timer_start(). */ + +#ifdef RTX +#define APP_TIMER_NODE_SIZE 40 /**< Size of app_timer.timer_node_t (used to allocate data). */ +#else +#define APP_TIMER_NODE_SIZE 32 /**< Size of app_timer.timer_node_t (used to allocate data). */ +#endif // RTX + +#define APP_TIMER_SCHED_EVENT_DATA_SIZE sizeof(app_timer_event_t) /**< Size of event data when scheduler is used. */ + +#define APP_TIMER_MAX_CNT_VAL RTC_COUNTER_COUNTER_Msk /**< Maximum counter value that can be returned by @ref app_timer_cnt_get. */ + +/**@brief Convert milliseconds to timer ticks. + * + * This macro uses 64-bit integer arithmetic, but as long as the macro parameters are + * constants (i.e. defines), the computation will be done by the preprocessor. + * + * @param[in] MS Milliseconds. + * + * @return Number of timer ticks. + */ +#ifndef FREERTOS +#define APP_TIMER_TICKS(MS) \ + ((uint32_t)ROUNDED_DIV( \ + (MS) * (uint64_t)APP_TIMER_CLOCK_FREQ, \ + 1000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1))) +#else +#include "FreeRTOSConfig.h" +#define APP_TIMER_TICKS(MS) (uint32_t)ROUNDED_DIV((MS)*configTICK_RATE_HZ,1000) +#endif + + +/** + * @brief Create a timer identifier and statically allocate memory for the timer. + * + * @param timer_id Name of the timer identifier variable that will be used to control the timer. + */ +#define APP_TIMER_DEF(timer_id) _APP_TIMER_DEF(timer_id) + +/**@brief Application time-out handler type. */ +typedef void (*app_timer_timeout_handler_t)(void * p_context); + +#ifdef APP_TIMER_V2 +/** + * @brief app_timer control block + */ +typedef struct +{ + nrf_sortlist_item_t list_item; /**< Token used by sortlist. */ + volatile uint32_t end_val; /**< RTC counter value when timer expires. */ + uint32_t repeat_period; /**< Repeat period (0 if single shot mode). */ + app_timer_timeout_handler_t handler; /**< User handler. */ + void * p_context; /**< User context. */ + NRF_LOG_INSTANCE_PTR_DECLARE(p_log) /**< Pointer to instance of the logger object (Conditionally compiled). */ + volatile bool active; /**< Flag indicating that timer is active. */ +} app_timer_t; + +/**@brief Timer ID type. + * Never declare a variable of this type, but use the macro @ref APP_TIMER_DEF instead.*/ +typedef app_timer_t * app_timer_id_t; + +#define _APP_TIMER_DEF(timer_id) \ + NRF_LOG_INSTANCE_REGISTER(APP_TIMER_LOG_NAME, timer_id, \ + APP_TIMER_CONFIG_INFO_COLOR, \ + APP_TIMER_CONFIG_DEBUG_COLOR, \ + APP_TIMER_CONFIG_INITIAL_LOG_LEVEL, \ + APP_TIMER_CONFIG_LOG_ENABLED ? \ + APP_TIMER_CONFIG_LOG_LEVEL : NRF_LOG_SEVERITY_NONE); \ + static app_timer_t CONCAT_2(timer_id,_data) = { \ + .active = false, \ + NRF_LOG_INSTANCE_PTR_INIT(p_log, APP_TIMER_LOG_NAME, timer_id) \ + }; \ + static const app_timer_id_t timer_id = &CONCAT_2(timer_id,_data) + +#else //APP_TIMER_V2 +typedef struct app_timer_t { uint32_t data[CEIL_DIV(APP_TIMER_NODE_SIZE, sizeof(uint32_t))]; } app_timer_t; + +/**@brief Timer ID type. + * Never declare a variable of this type, but use the macro @ref APP_TIMER_DEF instead.*/ +typedef app_timer_t * app_timer_id_t; + +#define _APP_TIMER_DEF(timer_id) \ + static app_timer_t CONCAT_2(timer_id,_data) = { {0} }; \ + static const app_timer_id_t timer_id = &CONCAT_2(timer_id,_data) + +#endif + + +/**@brief Structure passed to app_scheduler. */ +typedef struct +{ + app_timer_timeout_handler_t timeout_handler; + void * p_context; +} app_timer_event_t; + +/**@brief Timer modes. */ +typedef enum +{ + APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */ + APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */ +} app_timer_mode_t; + +/**@brief Function for initializing the timer module. + * + * @retval NRF_SUCCESS If the module was initialized successfully. + */ +ret_code_t app_timer_init(void); + +/**@brief Function for creating a timer instance. + * + * @param[in] p_timer_id Pointer to timer identifier. + * @param[in] mode Timer mode. + * @param[in] timeout_handler Function to be executed when the timer expires. + * + * @retval NRF_SUCCESS If the timer was successfully created. + * @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or + * the timer is running. + * + * @note This function does the timer allocation in the caller's context. It is also not protected + * by a critical region. Therefore care must be taken not to call it from several interrupt + * levels simultaneously. + * @note The function can be called again on the timer instance and will re-initialize the instance if + * the timer is not running. + * @attention The FreeRTOS and RTX app_timer implementation does not allow app_timer_create to + * be called on the previously initialized instance. + */ +ret_code_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler); + +/**@brief Function for starting a timer. + * + * @param[in] timer_id Timer identifier. + * @param[in] timeout_ticks Number of ticks (of RTC1, including prescaling) to time-out event + * (minimum 5 ticks). + * @param[in] p_context General purpose pointer. Will be passed to the time-out handler when + * the timer expires. + * + * @retval NRF_SUCCESS If the timer was successfully started. + * @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer + * has not been created. + * @retval NRF_ERROR_NO_MEM If the timer operations queue was full. + * + * @note The minimum timeout_ticks value is 5. + * @note For multiple active timers, time-outs occurring in close proximity to each other (in the + * range of 1 to 3 ticks) will have a positive jitter of maximum 3 ticks. + * @note When calling this method on a timer that is already running, the second start operation + * is ignored. + */ +ret_code_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context); + +/**@brief Function for stopping the specified timer. + * + * @param[in] timer_id Timer identifier. + * + * @retval NRF_SUCCESS If the timer was successfully stopped. + * @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer + * has not been created. + * @retval NRF_ERROR_NO_MEM If the timer operations queue was full. + */ +ret_code_t app_timer_stop(app_timer_id_t timer_id); + +/**@brief Function for stopping all running timers. + * + * @retval NRF_SUCCESS If all timers were successfully stopped. + * @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized. + * @retval NRF_ERROR_NO_MEM If the timer operations queue was full. + */ +ret_code_t app_timer_stop_all(void); + +/**@brief Function for returning the current value of the RTC1 counter. + * + * @return Current value of the RTC1 counter. + */ +uint32_t app_timer_cnt_get(void); + +/**@brief Function for computing the difference between two RTC1 counter values. + * + * @param[in] ticks_to Value returned by app_timer_cnt_get(). + * @param[in] ticks_from Value returned by app_timer_cnt_get(). + * + * @return Number of ticks from ticks_from to ticks_to. + */ +uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, + uint32_t ticks_from); + + +/**@brief Function for getting the maximum observed operation queue utilization. + * + * Function for tuning the module and determining OP_QUEUE_SIZE value and thus module RAM usage. + * + * @note APP_TIMER_WITH_PROFILER must be enabled to use this functionality. + * + * @return Maximum number of events in queue observed so far. + */ +uint8_t app_timer_op_queue_utilization_get(void); + +/** + * @brief Function for pausing RTC activity which drives app_timer. + * + * @note This function can be used for debugging purposes to ensure + * that application is halted when entering a breakpoint. + */ +void app_timer_pause(void); + +/** + * @brief Function for resuming RTC activity which drives app_timer. + * + * @note This function can be used for debugging purposes to resume + * application activity. + */ +void app_timer_resume(void); + +#ifdef __cplusplus +} +#endif + +#endif // APP_TIMER_H__ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_freertos.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_freertos.c new file mode 100644 index 0000000..bea64fd --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_freertos.c @@ -0,0 +1,241 @@ +/** + * Copyright (c) 2014 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(APP_TIMER) +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" + +#include "app_timer.h" +#include <stdlib.h> +#include <string.h> +#include "nrf.h" +#include "app_error.h" + +/** + * Note that this implementation is made only for enable SDK components which interacts with app_timer to work with FreeRTOS. + * It is more suitable to use native FreeRTOS timer for other purposes. + */ +/* Check if RTC FreeRTOS version is used */ +#if configTICK_SOURCE != FREERTOS_USE_RTC +#error app_timer in FreeRTOS variant have to be used with RTC tick source configuration. Default configuration have to be used in other case. +#endif + +/** + * @brief Waiting time for the timer queue + * + * Number of system ticks to wait for the timer queue to put the message. + * It is strongly recommended to set this to the value bigger than 1. + * In other case if timer message queue is full - any operation on timer may fail. + * @note + * Timer functions called from interrupt context would never wait. + */ +#define APP_TIMER_WAIT_FOR_QUEUE 2 + +/**@brief This structure keeps information about osTimer.*/ +typedef struct +{ + void * argument; + TimerHandle_t osHandle; + app_timer_timeout_handler_t func; + /** + * This member is to make sure that timer function is only called if timer is running. + * FreeRTOS may have timer running even after stop function is called, + * because it processes commands in Timer task and stopping function only puts command into the queue. */ + bool active; +}app_timer_info_t; + + +/* Check if freeRTOS timers are activated */ +#if configUSE_TIMERS == 0 + #error app_timer for freeRTOS requires configUSE_TIMERS option to be activated. +#endif + +/* Check if app_timer_t variable type can held our app_timer_info_t structure */ +STATIC_ASSERT(sizeof(app_timer_info_t) <= sizeof(app_timer_t)); + + +/** + * @brief Internal callback function for the system timer + * + * Internal function that is called from the system timer. + * It gets our parameter from timer data and sends it to user function. + * @param[in] xTimer Timer handler + */ +static void app_timer_callback(TimerHandle_t xTimer) +{ + app_timer_info_t * pinfo = (app_timer_info_t*)(pvTimerGetTimerID(xTimer)); + ASSERT(pinfo->osHandle == xTimer); + ASSERT(pinfo->func != NULL); + + if (pinfo->active) + pinfo->func(pinfo->argument); +} + + +uint32_t app_timer_init(void) +{ + return NRF_SUCCESS; +} + + +uint32_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler) +{ + app_timer_info_t * pinfo = (app_timer_info_t*)(*p_timer_id); + uint32_t err_code = NRF_SUCCESS; + unsigned long timer_mode; + + if ((timeout_handler == NULL) || (p_timer_id == NULL)) + { + return NRF_ERROR_INVALID_PARAM; + } + if (pinfo->active) + { + return NRF_ERROR_INVALID_STATE; + } + + if (pinfo->osHandle == NULL) + { + /* New timer is created */ + memset(pinfo, 0, sizeof(app_timer_info_t)); + + if (mode == APP_TIMER_MODE_SINGLE_SHOT) + timer_mode = pdFALSE; + else + timer_mode = pdTRUE; + + pinfo->func = timeout_handler; + pinfo->osHandle = xTimerCreate(" ", 1000, timer_mode, pinfo, app_timer_callback); + + if (pinfo->osHandle == NULL) + err_code = NRF_ERROR_NULL; + } + else + { + /* Timer cannot be reinitialized using FreeRTOS API */ + return NRF_ERROR_INVALID_STATE; + } + + return err_code; +} + + +uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) +{ + app_timer_info_t * pinfo = (app_timer_info_t*)(timer_id); + TimerHandle_t hTimer = pinfo->osHandle; + + if (hTimer == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + if (pinfo->active && (xTimerIsTimerActive(hTimer) != pdFALSE)) + { + // Timer already running - exit silently + return NRF_SUCCESS; + } + + pinfo->argument = p_context; + + if (__get_IPSR() != 0) + { + BaseType_t yieldReq = pdFALSE; + if (xTimerChangePeriodFromISR(hTimer, timeout_ticks, &yieldReq) != pdPASS) + { + return NRF_ERROR_NO_MEM; + } + + if ( xTimerStartFromISR(hTimer, &yieldReq) != pdPASS ) + { + return NRF_ERROR_NO_MEM; + } + + portYIELD_FROM_ISR(yieldReq); + } + else + { + if (xTimerChangePeriod(hTimer, timeout_ticks, APP_TIMER_WAIT_FOR_QUEUE) != pdPASS) + { + return NRF_ERROR_NO_MEM; + } + + if (xTimerStart(hTimer, APP_TIMER_WAIT_FOR_QUEUE) != pdPASS) + { + return NRF_ERROR_NO_MEM; + } + } + + pinfo->active = true; + return NRF_SUCCESS; +} + + +uint32_t app_timer_stop(app_timer_id_t timer_id) +{ + app_timer_info_t * pinfo = (app_timer_info_t*)(timer_id); + TimerHandle_t hTimer = pinfo->osHandle; + if (hTimer == NULL) + { + return NRF_ERROR_INVALID_STATE; + } + + if (__get_IPSR() != 0) + { + BaseType_t yieldReq = pdFALSE; + if (xTimerStopFromISR(hTimer, &yieldReq) != pdPASS) + { + return NRF_ERROR_NO_MEM; + } + portYIELD_FROM_ISR(yieldReq); + } + else + { + if (xTimerStop(hTimer, APP_TIMER_WAIT_FOR_QUEUE) != pdPASS) + { + return NRF_ERROR_NO_MEM; + } + } + + pinfo->active = false; + return NRF_SUCCESS; +} +#endif //NRF_MODULE_ENABLED(APP_TIMER) diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_rtx.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_rtx.c new file mode 100644 index 0000000..b12982f --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/app_timer_rtx.c @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(APP_TIMER) +#include "app_timer.h" +#include <stdlib.h> +#include "nrf.h" +#include "nrf_soc.h" +#include "app_error.h" +#include "cmsis_os.h" +#include "app_util_platform.h" + +#define RTC1_IRQ_PRI APP_IRQ_PRIORITY_LOWEST /**< Priority of the RTC1 interrupt. */ + +#define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */ + +/**@brief This structure keeps information about osTimer.*/ +typedef struct +{ + osTimerDef_t timerDef; + uint32_t buffer[6]; + osTimerId id; +}app_timer_info_t; + +/**@brief Store an array of timers with configuration. */ +typedef struct +{ + uint8_t max_timers; /**< The maximum number of timers*/ + uint32_t prescaler; + app_timer_info_t * app_timers; /**< Pointer to table of timers*/ +}app_timer_control_t; + +app_timer_control_t app_timer_control; + +/**@brief This structure is defined by RTX. It keeps information about created osTimers. It is used in app_timer_start(). */ +typedef struct os_timer_cb_ +{ + struct os_timer_cb_ * next; /**< Pointer to next active Timer */ + uint8_t state; /**< Timer State */ + uint8_t type; /**< Timer Type (Periodic/One-shot). */ + uint16_t reserved; /**< Reserved. */ + uint32_t tcnt; /**< Timer Delay Count. */ + uint32_t icnt; /**< Timer Initial Count. */ + void * arg; /**< Timer Function Argument. */ + const osTimerDef_t * timer; /**< Pointer to Timer definition. */ +} os_timer_cb; + +/**@brief This functions are defined by RTX.*/ +//lint --save -e10 -e19 -e526 +extern osStatus svcTimerStop(osTimerId timer_id); /**< Used in app_timer_stop(). */ +extern osStatus svcTimerStart(osTimerId timer_id, uint32_t millisec); /**< Used in app_timer_start(). */ +// lint --restore +static void * rt_id2obj (void *id) /**< Used in app_timer_start(). This function gives information if osTimerID is valid */ +{ + if ((uint32_t)id & 3U) + { + return NULL; + } + +#ifdef OS_SECTIONS_LINK_INFO + + if ((os_section_id$$Base != 0U) && (os_section_id$$Limit != 0U)) + { + if (id < (void *)os_section_id$$Base) + { + return NULL; + } + + if (id >= (void *)os_section_id$$Limit) + { + return NULL; + } + } +#endif + + return id; +} + + + +ret_code_t app_timer_init(void) +{ + if (p_buffer == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + + app_timer_control.app_timers = p_buffer; + NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI); + + return NRF_SUCCESS; +} + + +ret_code_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler) +{ + + if ((timeout_handler == NULL) || (p_timer_id == NULL)) + { + return NRF_ERROR_INVALID_PARAM; + } + + app_timer_info_t * p_timer_info = (app_timer_info_t *)*p_timer_id; + p_timer_info->timerDef.timer = p_timer_info->buffer; + p_timer_info->timerDef.ptimer = (os_ptimer)timeout_handler; + + p_timer_info->id = osTimerCreate(&(p_timer_info->timerDef), (os_timer_type)mode, NULL); + + if (p_timer_info->id) + return NRF_SUCCESS; + else + { + return NRF_ERROR_INVALID_PARAM; // This error is unspecified by rtx + } +} + +#define osTimerRunning 2 +ret_code_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) +{ + if ((timeout_ticks < APP_TIMER_MIN_TIMEOUT_TICKS)) + { + return NRF_ERROR_INVALID_PARAM; + } + uint32_t timeout_ms = + ((uint32_t)ROUNDED_DIV(timeout_ticks * 1000 * (APP_TIMER_CONFIG_RTC_FREQUENCY + 1), + (uint32_t)APP_TIMER_CLOCK_FREQ)); + + app_timer_info_t * p_timer_info = (app_timer_info_t *)timer_id; + if (rt_id2obj((void *)p_timer_info->id) == NULL) + return NRF_ERROR_INVALID_PARAM; + + // Pass p_context to timer_timeout_handler + ((os_timer_cb *)(p_timer_info->id))->arg = p_context; + + if (((os_timer_cb *)(p_timer_info->id))->state == osTimerRunning) + { + return NRF_SUCCESS; + } + // osTimerStart() returns osErrorISR if it is called in interrupt routine. + switch (osTimerStart((osTimerId)p_timer_info->id, timeout_ms) ) + { + case osOK: + return NRF_SUCCESS; + + case osErrorISR: + break; + + case osErrorParameter: + return NRF_ERROR_INVALID_PARAM; + + default: + return NRF_ERROR_INVALID_PARAM; + } + + // Start timer without svcCall + switch (svcTimerStart((osTimerId)p_timer_info->id, timeout_ms)) + { + case osOK: + return NRF_SUCCESS; + + case osErrorISR: + return NRF_ERROR_INVALID_STATE; + + case osErrorParameter: + return NRF_ERROR_INVALID_PARAM; + + default: + return NRF_ERROR_INVALID_PARAM; + } +} + +ret_code_t app_timer_stop(app_timer_id_t timer_id) +{ + app_timer_info_t * p_timer_info = (app_timer_info_t *)timer_id; + switch (osTimerStop((osTimerId)p_timer_info->id) ) + { + case osOK: + return NRF_SUCCESS; + + case osErrorISR: + break; + + case osErrorParameter: + return NRF_ERROR_INVALID_PARAM; + + case osErrorResource: + return NRF_SUCCESS; + + default: + return NRF_ERROR_INVALID_PARAM; + } + + // Stop timer without svcCall + switch (svcTimerStop((osTimerId)p_timer_info->id)) + { + case osOK: + return NRF_SUCCESS; + + case osErrorISR: + return NRF_ERROR_INVALID_STATE; + + case osErrorParameter: + return NRF_ERROR_INVALID_PARAM; + + case osErrorResource: + return NRF_SUCCESS; + + default: + return NRF_ERROR_INVALID_PARAM; + } +} + + +ret_code_t app_timer_stop_all(void) +{ + for (int i = 0; i < app_timer_control.max_timers; i++) + { + if (app_timer_control.app_timers[i].id) + { + (void)app_timer_stop((app_timer_id_t)app_timer_control.app_timers[i].id); + } + } + return 0; +} + + +extern uint32_t os_tick_val(void); +uint32_t app_timer_cnt_get(void) +{ + return os_tick_val(); +} + + +uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, + uint32_t ticks_from) +{ + return ((ticks_to - ticks_from) & MAX_RTC_COUNTER_VAL); +} +#endif //NRF_MODULE_ENABLED(APP_TIMER) diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c new file mode 100644 index 0000000..aefdb51 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c @@ -0,0 +1,568 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "app_timer.h" +#include "nrf_atfifo.h" +#include "nrf_sortlist.h" +#include "nrf_delay.h" +#if APP_TIMER_CONFIG_USE_SCHEDULER +#include "app_scheduler.h" +#endif +#include <stddef.h> +#define NRF_LOG_MODULE_NAME APP_TIMER_LOG_NAME +#if APP_TIMER_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL APP_TIMER_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR APP_TIMER_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR APP_TIMER_CONFIG_DEBUG_COLOR +#else //APP_TIMER_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif //APP_TIMER_CONFIG_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#include "drv_rtc.h" + +/** + * Maximum possible relative value is limited by safe window to detect cases when requested + * compare event has already occured. + */ +#define APP_TIMER_SAFE_WINDOW 1000 + +#define APP_TIMER_RTC_MAX_VALUE (DRV_RTC_MAX_CNT - APP_TIMER_SAFE_WINDOW) + +static drv_rtc_t m_rtc_inst = DRV_RTC_INSTANCE(1); + +/** + * @brief Timer requests types. + */ +typedef enum +{ + TIMER_REQ_START, + TIMER_REQ_STOP, + TIMER_REQ_STOP_ALL +} app_timer_req_type_t; + +/** + * @brief Operation request structure. + */ +typedef struct +{ + app_timer_req_type_t type; /**< Request type. */ + app_timer_t * p_timer; /**< Timer instance. */ +} timer_req_t; + +static app_timer_t * volatile mp_active_timer; /**< Timer currently handled by RTC driver. */ +static bool m_global_active; /**< Flag used to globally disable all timers. */ + +/* Request FIFO instance. */ +NRF_ATFIFO_DEF(m_req_fifo, timer_req_t, APP_TIMER_CONFIG_OP_QUEUE_SIZE); + +/* Sortlist instance. */ +static bool compare_func(nrf_sortlist_item_t * p_item0, nrf_sortlist_item_t *p_item1); +NRF_SORTLIST_DEF(m_app_timer_sortlist, compare_func); /**< Sortlist used for storing queued timers. */ + +/** + * @brief Function used for comparing items in sorted list. + */ +static inline bool compare_func(nrf_sortlist_item_t * p_item0, nrf_sortlist_item_t *p_item1) +{ + app_timer_t * p0 = CONTAINER_OF(p_item0, app_timer_t, list_item); + app_timer_t * p1 = CONTAINER_OF(p_item1, app_timer_t, list_item); + + uint32_t p0_end = p0->end_val; + uint32_t p1_end = p1->end_val; + return (p0_end <= p1_end) ? true : false; +} + +#if APP_TIMER_CONFIG_USE_SCHEDULER +static void scheduled_timeout_handler(void * p_event_data, uint16_t event_size) +{ + ASSERT(event_size == sizeof(app_timer_event_t)); + app_timer_event_t const * p_timer_event = (app_timer_event_t *)p_event_data; + + p_timer_event->timeout_handler(p_timer_event->p_context); +} +#endif + +/** + * @brief Function called on timer expiration + * + * Function calls user handler if timer was not stopped before. If timer is in repeated mode then + * timer is rescheduled. + * + * @param p_timer Timer instance. + * + * @return True if reevaluation of sortlist needed (becasue it was updated). + */ +static bool timer_expire(app_timer_t * p_timer) +{ + ASSERT(p_timer->handler); + bool ret = false; + if (m_global_active == true && p_timer != NULL && p_timer->active) + { +#if APP_TIMER_CONFIG_USE_SCHEDULER + app_timer_event_t timer_event; + + timer_event.timeout_handler = p_timer->handler; + timer_event.p_context = p_timer->p_context; + uint32_t err_code = app_sched_event_put(&timer_event, + sizeof(timer_event), + scheduled_timeout_handler); + APP_ERROR_CHECK(err_code); +#else + p_timer->handler(p_timer->p_context); +#endif + if (p_timer->repeat_period && p_timer->active) + { + p_timer->end_val += p_timer->repeat_period; + nrf_sortlist_add(&m_app_timer_sortlist, &p_timer->list_item); + ret = true; + } + else + { + p_timer->active = false; + } + } + return ret; +} + +/** + * @brief Function is configuring RTC driver to trigger timeout interrupt for given timer. + * + * It is possible that RTC driver will indicate that timeout already occured. In that case timer + * expires and function indicates that RTC was not configured. + * + * @param p_timer Timer instance. + * @param [in,out] p_rerun Flag indicating that sortlist reevaluation is required. + * + * @return True if RTC was successfully configured, false if timer already expired and RTC was not + * configured. + * + */ +static bool rtc_schedule(app_timer_t * p_timer, bool * p_rerun) +{ + ret_code_t ret = NRF_SUCCESS; + *p_rerun = false; + ret = drv_rtc_windowed_compare_set(&m_rtc_inst, 0, p_timer->end_val, APP_TIMER_SAFE_WINDOW); + + if (ret == NRF_SUCCESS) + { + return true; + } + else if (ret == NRF_ERROR_TIMEOUT) + { + *p_rerun = timer_expire(p_timer); + } + else + { + ASSERT(0); + } + return false; +} + +static inline app_timer_t * sortlist_pop(void) +{ + nrf_sortlist_item_t * p_next_item = nrf_sortlist_pop(&m_app_timer_sortlist); + return p_next_item ? CONTAINER_OF(p_next_item, app_timer_t, list_item) : NULL; +} + +static inline app_timer_t * sortlist_peek(void) +{ + nrf_sortlist_item_t const * p_next_item = nrf_sortlist_peek(&m_app_timer_sortlist); + return p_next_item ? CONTAINER_OF(p_next_item, app_timer_t, list_item) : NULL; +} + +static inline app_timer_t * sortlist_next(app_timer_t * p_item) +{ + nrf_sortlist_item_t const * p_next_item = nrf_sortlist_next(&p_item->list_item); + return p_next_item ? CONTAINER_OF(p_next_item, app_timer_t, list_item) : NULL; +} +/** + * @brief Function for deactivating all timers which are in the sorted list (active timers). + */ +static void sorted_list_stop_all(void) +{ + app_timer_t * p_next; + do + { + p_next = sortlist_pop(); + if (p_next) + { + p_next->active = false; + } + } while (p_next); +} + +/** + * @brief Function for handling RTC counter overflow. + * + * In case of overflow all active timers must have end value adjusted (reduced to 24 bit range). + */ +static void on_overflow_evt(void) +{ + NRF_LOG_DEBUG("Overflow EVT"); + if (mp_active_timer) + { + uint32_t end_val = mp_active_timer->end_val; + mp_active_timer->end_val = end_val & RTC_COUNTER_COUNTER_Msk; + } + + app_timer_t * p_next; + p_next = sortlist_peek(); + while (p_next) + { + if (p_next->end_val <= RTC_COUNTER_COUNTER_Msk) + { + //If overflow occurs then all timers with value lower than max value expires immediately. + UNUSED_RETURN_VALUE(timer_expire(p_next)); + } + else + { + p_next->end_val &= RTC_COUNTER_COUNTER_Msk; + } + p_next = sortlist_next(p_next); + } +} + +/** + * #brief Function for handling RTC compare event - active timer expiration. + */ +static void on_compare_evt(void) +{ + if (mp_active_timer) + { + NRF_LOG_INST_DEBUG(mp_active_timer->p_log, "Compare EVT"); + UNUSED_RETURN_VALUE(timer_expire(mp_active_timer)); + mp_active_timer = NULL; + } + else + { + NRF_LOG_WARNING("Compare event but no active timer (already stopped?)"); + } +} + +/** + * @brief Function updates RTC. + * + * Function is called at the end of RTC interrupt when all new user request and/or timer expiration + * occured. It configures RTC if there is any pending timer, reconfigures if the are timers with + * shorted timeout than active one or stops RTC if there is no active timers. + */ +static void rtc_update(drv_rtc_t const * const p_instance) +{ + while(1) + { + app_timer_t * p_next = sortlist_peek(); + bool rtc_reconf = false; + if (p_next) //Candidate for active timer + { + uint32_t next_end_val = p_next->end_val; + uint32_t active_end_val = mp_active_timer->end_val; + if (mp_active_timer == NULL) + { + //There is no active timer so candidate will become active timer. + rtc_reconf = true; + } + else if (mp_active_timer && + (active_end_val > next_end_val)) + { + //Candidate has shorter timeout than current active timer. Candidate will replace active timer. + //Active timer is put back into sorted list. + rtc_reconf = true; + if (mp_active_timer->active) + { + NRF_LOG_INST_DEBUG(mp_active_timer->p_log, "Timer preempted."); + nrf_sortlist_add(&m_app_timer_sortlist, &mp_active_timer->list_item); + } + } + + if (rtc_reconf) + { + bool rerun; + p_next = sortlist_pop(); + NRF_LOG_INST_DEBUG(p_next->p_log, "Activating timer (CC:%d).", next_end_val); + if (rtc_schedule(p_next, &rerun)) + { + if (!APP_TIMER_KEEPS_RTC_ACTIVE && (mp_active_timer == NULL)) + { + drv_rtc_start(p_instance); + } + mp_active_timer = p_next; + + if (rerun == false) + { + //RTC was successfully updated and sortlist was not updated. Function can be terminated. + break; + } + } + else + { + //If RTC driver indicated that timeout already occured a new candidate will be taken from sorted list. + NRF_LOG_INST_DEBUG(p_next->p_log,"Timer expired before scheduled to RTC."); + } + } + else + { + //RTC will not be updated. Function can terminate. + break; + } + } + else //No candidate for active timer. + { + if (!APP_TIMER_KEEPS_RTC_ACTIVE && mp_active_timer == NULL) + { + drv_rtc_stop(p_instance); + } + break; + } + } +} + +/** + * @brief Function for processing user requests. + * + * Function is called only in the context of RTC interrupt. + */ +static void timer_req_process(drv_rtc_t const * const p_instance) +{ + nrf_atfifo_item_get_t fifo_ctx; + timer_req_t * p_req = nrf_atfifo_item_get(m_req_fifo, &fifo_ctx); + + while (p_req) + { + switch (p_req->type) + { + case TIMER_REQ_START: + if (!p_req->p_timer->active) + { + p_req->p_timer->active = true; + if (p_req->p_timer->end_val - drv_rtc_counter_get(p_instance) > APP_TIMER_RTC_MAX_VALUE) + { + //A little trick to handle case when timer was scheduled just before overflow. + p_req->p_timer->end_val &= RTC_COUNTER_COUNTER_Msk; + } + nrf_sortlist_add(&m_app_timer_sortlist, &(p_req->p_timer->list_item)); + NRF_LOG_INST_DEBUG(p_req->p_timer->p_log,"Start request (CC:%d).", + p_req->p_timer->end_val); + } + break; + case TIMER_REQ_STOP: + if (p_req->p_timer == mp_active_timer) + { + mp_active_timer = NULL; + } + UNUSED_RETURN_VALUE(nrf_sortlist_remove(&m_app_timer_sortlist, &(p_req->p_timer->list_item))); + NRF_LOG_INST_DEBUG(p_req->p_timer->p_log,"Stop request."); + break; + case TIMER_REQ_STOP_ALL: + sorted_list_stop_all(); + m_global_active = true; + NRF_LOG_INFO("Stop all request."); + break; + default: + break; + } + UNUSED_RETURN_VALUE(nrf_atfifo_item_free(m_req_fifo, &fifo_ctx)); + p_req = nrf_atfifo_item_get(m_req_fifo, &fifo_ctx); + } +} + +static void rtc_irq(drv_rtc_t const * const p_instance) +{ + if (drv_rtc_overflow_pending(p_instance)) + { + on_overflow_evt(); + } + if (drv_rtc_compare_pending(p_instance, 0)) + { + on_compare_evt(); + } + timer_req_process(p_instance); + rtc_update(p_instance); +} + +/** + * @brief Function for triggering processing user requests. + * + * @note All user requests are processed in a single context - RTC interrupt. + */ +static inline void timer_request_proc_trigger(void) +{ + drv_rtc_irq_trigger(&m_rtc_inst); +} + +/** + * @brief Function for putting user request into the request queue + */ +static ret_code_t timer_req_schedule(app_timer_req_type_t type, app_timer_t * p_timer) +{ + nrf_atfifo_item_put_t fifo_ctx; + timer_req_t * p_req = nrf_atfifo_item_alloc(m_req_fifo, &fifo_ctx); + + if (p_req) + { + p_req->type = type; + p_req->p_timer = p_timer; + if (nrf_atfifo_item_put(m_req_fifo, &fifo_ctx)) + { + timer_request_proc_trigger(); + } + else + { + NRF_LOG_WARNING("Scheduling interrupted another scheduling."); + } + return NRF_SUCCESS; + } + else + { + return NRF_ERROR_NO_MEM; + } +} + +ret_code_t app_timer_init(void) +{ + ret_code_t err_code; + drv_rtc_config_t config = { + .prescaler = APP_TIMER_CONFIG_RTC_FREQUENCY, + .interrupt_priority = APP_TIMER_CONFIG_IRQ_PRIORITY + }; + + err_code = NRF_ATFIFO_INIT(m_req_fifo); + if (err_code != NRFX_SUCCESS) + { + return err_code; + } + + err_code = drv_rtc_init(&m_rtc_inst, &config, rtc_irq); + if (err_code != NRFX_SUCCESS) + { + return err_code; + } + drv_rtc_overflow_enable(&m_rtc_inst, true); + if (APP_TIMER_KEEPS_RTC_ACTIVE) + { + drv_rtc_start(&m_rtc_inst); + } + + m_global_active = true; + return err_code; +} + +ret_code_t app_timer_create(app_timer_id_t const * p_timer_id, + app_timer_mode_t mode, + app_timer_timeout_handler_t timeout_handler) +{ + ASSERT(p_timer_id); + ASSERT(timeout_handler); + + if (timeout_handler == NULL) + { + return NRF_ERROR_INVALID_PARAM; + } + + app_timer_t * p_t = (app_timer_t *) *p_timer_id; + p_t->handler = timeout_handler; + p_t->repeat_period = (mode == APP_TIMER_MODE_REPEATED) ? 1 : 0; + return NRF_SUCCESS; +} + +ret_code_t app_timer_start(app_timer_t * p_timer, uint32_t timeout_ticks, void * p_context) +{ + ASSERT(p_timer); + app_timer_t * p_t = (app_timer_t *) p_timer; + + if (timeout_ticks > APP_TIMER_RTC_MAX_VALUE) + { + return NRF_ERROR_INVALID_PARAM; + } + p_t->p_context = p_context; + p_t->end_val = drv_rtc_counter_get(&m_rtc_inst) + timeout_ticks; + + if (p_t->repeat_period) + { + p_t->repeat_period = timeout_ticks; + } + + return timer_req_schedule(TIMER_REQ_START, p_t); +} + + +ret_code_t app_timer_stop(app_timer_t * p_timer) +{ + ASSERT(p_timer); + app_timer_t * p_t = (app_timer_t *) p_timer; + p_t->active = false; + + return timer_req_schedule(TIMER_REQ_STOP, p_t); +} + +ret_code_t app_timer_stop_all(void) +{ + //block timer globally + m_global_active = false; + + return timer_req_schedule(TIMER_REQ_STOP_ALL, NULL); +} + +uint8_t app_timer_op_queue_utilization_get(void) +{ + /* Currently not supported by ATFIFO */ + return 0; +} + +uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, + uint32_t ticks_from) +{ + return ((ticks_to - ticks_from) & RTC_COUNTER_COUNTER_Msk); +} + +uint32_t app_timer_cnt_get(void) +{ + return drv_rtc_counter_get(&m_rtc_inst); +} + +void app_timer_pause(void) +{ + drv_rtc_stop(&m_rtc_inst); +} + +void app_timer_resume(void) +{ + drv_rtc_start(&m_rtc_inst); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.c new file mode 100644 index 0000000..8bda72d --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.c @@ -0,0 +1,330 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <nrfx.h> +#include <nrf_delay.h> +#include <drv_rtc.h> + +/* Module is integral part of app_timer implementation. */ +#define NRF_LOG_MODULE_NAME app_timer +#include <nrf_log.h> + +#define EVT_TO_STR(event) \ + (event == NRF_RTC_EVENT_TICK ? "NRF_RTC_EVENT_TICK" : \ + (event == NRF_RTC_EVENT_OVERFLOW ? "NRF_RTC_EVENT_OVERFLOW" : \ + (event == NRF_RTC_EVENT_COMPARE_0 ? "NRF_RTC_EVENT_COMPARE_0" : \ + (event == NRF_RTC_EVENT_COMPARE_1 ? "NRF_RTC_EVENT_COMPARE_1" : \ + (event == NRF_RTC_EVENT_COMPARE_2 ? "NRF_RTC_EVENT_COMPARE_2" : \ + (event == NRF_RTC_EVENT_COMPARE_3 ? "NRF_RTC_EVENT_COMPARE_3" : \ + "UNKNOWN EVENT")))))) +#if defined ( __ICCARM__ ) +/* IAR gives warning for offsetof with non-constant expression.*/ +#define CC_IDX_TO_CC_EVENT(_cc) \ + ((nrf_rtc_event_t)(offsetof(NRF_RTC_Type, EVENTS_COMPARE[0]) + sizeof(uint32_t)*_cc)) +#else +#define CC_IDX_TO_CC_EVENT(_cc) \ + ((nrf_rtc_event_t)(offsetof(NRF_RTC_Type, EVENTS_COMPARE[_cc]))) +#endif + +/**@brief RTC driver instance control block structure. */ +typedef struct +{ + drv_rtc_t const * p_instance; + nrfx_drv_state_t state; /**< Instance state. */ +} drv_rtc_cb_t; + +// User callbacks local storage. +static drv_rtc_handler_t m_handlers[DRV_RTC_ENABLED_COUNT]; +static drv_rtc_cb_t m_cb[DRV_RTC_ENABLED_COUNT]; + +// According to Produce Specification RTC may not trigger COMPARE event if CC value set is equal to +// COUNTER value or COUNTER+1. +#define COUNTER_TO_CC_MIN_DISTANCE 2 + +ret_code_t drv_rtc_init(drv_rtc_t const * const p_instance, + drv_rtc_config_t const * p_config, + drv_rtc_handler_t handler) +{ + ASSERT(p_instance); + ASSERT(p_config); + ASSERT(handler); + + ret_code_t err_code; + + m_handlers[p_instance->instance_id] = handler; + + if (m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED) + { + err_code = NRF_ERROR_INVALID_STATE; + NRF_LOG_WARNING("RTC instance already initialized."); + return err_code; + } + + nrf_rtc_prescaler_set(p_instance->p_reg, p_config->prescaler); + NRFX_IRQ_PRIORITY_SET(p_instance->irq, p_config->interrupt_priority); + NRFX_IRQ_ENABLE(p_instance->irq); + + m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_INITIALIZED; + m_cb[p_instance->instance_id].p_instance = p_instance; + + err_code = NRF_SUCCESS; + NRF_LOG_INFO("RTC: initialized."); + return err_code; +} + +void drv_rtc_uninit(drv_rtc_t const * const p_instance) +{ + ASSERT(p_instance); + uint32_t mask = NRF_RTC_INT_TICK_MASK | + NRF_RTC_INT_OVERFLOW_MASK | + NRF_RTC_INT_COMPARE0_MASK | + NRF_RTC_INT_COMPARE1_MASK | + NRF_RTC_INT_COMPARE2_MASK | + NRF_RTC_INT_COMPARE3_MASK; + ASSERT(m_cb[p_instance->instance_id].state != NRFX_DRV_STATE_UNINITIALIZED); + + NRFX_IRQ_DISABLE(p_instance->irq); + + drv_rtc_stop(p_instance); + nrf_rtc_event_disable(p_instance->p_reg, mask); + nrf_rtc_int_disable(p_instance->p_reg, mask); + + m_cb[p_instance->instance_id].state = NRFX_DRV_STATE_UNINITIALIZED; + NRF_LOG_INFO("RTC: Uninitialized."); +} + +void drv_rtc_start(drv_rtc_t const * const p_instance) +{ + ASSERT(p_instance); + nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_START); +} + +void drv_rtc_stop(drv_rtc_t const * const p_instance) +{ + ASSERT(p_instance); + nrf_rtc_task_trigger(p_instance->p_reg, NRF_RTC_TASK_STOP); +} + +void drv_rtc_compare_set(drv_rtc_t const * const p_instance, + uint32_t cc, + uint32_t abs_value, + bool irq_enable) +{ + ASSERT(p_instance); + nrf_rtc_int_t cc_int_mask = (nrf_rtc_int_t)(NRF_RTC_INT_COMPARE0_MASK << cc); + nrf_rtc_event_t cc_evt = CC_IDX_TO_CC_EVENT(cc); + abs_value &= RTC_COUNTER_COUNTER_Msk; + + nrf_rtc_int_disable(p_instance->p_reg, cc_int_mask); + nrf_rtc_event_disable(p_instance->p_reg, cc_int_mask); + nrf_rtc_event_clear(p_instance->p_reg, cc_evt); + nrf_rtc_cc_set(p_instance->p_reg, cc,abs_value); + nrf_rtc_event_enable(p_instance->p_reg, cc_int_mask); + + if (irq_enable) + { + nrf_rtc_int_enable(p_instance->p_reg, cc_int_mask); + } +} + +ret_code_t drv_rtc_windowed_compare_set(drv_rtc_t const * const p_instance, + uint32_t cc, + uint32_t abs_value, + uint16_t safe_window) +{ + ASSERT(p_instance); + uint32_t prev_cc_set; + uint32_t counter; + nrf_rtc_int_t cc_int_mask = (nrf_rtc_int_t)(NRF_RTC_INT_COMPARE0_MASK << cc); + nrf_rtc_event_t cc_evt = CC_IDX_TO_CC_EVENT(cc);; + abs_value &=RTC_COUNTER_COUNTER_Msk; + + nrf_rtc_int_disable(p_instance->p_reg, cc_int_mask); + nrf_rtc_event_disable(p_instance->p_reg, cc_int_mask); + + nrf_rtc_event_clear(p_instance->p_reg, cc_evt); + prev_cc_set = nrf_rtc_cc_get(p_instance->p_reg, cc); + + nrf_rtc_cc_set(p_instance->p_reg, cc,abs_value); + /* If prev CC setting equals or is just in front of the counter then there is a risk that before + * new CC will be set after enabling event previous CC will generate COMPARE event. In such risk + * delay must be introduced between writting CC value and enabling the event. + */ + counter = nrf_rtc_counter_get(p_instance->p_reg); + if (((prev_cc_set - counter) & RTC_COUNTER_COUNTER_Msk) == 1) + { + NRF_LOG_DEBUG("RTC: Delay introduced due to risk of pre-firing."); + nrf_delay_us(33); + } + nrf_rtc_event_enable(p_instance->p_reg, cc_int_mask); + + counter = nrf_rtc_counter_get(p_instance->p_reg); + int32_t diff = (int32_t)abs_value - (int32_t)counter; + + diff &= RTC_COUNTER_COUNTER_Msk; + diff = (diff & 0x800000) ? (diff | 0xFF000000) : diff; + + /* If diff shows that abs_value is after the counter or up to 2 ticks before then it is assumed + * that compare channel was set to late. It is based on a assumption that abs_value will never + * be set to value bigger than maximum counter value - safe window. */ + if ((diff > (int32_t)(-safe_window)) && (diff <= COUNTER_TO_CC_MIN_DISTANCE)) + { + //set CC to something back in time to prevent event triggering on next compare set. + NRF_LOG_DEBUG("RTC: Windowed compare set timeout (abs_value:%d, counter:%d).", + abs_value, counter); + return NRF_ERROR_TIMEOUT; + } + else + { + nrf_rtc_int_enable(p_instance->p_reg, cc_int_mask); + } + return NRF_SUCCESS; +} + +static void evt_enable(drv_rtc_t const * const p_instance, uint32_t mask, bool irq_enable) +{ + ASSERT(p_instance); + nrf_rtc_event_enable(p_instance->p_reg, mask); + if (irq_enable) + { + nrf_rtc_int_enable(p_instance->p_reg, mask); + } +} + +static void evt_disable(drv_rtc_t const * const p_instance, uint32_t mask) +{ + ASSERT(p_instance); + nrf_rtc_event_disable(p_instance->p_reg, mask); + nrf_rtc_int_disable(p_instance->p_reg, mask); +} + +static bool evt_pending(drv_rtc_t const * const p_instance, nrf_rtc_event_t event) +{ + ASSERT(p_instance); + if (nrf_rtc_event_pending(p_instance->p_reg, event)) + { + nrf_rtc_event_clear(p_instance->p_reg, event); + return true; + } + return false; +} + +void drv_rtc_overflow_enable(drv_rtc_t const * const p_instance, bool irq_enable) +{ + evt_enable(p_instance, NRF_RTC_INT_OVERFLOW_MASK, irq_enable); +} + +void drv_rtc_overflow_disable(drv_rtc_t const * const p_instance) +{ + evt_disable(p_instance, NRF_RTC_INT_OVERFLOW_MASK); +} + +bool drv_rtc_overflow_pending(drv_rtc_t const * const p_instance) +{ + return evt_pending(p_instance, NRF_RTC_EVENT_OVERFLOW); +} + +void drv_rtc_tick_enable(drv_rtc_t const * const p_instance, bool irq_enable) +{ + evt_enable(p_instance, NRF_RTC_INT_TICK_MASK, irq_enable); +} + +void drv_rtc_tick_disable(drv_rtc_t const * const p_instance) +{ + evt_disable(p_instance, NRF_RTC_INT_TICK_MASK); +} + +bool drv_rtc_tick_pending(drv_rtc_t const * const p_instance) +{ + return evt_pending(p_instance, NRF_RTC_EVENT_TICK); +} + +void drv_rtc_compare_enable(drv_rtc_t const * const p_instance, + uint32_t cc, + bool irq_enable) +{ + evt_enable(p_instance, (uint32_t)NRF_RTC_INT_COMPARE0_MASK << cc, irq_enable); +} + +void drv_rtc_compare_disable(drv_rtc_t const * const p_instance, uint32_t cc) +{ + evt_disable(p_instance, (uint32_t)NRF_RTC_INT_COMPARE0_MASK << cc); +} + +bool drv_rtc_compare_pending(drv_rtc_t const * const p_instance, uint32_t cc) +{ + nrf_rtc_event_t cc_evt = CC_IDX_TO_CC_EVENT(cc); + return evt_pending(p_instance, cc_evt); +} + +uint32_t drv_rtc_counter_get(drv_rtc_t const * const p_instance) +{ + return nrf_rtc_counter_get(p_instance->p_reg); +} + +void drv_rtc_irq_trigger(drv_rtc_t const * const p_instance) +{ + NVIC_SetPendingIRQ(p_instance->irq); +} + +#define drv_rtc_rtc_0_irq_handler RTC0_IRQHandler +#define drv_rtc_rtc_1_irq_handler RTC1_IRQHandler +#define drv_rtc_rtc_2_irq_handler RTC2_IRQHandler + +#if defined(APP_TIMER_V2_RTC0_ENABLED) +void drv_rtc_rtc_0_irq_handler(void) +{ + m_handlers[DRV_RTC_RTC0_INST_IDX](m_cb[DRV_RTC_RTC0_INST_IDX].p_instance); +} +#endif + +#if defined(APP_TIMER_V2_RTC1_ENABLED) +void drv_rtc_rtc_1_irq_handler(void) +{ + m_handlers[DRV_RTC_RTC1_INST_IDX](m_cb[DRV_RTC_RTC1_INST_IDX].p_instance); +} +#endif + +#if defined(APP_TIMER_V2_RTC2_ENABLED) +void drv_rtc_rtc_2_irq_handler(void) +{ + m_handlers[DRV_RTC_RTC2_INST_IDX](m_cb[DRV_RTC_RTC2_INST_IDX].p_instance); +} +#endif diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.h new file mode 100644 index 0000000..3410460 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/drv_rtc.h @@ -0,0 +1,304 @@ +/** + * Copyright (c) 2018 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef DRV_RTC_H__ +#define DRV_RTC_H__ + +#include <nrfx.h> +#include <hal/nrf_rtc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup drv_rtc RTC driver + * @{ + * @ingroup app_timer + * @brief Real Timer Counter (RTC) peripheral driver for app_timer. + */ + +/** @brief Maximum RTC counter value. */ +#define DRV_RTC_MAX_CNT RTC_COUNTER_COUNTER_Msk + +/** @brief Time requires to update registers between RTC and MCU domains. */ +#define DRV_RTC_CONFIG_APPLY_TIME_US 33 + +/** + * @brief Minimum delta value between set value and counter value. + * + * RTC peripheral requires two ticks to be sure that value it properly set in RTC value. Compare + * channel function requires additional one tick to avoid problematic situations (lack or additional + * unspecified event) when Compare Channel register is reseting or setting to N+2 value. + */ +#define DRV_RTC_MIN_TICK_HANDLED 3 + +/** @brief Macro to convert microseconds into ticks. */ +#define DRV_RTC_US_TO_TICKS(us,freq) (us >= 2^17 ? \ + ((((us)/1000)*(freq))/1000U) : (((us)*(freq))/1000000U) ) + + +/** @brief RTC driver instance structure. */ +typedef struct +{ + NRF_RTC_Type * p_reg; /**< Pointer to instance register set. */ + IRQn_Type irq; /**< Instance IRQ ID. */ + uint8_t instance_id; /**< Instance index. */ + uint8_t cc_channel_count; /**< Number of capture/compare channels. */ +} drv_rtc_t; + +/** @brief Macro for creating RTC driver instance.*/ +#define DRV_RTC_INSTANCE(id) \ +{ \ + .p_reg = NRFX_CONCAT_2(NRF_RTC, id), \ + .irq = NRFX_CONCAT_3(RTC, id, _IRQn), \ + .instance_id = NRFX_CONCAT_3(DRV_RTC_RTC, id, _INST_IDX), \ + .cc_channel_count = NRF_RTC_CC_CHANNEL_COUNT(id), \ +} + +enum { +#if defined(APP_TIMER_V2_RTC0_ENABLED) + DRV_RTC_RTC0_INST_IDX, +#endif +#if defined(APP_TIMER_V2_RTC1_ENABLED) + DRV_RTC_RTC1_INST_IDX, +#endif +#if defined(APP_TIMER_V2_RTC2_ENABLED) + DRV_RTC_RTC2_INST_IDX, +#endif + DRV_RTC_ENABLED_COUNT +}; + +/** @brief RTC driver instance configuration structure. */ +typedef struct +{ + uint16_t prescaler; /**< Prescaler. */ + uint8_t interrupt_priority; /**< Interrupt priority. */ +} drv_rtc_config_t; + +/** @brief RTC instance default configuration. */ +#define DRV_RTC_DEFAULT_CONFIG \ +{ \ + .prescaler = RTC_FREQ_TO_PRESCALER(DRV_RTC_DEFAULT_CONFIG_FREQUENCY), \ + .interrupt_priority = DRV_RTC_DEFAULT_CONFIG_IRQ_PRIORITY, \ +} + +/** @brief RTC driver instance handler type. */ +typedef void (*drv_rtc_handler_t)(drv_rtc_t const * const p_instance); + +/** + * @brief Function for initializing the RTC driver instance. + * + * After initialization, the instance is in power off state. The LFCLK (@ref nrfx_clock) + * has to be started before using @ref drv_rtc. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] p_config Pointer to the structure with initial configuration. + * @param[in] handler Event handler provided by the user. Must not be NULL. + * + * @retval NRF_SUCCESS If successfully initialized. + * @retval NRF_ERROR_INVALID_STATE If the instance is already initialized. + */ +ret_code_t drv_rtc_init(drv_rtc_t const * const p_instance, + drv_rtc_config_t const * p_config, + drv_rtc_handler_t handler); + +/** + * @brief Function for uninitializing the RTC driver instance. + * + * After uninitialization, the instance is in idle state. The hardware should return to the state + * before initialization. The function asserts if the instance is in idle state. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_uninit(drv_rtc_t const * const p_instance); + +/** + * @brief Function for starting RTC clock. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_start(drv_rtc_t const * const p_instance); + +/** + * @brief Function for stopping RTC clock. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_stop(drv_rtc_t const * const p_instance); + +/** + * @brief Function for configuring compare channel. + * + * @note Function disables interrupts and only enable compare events. Remember to enable interrupt + * using @ref drv_rtc_compare_enable in case of using it. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] cc Compare channel index. + * @param[in] abs_value Absolute value to be set in the compare register. + * @param[in] irq_enable True to enable interrupt. + */ +void drv_rtc_compare_set(drv_rtc_t const * const p_instance, + uint32_t cc, + uint32_t abs_value, + bool irq_enable); + +/** + * @brief Function for configuring compare channel with safe window. + * + * Maximum possible relative value is limited by safe window to detect + * cases when requested compare event has already occured. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] cc Compare channel index. + * @param[in] abs_value Absolute value to be set in the compare register. + * @param[in] safe_window Width of the safe window. + * + * @retval NRF_ERROR_TIMEOUT If @par abs_value is in safe window of event occured before + * enabling compare channel intterupt. + * @retval NRF_SUCCESS If successfully set. + */ +ret_code_t drv_rtc_windowed_compare_set(drv_rtc_t const * const p_instance, + uint32_t cc, + uint32_t abs_value, + uint16_t safe_window); + +/** + * @brief Function for enabling overflow event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] irq_enable True to enable interrupt. + */ +void drv_rtc_overflow_enable(drv_rtc_t const * const p_instance, bool irq_enable); + +/** + * @brief Function for diabling overflow event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_overflow_disable(drv_rtc_t const * const p_instance); + +/** + * @brief Function for checking if overflow event has occured. + * + * @note Event is cleared after reading. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return True if interrupt pending, false otherwise. + */ +bool drv_rtc_overflow_pending(drv_rtc_t const * const p_instance); + +/** + * @brief Function for enabling tick event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] irq_enable True to enable interrupt. + */ +void drv_rtc_tick_enable(drv_rtc_t const * const p_instance, bool irq_enable); + +/** + * @brief Function for disabling tick event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_tick_disable(drv_rtc_t const * const p_instance); + +/** + * @brief Function for checking if tick event has occured. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return True if interrupt pending, false otherwise. + */ +bool drv_rtc_tick_pending(drv_rtc_t const * const p_instance); + +/** + * @brief Function for enabling compare channel event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] cc Compare channel index. + * @param[in] irq_enable True to enable interrupt. + */ +void drv_rtc_compare_enable(drv_rtc_t const * const p_instance, + uint32_t cc, + bool irq_enable); + +/** + * @brief Function for disabling compare channel event and interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] cc Compare channel index. + */ +void drv_rtc_compare_disable(drv_rtc_t const * const p_instance, uint32_t cc); + +/** + * @brief Function for checking if compare channel event has occured. + * + * @param[in] p_instance Pointer to the driver instance structure. + * @param[in] cc Compare channel index. + * + * @return True if interrupt pending, false otherwise. + */ +bool drv_rtc_compare_pending(drv_rtc_t const * const p_instance, uint32_t cc); + +/** + * @brief Function for getting current value of RTC counter. + * + * @param[in] p_instance Pointer to the driver instance structure. + * + * @return Counter value. + */ +uint32_t drv_rtc_counter_get(drv_rtc_t const * const p_instance); + +/** + * @brief Function for triggering RTC interrupt. + * + * @param[in] p_instance Pointer to the driver instance structure. + */ +void drv_rtc_irq_trigger(drv_rtc_t const * const p_instance); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // DRV_RTC_H__ |