aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/timer/experimental/app_timer2.c568
1 files changed, 568 insertions, 0 deletions
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);
+}