AndroidのBLEラジコンプロポアプリ「BLEPropo」と接続し、RCサーボとDCモータを制御するプログラムです。 mbed HRM1017で動作を確認しています。 BLEPropo → https://github.com/lipoyang/BLEPropo

Dependencies:   BLE_API mbed

Fork of BLE_RCBController2 by Junichi Katsu

BLEを使ったAndroid用ラジコンプロポアプリ「BLEPropo」に対応するmbed HRM1017用ファームウェアです。
BLEPropoは、GitHubにて公開中。
https://github.com/lipoyang/BLEPropo
/media/uploads/lipoyang/blepropo_ui.png
ラジコンは、mbed HRM1017とRCサーボやDCモータを組み合わせて作ります。
/media/uploads/lipoyang/ble_wiring.png

Committer:
jksoft
Date:
Wed Aug 20 13:41:01 2014 +0000
Revision:
4:ebda47d22091
Parent:
nRF51822/nordic/app_common/app_timer.cpp@1:48f6e08a3ac2
?????????

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jksoft 1:48f6e08a3ac2 1 /* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
jksoft 1:48f6e08a3ac2 2 *
jksoft 1:48f6e08a3ac2 3 * The information contained herein is property of Nordic Semiconductor ASA.
jksoft 1:48f6e08a3ac2 4 * Terms and conditions of usage are described in detail in NORDIC
jksoft 1:48f6e08a3ac2 5 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
jksoft 1:48f6e08a3ac2 6 *
jksoft 1:48f6e08a3ac2 7 * Licensees are granted free, non-transferable use of the information. NO
jksoft 1:48f6e08a3ac2 8 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
jksoft 1:48f6e08a3ac2 9 * the file.
jksoft 1:48f6e08a3ac2 10 *
jksoft 1:48f6e08a3ac2 11 */
jksoft 1:48f6e08a3ac2 12
jksoft 1:48f6e08a3ac2 13 #include "app_timer.h"
jksoft 1:48f6e08a3ac2 14 #include <stdlib.h>
jksoft 1:48f6e08a3ac2 15 #include "nrf51.h"
jksoft 1:48f6e08a3ac2 16 #include "nrf51_bitfields.h"
jksoft 1:48f6e08a3ac2 17 #include "nrf_soc.h"
jksoft 1:48f6e08a3ac2 18 #include "app_error.h"
jksoft 1:48f6e08a3ac2 19 //#include "nrf_delay.h"
jksoft 1:48f6e08a3ac2 20 #include "mbed.h"
jksoft 1:48f6e08a3ac2 21 #include "app_util.h"
jksoft 1:48f6e08a3ac2 22 #include "app_util_platform.h"
jksoft 1:48f6e08a3ac2 23
jksoft 1:48f6e08a3ac2 24
jksoft 1:48f6e08a3ac2 25 #define RTC1_IRQ_PRI APP_IRQ_PRIORITY_LOW /**< Priority of the RTC1 interrupt (used for checking for timeouts and executing timeout handlers). */
jksoft 1:48f6e08a3ac2 26 #define SWI0_IRQ_PRI APP_IRQ_PRIORITY_LOW /**< Priority of the SWI0 interrupt (used for updating the timer list). */
jksoft 1:48f6e08a3ac2 27
jksoft 1:48f6e08a3ac2 28 // The current design assumes that both interrupt handlers run at the same interrupt level.
jksoft 1:48f6e08a3ac2 29 // If this is to be changed, protection must be added to prevent them from interrupting each other
jksoft 1:48f6e08a3ac2 30 // (e.g. by using guard/trigger flags).
jksoft 1:48f6e08a3ac2 31 STATIC_ASSERT(RTC1_IRQ_PRI == SWI0_IRQ_PRI);
jksoft 1:48f6e08a3ac2 32
jksoft 1:48f6e08a3ac2 33 #define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */
jksoft 1:48f6e08a3ac2 34
jksoft 1:48f6e08a3ac2 35 #define APP_HIGH_USER_ID 0 /**< User Id for the Application High "user". */
jksoft 1:48f6e08a3ac2 36 #define APP_LOW_USER_ID 1 /**< User Id for the Application Low "user". */
jksoft 1:48f6e08a3ac2 37 #define THREAD_MODE_USER_ID 2 /**< User Id for the Thread Mode "user". */
jksoft 1:48f6e08a3ac2 38
jksoft 1:48f6e08a3ac2 39 #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.*/
jksoft 1:48f6e08a3ac2 40
jksoft 1:48f6e08a3ac2 41 #define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */
jksoft 1:48f6e08a3ac2 42
jksoft 1:48f6e08a3ac2 43 /**@brief Timer allocation state type. */
jksoft 1:48f6e08a3ac2 44 typedef enum
jksoft 1:48f6e08a3ac2 45 {
jksoft 1:48f6e08a3ac2 46 STATE_FREE, /**< The timer node is available. */
jksoft 1:48f6e08a3ac2 47 STATE_ALLOCATED /**< The timer node has been allocated. */
jksoft 1:48f6e08a3ac2 48 } timer_alloc_state_t;
jksoft 1:48f6e08a3ac2 49
jksoft 1:48f6e08a3ac2 50 /**@brief Timer node type. The nodes will be used form a linked list of running timers. */
jksoft 1:48f6e08a3ac2 51 typedef struct
jksoft 1:48f6e08a3ac2 52 {
jksoft 1:48f6e08a3ac2 53 timer_alloc_state_t state; /**< Timer allocation state. */
jksoft 1:48f6e08a3ac2 54 app_timer_mode_t mode; /**< Timer mode. */
jksoft 1:48f6e08a3ac2 55 uint32_t ticks_to_expire; /**< Number of ticks from previous timer interrupt to timer expiry. */
jksoft 1:48f6e08a3ac2 56 uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */
jksoft 1:48f6e08a3ac2 57 uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */
jksoft 1:48f6e08a3ac2 58 uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */
jksoft 1:48f6e08a3ac2 59 bool is_running; /**< True if timer is running, False otherwise. */
jksoft 1:48f6e08a3ac2 60 app_timer_timeout_handler_t p_timeout_handler; /**< Pointer to function to be executed when the timer expires. */
jksoft 1:48f6e08a3ac2 61 void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */
jksoft 1:48f6e08a3ac2 62 app_timer_id_t next; /**< Id of next timer in list of running timers. */
jksoft 1:48f6e08a3ac2 63 } timer_node_t;
jksoft 1:48f6e08a3ac2 64
jksoft 1:48f6e08a3ac2 65 STATIC_ASSERT(sizeof(timer_node_t) <= APP_TIMER_NODE_SIZE);
jksoft 1:48f6e08a3ac2 66 STATIC_ASSERT(sizeof(timer_node_t) % 4 == 0);
jksoft 1:48f6e08a3ac2 67
jksoft 1:48f6e08a3ac2 68 /**@brief Set of available timer operation types. */
jksoft 1:48f6e08a3ac2 69 typedef enum
jksoft 1:48f6e08a3ac2 70 {
jksoft 1:48f6e08a3ac2 71 TIMER_USER_OP_TYPE_NONE, /**< Invalid timer operation type. */
jksoft 1:48f6e08a3ac2 72 TIMER_USER_OP_TYPE_START, /**< Timer operation type Start. */
jksoft 1:48f6e08a3ac2 73 TIMER_USER_OP_TYPE_STOP, /**< Timer operation type Stop. */
jksoft 1:48f6e08a3ac2 74 TIMER_USER_OP_TYPE_STOP_ALL /**< Timer operation type Stop All. */
jksoft 1:48f6e08a3ac2 75 } timer_user_op_type_t;
jksoft 1:48f6e08a3ac2 76
jksoft 1:48f6e08a3ac2 77 /**@brief Structure describing a timer start operation. */
jksoft 1:48f6e08a3ac2 78 typedef struct
jksoft 1:48f6e08a3ac2 79 {
jksoft 1:48f6e08a3ac2 80 uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */
jksoft 1:48f6e08a3ac2 81 uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */
jksoft 1:48f6e08a3ac2 82 uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */
jksoft 1:48f6e08a3ac2 83 void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */
jksoft 1:48f6e08a3ac2 84 } timer_user_op_start_t;
jksoft 1:48f6e08a3ac2 85
jksoft 1:48f6e08a3ac2 86 /**@brief Structure describing a timer operation. */
jksoft 1:48f6e08a3ac2 87 typedef struct
jksoft 1:48f6e08a3ac2 88 {
jksoft 1:48f6e08a3ac2 89 timer_user_op_type_t op_type; /**< Timer operation type. */
jksoft 1:48f6e08a3ac2 90 app_timer_id_t timer_id; /**< Id of timer on which the operation is to be performed. */
jksoft 1:48f6e08a3ac2 91 union
jksoft 1:48f6e08a3ac2 92 {
jksoft 1:48f6e08a3ac2 93 timer_user_op_start_t start; /**< Structure describing a timer start operation. */
jksoft 1:48f6e08a3ac2 94 } params;
jksoft 1:48f6e08a3ac2 95 } timer_user_op_t;
jksoft 1:48f6e08a3ac2 96
jksoft 1:48f6e08a3ac2 97 STATIC_ASSERT(sizeof(timer_user_op_t) <= APP_TIMER_USER_OP_SIZE);
jksoft 1:48f6e08a3ac2 98 STATIC_ASSERT(sizeof(timer_user_op_t) % 4 == 0);
jksoft 1:48f6e08a3ac2 99
jksoft 1:48f6e08a3ac2 100 /**@brief Structure describing a timer user.
jksoft 1:48f6e08a3ac2 101 *
jksoft 1:48f6e08a3ac2 102 * @details For each user of the timer module, there will be a timer operations queue. This queue
jksoft 1:48f6e08a3ac2 103 * will hold timer operations issued by this user until the timer interrupt handler
jksoft 1:48f6e08a3ac2 104 * processes these operations. For the current implementation, there will be one user for
jksoft 1:48f6e08a3ac2 105 * each interrupt level available to the application (APP_HIGH, APP_LOW and THREAD_MODE),
jksoft 1:48f6e08a3ac2 106 * but the module can easily be modified to e.g. have one queue per process when using an
jksoft 1:48f6e08a3ac2 107 * RTOS. The purpose of the queues is to be able to have a completely lockless timer
jksoft 1:48f6e08a3ac2 108 * implementation.
jksoft 1:48f6e08a3ac2 109 */
jksoft 1:48f6e08a3ac2 110 typedef struct
jksoft 1:48f6e08a3ac2 111 {
jksoft 1:48f6e08a3ac2 112 uint8_t first; /**< Index of first entry to have been inserted in the queue (i.e. the next entry to be executed). */
jksoft 1:48f6e08a3ac2 113 uint8_t last; /**< Index of last entry to have been inserted in the queue. */
jksoft 1:48f6e08a3ac2 114 uint8_t user_op_queue_size; /**< Queue size. */
jksoft 1:48f6e08a3ac2 115 timer_user_op_t * p_user_op_queue; /**< Queue buffer. */
jksoft 1:48f6e08a3ac2 116 } timer_user_t;
jksoft 1:48f6e08a3ac2 117
jksoft 1:48f6e08a3ac2 118 STATIC_ASSERT(sizeof(timer_user_t) == APP_TIMER_USER_SIZE);
jksoft 1:48f6e08a3ac2 119 STATIC_ASSERT(sizeof(timer_user_t) % 4 == 0);
jksoft 1:48f6e08a3ac2 120
jksoft 1:48f6e08a3ac2 121 /**@brief User id type.
jksoft 1:48f6e08a3ac2 122 *
jksoft 1:48f6e08a3ac2 123 * @details In the current implementation, this will automatically be generated from the current
jksoft 1:48f6e08a3ac2 124 * interrupt level.
jksoft 1:48f6e08a3ac2 125 */
jksoft 1:48f6e08a3ac2 126 typedef uint32_t timer_user_id_t;
jksoft 1:48f6e08a3ac2 127
jksoft 1:48f6e08a3ac2 128 #define TIMER_NULL ((app_timer_id_t)(0 - 1)) /**< Invalid timer id. */
jksoft 1:48f6e08a3ac2 129 #define CONTEXT_QUEUE_SIZE_MAX (2) /**< Timer internal elapsed ticks queue size. */
jksoft 1:48f6e08a3ac2 130
jksoft 1:48f6e08a3ac2 131 static uint8_t m_node_array_size; /**< Size of timer node array. */
jksoft 1:48f6e08a3ac2 132 static timer_node_t * mp_nodes = NULL; /**< Array of timer nodes. */
jksoft 1:48f6e08a3ac2 133 static uint8_t m_user_array_size; /**< Size of timer user array. */
jksoft 1:48f6e08a3ac2 134 static timer_user_t * mp_users; /**< Array of timer users. */
jksoft 1:48f6e08a3ac2 135 static app_timer_id_t m_timer_id_head; /**< First timer in list of running timers. */
jksoft 1:48f6e08a3ac2 136 static uint32_t m_ticks_latest; /**< Last known RTC counter value. */
jksoft 1:48f6e08a3ac2 137 static uint32_t m_ticks_elapsed[CONTEXT_QUEUE_SIZE_MAX]; /**< Timer internal elapsed ticks queue. */
jksoft 1:48f6e08a3ac2 138 static uint8_t m_ticks_elapsed_q_read_ind; /**< Timer internal elapsed ticks queue read index. */
jksoft 1:48f6e08a3ac2 139 static uint8_t m_ticks_elapsed_q_write_ind; /**< Timer internal elapsed ticks queue write index. */
jksoft 1:48f6e08a3ac2 140 static app_timer_evt_schedule_func_t m_evt_schedule_func; /**< Pointer to function for propagating timeout events to the scheduler. */
jksoft 1:48f6e08a3ac2 141
jksoft 1:48f6e08a3ac2 142
jksoft 1:48f6e08a3ac2 143 /**@brief Function for initializing the RTC1 counter.
jksoft 1:48f6e08a3ac2 144 *
jksoft 1:48f6e08a3ac2 145 * @param[in] prescaler Value of the RTC1 PRESCALER register. Set to 0 for no prescaling.
jksoft 1:48f6e08a3ac2 146 */
jksoft 1:48f6e08a3ac2 147 static void rtc1_init(uint32_t prescaler)
jksoft 1:48f6e08a3ac2 148 {
jksoft 1:48f6e08a3ac2 149 NRF_RTC1->PRESCALER = prescaler;
jksoft 1:48f6e08a3ac2 150 NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI);
jksoft 1:48f6e08a3ac2 151 }
jksoft 1:48f6e08a3ac2 152
jksoft 1:48f6e08a3ac2 153
jksoft 1:48f6e08a3ac2 154 /**@brief Function for starting the RTC1 timer.
jksoft 1:48f6e08a3ac2 155 */
jksoft 1:48f6e08a3ac2 156 static void rtc1_start(void)
jksoft 1:48f6e08a3ac2 157 {
jksoft 1:48f6e08a3ac2 158 NRF_RTC1->EVTENSET = RTC_EVTEN_COMPARE0_Msk;
jksoft 1:48f6e08a3ac2 159 NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
jksoft 1:48f6e08a3ac2 160
jksoft 1:48f6e08a3ac2 161 NVIC_ClearPendingIRQ(RTC1_IRQn);
jksoft 1:48f6e08a3ac2 162 NVIC_EnableIRQ(RTC1_IRQn);
jksoft 1:48f6e08a3ac2 163
jksoft 1:48f6e08a3ac2 164 NRF_RTC1->TASKS_START = 1;
jksoft 1:48f6e08a3ac2 165 wait(0.0000001 * MAX_RTC_TASKS_DELAY);
jksoft 1:48f6e08a3ac2 166 }
jksoft 1:48f6e08a3ac2 167
jksoft 1:48f6e08a3ac2 168
jksoft 1:48f6e08a3ac2 169 /**@brief Function for stopping the RTC1 timer.
jksoft 1:48f6e08a3ac2 170 */
jksoft 1:48f6e08a3ac2 171 static void rtc1_stop(void)
jksoft 1:48f6e08a3ac2 172 {
jksoft 1:48f6e08a3ac2 173 NVIC_DisableIRQ(RTC1_IRQn);
jksoft 1:48f6e08a3ac2 174
jksoft 1:48f6e08a3ac2 175 NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
jksoft 1:48f6e08a3ac2 176 NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
jksoft 1:48f6e08a3ac2 177
jksoft 1:48f6e08a3ac2 178 NRF_RTC1->TASKS_STOP = 1;
jksoft 1:48f6e08a3ac2 179 wait(0.0000001 * MAX_RTC_TASKS_DELAY);
jksoft 1:48f6e08a3ac2 180 }
jksoft 1:48f6e08a3ac2 181
jksoft 1:48f6e08a3ac2 182
jksoft 1:48f6e08a3ac2 183 /**@brief Function for returning the current value of the RTC1 counter.
jksoft 1:48f6e08a3ac2 184 *
jksoft 1:48f6e08a3ac2 185 * @return Current value of the RTC1 counter.
jksoft 1:48f6e08a3ac2 186 */
jksoft 1:48f6e08a3ac2 187 static __INLINE uint32_t rtc1_counter_get(void)
jksoft 1:48f6e08a3ac2 188 {
jksoft 1:48f6e08a3ac2 189 return NRF_RTC1->COUNTER;
jksoft 1:48f6e08a3ac2 190 }
jksoft 1:48f6e08a3ac2 191
jksoft 1:48f6e08a3ac2 192
jksoft 1:48f6e08a3ac2 193 /**@brief Function for computing the difference between two RTC1 counter values.
jksoft 1:48f6e08a3ac2 194 *
jksoft 1:48f6e08a3ac2 195 * @return Number of ticks elapsed from ticks_old to ticks_now.
jksoft 1:48f6e08a3ac2 196 */
jksoft 1:48f6e08a3ac2 197 static __INLINE uint32_t ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old)
jksoft 1:48f6e08a3ac2 198 {
jksoft 1:48f6e08a3ac2 199 return ((ticks_now - ticks_old) & MAX_RTC_COUNTER_VAL);
jksoft 1:48f6e08a3ac2 200 }
jksoft 1:48f6e08a3ac2 201
jksoft 1:48f6e08a3ac2 202
jksoft 1:48f6e08a3ac2 203 /**@brief Function for setting the RTC1 Capture Compare register 0, and enabling the corresponding
jksoft 1:48f6e08a3ac2 204 * event.
jksoft 1:48f6e08a3ac2 205 *
jksoft 1:48f6e08a3ac2 206 * @param[in] value New value of Capture Compare register 0.
jksoft 1:48f6e08a3ac2 207 */
jksoft 1:48f6e08a3ac2 208 static __INLINE void rtc1_compare0_set(uint32_t value)
jksoft 1:48f6e08a3ac2 209 {
jksoft 1:48f6e08a3ac2 210 NRF_RTC1->CC[0] = value;
jksoft 1:48f6e08a3ac2 211 }
jksoft 1:48f6e08a3ac2 212
jksoft 1:48f6e08a3ac2 213
jksoft 1:48f6e08a3ac2 214 /**@brief Function for inserting a timer in the timer list.
jksoft 1:48f6e08a3ac2 215 *
jksoft 1:48f6e08a3ac2 216 * @param[in] timer_id Id of timer to insert.
jksoft 1:48f6e08a3ac2 217 */
jksoft 1:48f6e08a3ac2 218 static void timer_list_insert(app_timer_id_t timer_id)
jksoft 1:48f6e08a3ac2 219 {
jksoft 1:48f6e08a3ac2 220 timer_node_t * p_timer = &mp_nodes[timer_id];
jksoft 1:48f6e08a3ac2 221
jksoft 1:48f6e08a3ac2 222 if (m_timer_id_head == TIMER_NULL)
jksoft 1:48f6e08a3ac2 223 {
jksoft 1:48f6e08a3ac2 224 m_timer_id_head = timer_id;
jksoft 1:48f6e08a3ac2 225 }
jksoft 1:48f6e08a3ac2 226 else
jksoft 1:48f6e08a3ac2 227 {
jksoft 1:48f6e08a3ac2 228 if (p_timer->ticks_to_expire <= mp_nodes[m_timer_id_head].ticks_to_expire)
jksoft 1:48f6e08a3ac2 229 {
jksoft 1:48f6e08a3ac2 230 mp_nodes[m_timer_id_head].ticks_to_expire -= p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 231
jksoft 1:48f6e08a3ac2 232 p_timer->next = m_timer_id_head;
jksoft 1:48f6e08a3ac2 233 m_timer_id_head = timer_id;
jksoft 1:48f6e08a3ac2 234 }
jksoft 1:48f6e08a3ac2 235 else
jksoft 1:48f6e08a3ac2 236 {
jksoft 1:48f6e08a3ac2 237 app_timer_id_t previous;
jksoft 1:48f6e08a3ac2 238 app_timer_id_t current;
jksoft 1:48f6e08a3ac2 239 uint32_t ticks_to_expire;
jksoft 1:48f6e08a3ac2 240
jksoft 1:48f6e08a3ac2 241 ticks_to_expire = p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 242 previous = m_timer_id_head;
jksoft 1:48f6e08a3ac2 243 current = m_timer_id_head;
jksoft 1:48f6e08a3ac2 244
jksoft 1:48f6e08a3ac2 245 while ((current != TIMER_NULL) && (ticks_to_expire > mp_nodes[current].ticks_to_expire))
jksoft 1:48f6e08a3ac2 246 {
jksoft 1:48f6e08a3ac2 247 ticks_to_expire -= mp_nodes[current].ticks_to_expire;
jksoft 1:48f6e08a3ac2 248 previous = current;
jksoft 1:48f6e08a3ac2 249 current = mp_nodes[current].next;
jksoft 1:48f6e08a3ac2 250 }
jksoft 1:48f6e08a3ac2 251
jksoft 1:48f6e08a3ac2 252 if (current != TIMER_NULL)
jksoft 1:48f6e08a3ac2 253 {
jksoft 1:48f6e08a3ac2 254 mp_nodes[current].ticks_to_expire -= ticks_to_expire;
jksoft 1:48f6e08a3ac2 255 }
jksoft 1:48f6e08a3ac2 256
jksoft 1:48f6e08a3ac2 257 p_timer->ticks_to_expire = ticks_to_expire;
jksoft 1:48f6e08a3ac2 258 p_timer->next = current;
jksoft 1:48f6e08a3ac2 259 mp_nodes[previous].next = timer_id;
jksoft 1:48f6e08a3ac2 260 }
jksoft 1:48f6e08a3ac2 261 }
jksoft 1:48f6e08a3ac2 262 }
jksoft 1:48f6e08a3ac2 263
jksoft 1:48f6e08a3ac2 264
jksoft 1:48f6e08a3ac2 265 /**@brief Function for removing a timer from the timer queue.
jksoft 1:48f6e08a3ac2 266 *
jksoft 1:48f6e08a3ac2 267 * @param[in] timer_id Id of timer to remove.
jksoft 1:48f6e08a3ac2 268 */
jksoft 1:48f6e08a3ac2 269 static void timer_list_remove(app_timer_id_t timer_id)
jksoft 1:48f6e08a3ac2 270 {
jksoft 1:48f6e08a3ac2 271 app_timer_id_t previous;
jksoft 1:48f6e08a3ac2 272 app_timer_id_t current;
jksoft 1:48f6e08a3ac2 273 uint32_t timeout;
jksoft 1:48f6e08a3ac2 274
jksoft 1:48f6e08a3ac2 275 // Find the timer's position in timer list
jksoft 1:48f6e08a3ac2 276 previous = m_timer_id_head;
jksoft 1:48f6e08a3ac2 277 current = previous;
jksoft 1:48f6e08a3ac2 278
jksoft 1:48f6e08a3ac2 279 while (current != TIMER_NULL)
jksoft 1:48f6e08a3ac2 280 {
jksoft 1:48f6e08a3ac2 281 if (current == timer_id)
jksoft 1:48f6e08a3ac2 282 {
jksoft 1:48f6e08a3ac2 283 break;
jksoft 1:48f6e08a3ac2 284 }
jksoft 1:48f6e08a3ac2 285 previous = current;
jksoft 1:48f6e08a3ac2 286 current = mp_nodes[current].next;
jksoft 1:48f6e08a3ac2 287 }
jksoft 1:48f6e08a3ac2 288
jksoft 1:48f6e08a3ac2 289 // Timer not in active list
jksoft 1:48f6e08a3ac2 290 if (current == TIMER_NULL)
jksoft 1:48f6e08a3ac2 291 {
jksoft 1:48f6e08a3ac2 292 return;
jksoft 1:48f6e08a3ac2 293 }
jksoft 1:48f6e08a3ac2 294
jksoft 1:48f6e08a3ac2 295 // Timer is the first in the list
jksoft 1:48f6e08a3ac2 296 if (previous == current)
jksoft 1:48f6e08a3ac2 297 {
jksoft 1:48f6e08a3ac2 298 m_timer_id_head = mp_nodes[m_timer_id_head].next;
jksoft 1:48f6e08a3ac2 299 }
jksoft 1:48f6e08a3ac2 300
jksoft 1:48f6e08a3ac2 301 // Remaining timeout between next timeout
jksoft 1:48f6e08a3ac2 302 timeout = mp_nodes[current].ticks_to_expire;
jksoft 1:48f6e08a3ac2 303
jksoft 1:48f6e08a3ac2 304 // Link previous timer with next of this timer, i.e. removing the timer from list
jksoft 1:48f6e08a3ac2 305 mp_nodes[previous].next = mp_nodes[current].next;
jksoft 1:48f6e08a3ac2 306
jksoft 1:48f6e08a3ac2 307 // If this is not the last timer, increment the next timer by this timer timeout
jksoft 1:48f6e08a3ac2 308 current = mp_nodes[previous].next;
jksoft 1:48f6e08a3ac2 309 if (current != TIMER_NULL)
jksoft 1:48f6e08a3ac2 310 {
jksoft 1:48f6e08a3ac2 311 mp_nodes[current].ticks_to_expire += timeout;
jksoft 1:48f6e08a3ac2 312 }
jksoft 1:48f6e08a3ac2 313 }
jksoft 1:48f6e08a3ac2 314
jksoft 1:48f6e08a3ac2 315
jksoft 1:48f6e08a3ac2 316 /**@brief Function for scheduling a check for timeouts by generating a RTC1 interrupt.
jksoft 1:48f6e08a3ac2 317 */
jksoft 1:48f6e08a3ac2 318 static void timer_timeouts_check_sched(void)
jksoft 1:48f6e08a3ac2 319 {
jksoft 1:48f6e08a3ac2 320 NVIC_SetPendingIRQ(RTC1_IRQn);
jksoft 1:48f6e08a3ac2 321 }
jksoft 1:48f6e08a3ac2 322
jksoft 1:48f6e08a3ac2 323
jksoft 1:48f6e08a3ac2 324 /**@brief Function for scheduling a timer list update by generating a SWI0 interrupt.
jksoft 1:48f6e08a3ac2 325 */
jksoft 1:48f6e08a3ac2 326 static void timer_list_handler_sched(void)
jksoft 1:48f6e08a3ac2 327 {
jksoft 1:48f6e08a3ac2 328 NVIC_SetPendingIRQ(SWI0_IRQn);
jksoft 1:48f6e08a3ac2 329 }
jksoft 1:48f6e08a3ac2 330
jksoft 1:48f6e08a3ac2 331
jksoft 1:48f6e08a3ac2 332 /**@brief Function for executing an application timeout handler, either by calling it directly, or
jksoft 1:48f6e08a3ac2 333 * by passing an event to the @ref app_scheduler.
jksoft 1:48f6e08a3ac2 334 *
jksoft 1:48f6e08a3ac2 335 * @param[in] p_timer Pointer to expired timer.
jksoft 1:48f6e08a3ac2 336 */
jksoft 1:48f6e08a3ac2 337 static void timeout_handler_exec(timer_node_t * p_timer)
jksoft 1:48f6e08a3ac2 338 {
jksoft 1:48f6e08a3ac2 339 if (m_evt_schedule_func != NULL)
jksoft 1:48f6e08a3ac2 340 {
jksoft 1:48f6e08a3ac2 341 uint32_t err_code = m_evt_schedule_func(p_timer->p_timeout_handler, p_timer->p_context);
jksoft 1:48f6e08a3ac2 342 APP_ERROR_CHECK(err_code);
jksoft 1:48f6e08a3ac2 343 }
jksoft 1:48f6e08a3ac2 344 else
jksoft 1:48f6e08a3ac2 345 {
jksoft 1:48f6e08a3ac2 346 p_timer->p_timeout_handler(p_timer->p_context);
jksoft 1:48f6e08a3ac2 347 }
jksoft 1:48f6e08a3ac2 348 }
jksoft 1:48f6e08a3ac2 349
jksoft 1:48f6e08a3ac2 350
jksoft 1:48f6e08a3ac2 351 /**@brief Function for checking for expired timers.
jksoft 1:48f6e08a3ac2 352 */
jksoft 1:48f6e08a3ac2 353 static void timer_timeouts_check(void)
jksoft 1:48f6e08a3ac2 354 {
jksoft 1:48f6e08a3ac2 355 // Handle expired of timer
jksoft 1:48f6e08a3ac2 356 if (m_timer_id_head != TIMER_NULL)
jksoft 1:48f6e08a3ac2 357 {
jksoft 1:48f6e08a3ac2 358 app_timer_id_t timer_id;
jksoft 1:48f6e08a3ac2 359 uint32_t ticks_elapsed;
jksoft 1:48f6e08a3ac2 360 uint32_t ticks_expired;
jksoft 1:48f6e08a3ac2 361
jksoft 1:48f6e08a3ac2 362 // Initialize actual elapsed ticks being consumed to 0
jksoft 1:48f6e08a3ac2 363 ticks_expired = 0;
jksoft 1:48f6e08a3ac2 364
jksoft 1:48f6e08a3ac2 365 // ticks_elapsed is collected here, job will use it
jksoft 1:48f6e08a3ac2 366 ticks_elapsed = ticks_diff_get(rtc1_counter_get(), m_ticks_latest);
jksoft 1:48f6e08a3ac2 367
jksoft 1:48f6e08a3ac2 368 // Auto variable containing the head of timers expiring
jksoft 1:48f6e08a3ac2 369 timer_id = m_timer_id_head;
jksoft 1:48f6e08a3ac2 370
jksoft 1:48f6e08a3ac2 371 // Expire all timers within ticks_elapsed and collect ticks_expired
jksoft 1:48f6e08a3ac2 372 while (timer_id != TIMER_NULL)
jksoft 1:48f6e08a3ac2 373 {
jksoft 1:48f6e08a3ac2 374 timer_node_t * p_timer;
jksoft 1:48f6e08a3ac2 375
jksoft 1:48f6e08a3ac2 376 // Auto variable for current timer node
jksoft 1:48f6e08a3ac2 377 p_timer = &mp_nodes[timer_id];
jksoft 1:48f6e08a3ac2 378
jksoft 1:48f6e08a3ac2 379 // Do nothing if timer did not expire
jksoft 1:48f6e08a3ac2 380 if (ticks_elapsed < p_timer->ticks_to_expire)
jksoft 1:48f6e08a3ac2 381 {
jksoft 1:48f6e08a3ac2 382 break;
jksoft 1:48f6e08a3ac2 383 }
jksoft 1:48f6e08a3ac2 384
jksoft 1:48f6e08a3ac2 385 // Decrement ticks_elapsed and collect expired ticks
jksoft 1:48f6e08a3ac2 386 ticks_elapsed -= p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 387 ticks_expired += p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 388
jksoft 1:48f6e08a3ac2 389 // Move to next timer
jksoft 1:48f6e08a3ac2 390 timer_id = p_timer->next;
jksoft 1:48f6e08a3ac2 391
jksoft 1:48f6e08a3ac2 392 // Execute Task
jksoft 1:48f6e08a3ac2 393 timeout_handler_exec(p_timer);
jksoft 1:48f6e08a3ac2 394 }
jksoft 1:48f6e08a3ac2 395
jksoft 1:48f6e08a3ac2 396 // Prepare to queue the ticks expired in the m_ticks_elapsed queue.
jksoft 1:48f6e08a3ac2 397 if (m_ticks_elapsed_q_read_ind == m_ticks_elapsed_q_write_ind)
jksoft 1:48f6e08a3ac2 398 {
jksoft 1:48f6e08a3ac2 399 // The read index of the queue is equal to the write index. This means the new
jksoft 1:48f6e08a3ac2 400 // value of ticks_expired should be stored at a new location in the m_ticks_elapsed
jksoft 1:48f6e08a3ac2 401 // queue (which is implemented as a double buffer).
jksoft 1:48f6e08a3ac2 402
jksoft 1:48f6e08a3ac2 403 // Check if there will be a queue overflow.
jksoft 1:48f6e08a3ac2 404 if (++m_ticks_elapsed_q_write_ind == CONTEXT_QUEUE_SIZE_MAX)
jksoft 1:48f6e08a3ac2 405 {
jksoft 1:48f6e08a3ac2 406 // There will be a queue overflow. Hence the write index should point to the start
jksoft 1:48f6e08a3ac2 407 // of the queue.
jksoft 1:48f6e08a3ac2 408 m_ticks_elapsed_q_write_ind = 0;
jksoft 1:48f6e08a3ac2 409 }
jksoft 1:48f6e08a3ac2 410 }
jksoft 1:48f6e08a3ac2 411
jksoft 1:48f6e08a3ac2 412 // Queue the ticks expired.
jksoft 1:48f6e08a3ac2 413 m_ticks_elapsed[m_ticks_elapsed_q_write_ind] = ticks_expired;
jksoft 1:48f6e08a3ac2 414
jksoft 1:48f6e08a3ac2 415 timer_list_handler_sched();
jksoft 1:48f6e08a3ac2 416 }
jksoft 1:48f6e08a3ac2 417 }
jksoft 1:48f6e08a3ac2 418
jksoft 1:48f6e08a3ac2 419
jksoft 1:48f6e08a3ac2 420 /**@brief Function for acquiring the number of ticks elapsed.
jksoft 1:48f6e08a3ac2 421 *
jksoft 1:48f6e08a3ac2 422 * @param[out] p_ticks_elapsed Number of ticks elapsed.
jksoft 1:48f6e08a3ac2 423 *
jksoft 1:48f6e08a3ac2 424 * @return TRUE if elapsed ticks was read from queue, FALSE otherwise.
jksoft 1:48f6e08a3ac2 425 */
jksoft 1:48f6e08a3ac2 426 static bool elapsed_ticks_acquire(uint32_t * p_ticks_elapsed)
jksoft 1:48f6e08a3ac2 427 {
jksoft 1:48f6e08a3ac2 428 // Pick the elapsed value from queue
jksoft 1:48f6e08a3ac2 429 if (m_ticks_elapsed_q_read_ind != m_ticks_elapsed_q_write_ind)
jksoft 1:48f6e08a3ac2 430 {
jksoft 1:48f6e08a3ac2 431 // Dequeue elapsed value
jksoft 1:48f6e08a3ac2 432 m_ticks_elapsed_q_read_ind++;
jksoft 1:48f6e08a3ac2 433 if (m_ticks_elapsed_q_read_ind == CONTEXT_QUEUE_SIZE_MAX)
jksoft 1:48f6e08a3ac2 434 {
jksoft 1:48f6e08a3ac2 435 m_ticks_elapsed_q_read_ind = 0;
jksoft 1:48f6e08a3ac2 436 }
jksoft 1:48f6e08a3ac2 437
jksoft 1:48f6e08a3ac2 438 *p_ticks_elapsed = m_ticks_elapsed[m_ticks_elapsed_q_read_ind];
jksoft 1:48f6e08a3ac2 439
jksoft 1:48f6e08a3ac2 440 m_ticks_latest += *p_ticks_elapsed;
jksoft 1:48f6e08a3ac2 441 m_ticks_latest &= MAX_RTC_COUNTER_VAL;
jksoft 1:48f6e08a3ac2 442
jksoft 1:48f6e08a3ac2 443 return true;
jksoft 1:48f6e08a3ac2 444 }
jksoft 1:48f6e08a3ac2 445 else
jksoft 1:48f6e08a3ac2 446 {
jksoft 1:48f6e08a3ac2 447 // No elapsed value in queue
jksoft 1:48f6e08a3ac2 448 *p_ticks_elapsed = 0;
jksoft 1:48f6e08a3ac2 449 return false;
jksoft 1:48f6e08a3ac2 450 }
jksoft 1:48f6e08a3ac2 451 }
jksoft 1:48f6e08a3ac2 452
jksoft 1:48f6e08a3ac2 453
jksoft 1:48f6e08a3ac2 454 /**@brief Function for handling the timer list deletions.
jksoft 1:48f6e08a3ac2 455 *
jksoft 1:48f6e08a3ac2 456 * @return TRUE if Capture Compare register must be updated, FALSE otherwise.
jksoft 1:48f6e08a3ac2 457 */
jksoft 1:48f6e08a3ac2 458 static bool list_deletions_handler(void)
jksoft 1:48f6e08a3ac2 459 {
jksoft 1:48f6e08a3ac2 460 app_timer_id_t timer_id_old_head;
jksoft 1:48f6e08a3ac2 461 uint8_t user_id;
jksoft 1:48f6e08a3ac2 462
jksoft 1:48f6e08a3ac2 463 // Remember the old head, so as to decide if new compare needs to be set
jksoft 1:48f6e08a3ac2 464 timer_id_old_head = m_timer_id_head;
jksoft 1:48f6e08a3ac2 465
jksoft 1:48f6e08a3ac2 466 user_id = m_user_array_size;
jksoft 1:48f6e08a3ac2 467 while (user_id--)
jksoft 1:48f6e08a3ac2 468 {
jksoft 1:48f6e08a3ac2 469 timer_user_t * p_user = &mp_users[user_id];
jksoft 1:48f6e08a3ac2 470 uint8_t user_ops_first = p_user->first;
jksoft 1:48f6e08a3ac2 471
jksoft 1:48f6e08a3ac2 472 while (user_ops_first != p_user->last)
jksoft 1:48f6e08a3ac2 473 {
jksoft 1:48f6e08a3ac2 474 timer_node_t * p_timer;
jksoft 1:48f6e08a3ac2 475 timer_user_op_t * p_user_op = &p_user->p_user_op_queue[user_ops_first];
jksoft 1:48f6e08a3ac2 476
jksoft 1:48f6e08a3ac2 477 // Traverse to next operation in queue
jksoft 1:48f6e08a3ac2 478 user_ops_first++;
jksoft 1:48f6e08a3ac2 479 if (user_ops_first == p_user->user_op_queue_size)
jksoft 1:48f6e08a3ac2 480 {
jksoft 1:48f6e08a3ac2 481 user_ops_first = 0;
jksoft 1:48f6e08a3ac2 482 }
jksoft 1:48f6e08a3ac2 483
jksoft 1:48f6e08a3ac2 484 switch (p_user_op->op_type)
jksoft 1:48f6e08a3ac2 485 {
jksoft 1:48f6e08a3ac2 486 case TIMER_USER_OP_TYPE_STOP:
jksoft 1:48f6e08a3ac2 487 // Delete node if timer is running
jksoft 1:48f6e08a3ac2 488 p_timer = &mp_nodes[p_user_op->timer_id];
jksoft 1:48f6e08a3ac2 489 if (p_timer->is_running)
jksoft 1:48f6e08a3ac2 490 {
jksoft 1:48f6e08a3ac2 491 timer_list_remove(p_user_op->timer_id);
jksoft 1:48f6e08a3ac2 492 p_timer->is_running = false;
jksoft 1:48f6e08a3ac2 493 }
jksoft 1:48f6e08a3ac2 494 break;
jksoft 1:48f6e08a3ac2 495
jksoft 1:48f6e08a3ac2 496 case TIMER_USER_OP_TYPE_STOP_ALL:
jksoft 1:48f6e08a3ac2 497 // Delete list of running timers, and mark all timers as not running
jksoft 1:48f6e08a3ac2 498 while (m_timer_id_head != TIMER_NULL)
jksoft 1:48f6e08a3ac2 499 {
jksoft 1:48f6e08a3ac2 500 timer_node_t * p_head = &mp_nodes[m_timer_id_head];
jksoft 1:48f6e08a3ac2 501
jksoft 1:48f6e08a3ac2 502 p_head->is_running = false;
jksoft 1:48f6e08a3ac2 503 m_timer_id_head = p_head->next;
jksoft 1:48f6e08a3ac2 504 }
jksoft 1:48f6e08a3ac2 505 break;
jksoft 1:48f6e08a3ac2 506
jksoft 1:48f6e08a3ac2 507 default:
jksoft 1:48f6e08a3ac2 508 // No implementation needed.
jksoft 1:48f6e08a3ac2 509 break;
jksoft 1:48f6e08a3ac2 510 }
jksoft 1:48f6e08a3ac2 511 }
jksoft 1:48f6e08a3ac2 512 }
jksoft 1:48f6e08a3ac2 513
jksoft 1:48f6e08a3ac2 514 // Detect change in head of the list
jksoft 1:48f6e08a3ac2 515 return (m_timer_id_head != timer_id_old_head);
jksoft 1:48f6e08a3ac2 516 }
jksoft 1:48f6e08a3ac2 517
jksoft 1:48f6e08a3ac2 518
jksoft 1:48f6e08a3ac2 519 /**@brief Function for updating the timer list for expired timers.
jksoft 1:48f6e08a3ac2 520 *
jksoft 1:48f6e08a3ac2 521 * @param[in] ticks_elapsed Number of elapsed ticks.
jksoft 1:48f6e08a3ac2 522 * @param[in] ticks_previous Previous known value of the RTC counter.
jksoft 1:48f6e08a3ac2 523 * @param[out] p_restart_list_head List of repeating timers to be restarted.
jksoft 1:48f6e08a3ac2 524 */
jksoft 1:48f6e08a3ac2 525 static void expired_timers_handler(uint32_t ticks_elapsed,
jksoft 1:48f6e08a3ac2 526 uint32_t ticks_previous,
jksoft 1:48f6e08a3ac2 527 app_timer_id_t * p_restart_list_head)
jksoft 1:48f6e08a3ac2 528 {
jksoft 1:48f6e08a3ac2 529 uint32_t ticks_expired = 0;
jksoft 1:48f6e08a3ac2 530
jksoft 1:48f6e08a3ac2 531 while (m_timer_id_head != TIMER_NULL)
jksoft 1:48f6e08a3ac2 532 {
jksoft 1:48f6e08a3ac2 533 timer_node_t * p_timer;
jksoft 1:48f6e08a3ac2 534 app_timer_id_t id_expired;
jksoft 1:48f6e08a3ac2 535
jksoft 1:48f6e08a3ac2 536 // Auto variable for current timer node
jksoft 1:48f6e08a3ac2 537 p_timer = &mp_nodes[m_timer_id_head];
jksoft 1:48f6e08a3ac2 538
jksoft 1:48f6e08a3ac2 539 // Do nothing if timer did not expire
jksoft 1:48f6e08a3ac2 540 if (ticks_elapsed < p_timer->ticks_to_expire)
jksoft 1:48f6e08a3ac2 541 {
jksoft 1:48f6e08a3ac2 542 p_timer->ticks_to_expire -= ticks_elapsed;
jksoft 1:48f6e08a3ac2 543 break;
jksoft 1:48f6e08a3ac2 544 }
jksoft 1:48f6e08a3ac2 545
jksoft 1:48f6e08a3ac2 546 // Decrement ticks_elapsed and collect expired ticks
jksoft 1:48f6e08a3ac2 547 ticks_elapsed -= p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 548 ticks_expired += p_timer->ticks_to_expire;
jksoft 1:48f6e08a3ac2 549
jksoft 1:48f6e08a3ac2 550 // Timer expired, set ticks_to_expire zero
jksoft 1:48f6e08a3ac2 551 p_timer->ticks_to_expire = 0;
jksoft 1:48f6e08a3ac2 552 p_timer->is_running = false;
jksoft 1:48f6e08a3ac2 553
jksoft 1:48f6e08a3ac2 554 // Remove the expired timer from head
jksoft 1:48f6e08a3ac2 555 id_expired = m_timer_id_head;
jksoft 1:48f6e08a3ac2 556 m_timer_id_head = p_timer->next;
jksoft 1:48f6e08a3ac2 557
jksoft 1:48f6e08a3ac2 558 // Timer will be restarted if periodic
jksoft 1:48f6e08a3ac2 559 if (p_timer->ticks_periodic_interval != 0)
jksoft 1:48f6e08a3ac2 560 {
jksoft 1:48f6e08a3ac2 561 p_timer->ticks_at_start = (ticks_previous + ticks_expired) & MAX_RTC_COUNTER_VAL;
jksoft 1:48f6e08a3ac2 562 p_timer->ticks_first_interval = p_timer->ticks_periodic_interval;
jksoft 1:48f6e08a3ac2 563 p_timer->next = *p_restart_list_head;
jksoft 1:48f6e08a3ac2 564 *p_restart_list_head = id_expired;
jksoft 1:48f6e08a3ac2 565 }
jksoft 1:48f6e08a3ac2 566 }
jksoft 1:48f6e08a3ac2 567 }
jksoft 1:48f6e08a3ac2 568
jksoft 1:48f6e08a3ac2 569
jksoft 1:48f6e08a3ac2 570 /**@brief Function for handling timer list insertions.
jksoft 1:48f6e08a3ac2 571 *
jksoft 1:48f6e08a3ac2 572 * @param[in] p_restart_list_head List of repeating timers to be restarted.
jksoft 1:48f6e08a3ac2 573 *
jksoft 1:48f6e08a3ac2 574 * @return TRUE if Capture Compare register must be updated, FALSE otherwise.
jksoft 1:48f6e08a3ac2 575 */
jksoft 1:48f6e08a3ac2 576 static bool list_insertions_handler(app_timer_id_t restart_list_head)
jksoft 1:48f6e08a3ac2 577 {
jksoft 1:48f6e08a3ac2 578 app_timer_id_t timer_id_old_head;
jksoft 1:48f6e08a3ac2 579 uint8_t user_id;
jksoft 1:48f6e08a3ac2 580
jksoft 1:48f6e08a3ac2 581 // Remember the old head, so as to decide if new compare needs to be set
jksoft 1:48f6e08a3ac2 582 timer_id_old_head = m_timer_id_head;
jksoft 1:48f6e08a3ac2 583
jksoft 1:48f6e08a3ac2 584 user_id = m_user_array_size;
jksoft 1:48f6e08a3ac2 585 while (user_id--)
jksoft 1:48f6e08a3ac2 586 {
jksoft 1:48f6e08a3ac2 587 timer_user_t * p_user = &mp_users[user_id];
jksoft 1:48f6e08a3ac2 588
jksoft 1:48f6e08a3ac2 589 // Handle insertions of timers
jksoft 1:48f6e08a3ac2 590 while ((restart_list_head != TIMER_NULL) || (p_user->first != p_user->last))
jksoft 1:48f6e08a3ac2 591 {
jksoft 1:48f6e08a3ac2 592 app_timer_id_t id_start;
jksoft 1:48f6e08a3ac2 593 timer_node_t * p_timer;
jksoft 1:48f6e08a3ac2 594
jksoft 1:48f6e08a3ac2 595 if (restart_list_head != TIMER_NULL)
jksoft 1:48f6e08a3ac2 596 {
jksoft 1:48f6e08a3ac2 597 id_start = restart_list_head;
jksoft 1:48f6e08a3ac2 598 p_timer = &mp_nodes[id_start];
jksoft 1:48f6e08a3ac2 599 restart_list_head = p_timer->next;
jksoft 1:48f6e08a3ac2 600 }
jksoft 1:48f6e08a3ac2 601 else
jksoft 1:48f6e08a3ac2 602 {
jksoft 1:48f6e08a3ac2 603 timer_user_op_t * p_user_op = &p_user->p_user_op_queue[p_user->first];
jksoft 1:48f6e08a3ac2 604
jksoft 1:48f6e08a3ac2 605 p_user->first++;
jksoft 1:48f6e08a3ac2 606 if (p_user->first == p_user->user_op_queue_size)
jksoft 1:48f6e08a3ac2 607 {
jksoft 1:48f6e08a3ac2 608 p_user->first = 0;
jksoft 1:48f6e08a3ac2 609 }
jksoft 1:48f6e08a3ac2 610
jksoft 1:48f6e08a3ac2 611 id_start = p_user_op->timer_id;
jksoft 1:48f6e08a3ac2 612 p_timer = &mp_nodes[id_start];
jksoft 1:48f6e08a3ac2 613
jksoft 1:48f6e08a3ac2 614 if ((p_user_op->op_type != TIMER_USER_OP_TYPE_START) || p_timer->is_running)
jksoft 1:48f6e08a3ac2 615 {
jksoft 1:48f6e08a3ac2 616 continue;
jksoft 1:48f6e08a3ac2 617 }
jksoft 1:48f6e08a3ac2 618
jksoft 1:48f6e08a3ac2 619 p_timer->ticks_at_start = p_user_op->params.start.ticks_at_start;
jksoft 1:48f6e08a3ac2 620 p_timer->ticks_first_interval = p_user_op->params.start.ticks_first_interval;
jksoft 1:48f6e08a3ac2 621 p_timer->ticks_periodic_interval = p_user_op->params.start.ticks_periodic_interval;
jksoft 1:48f6e08a3ac2 622 p_timer->p_context = p_user_op->params.start.p_context;
jksoft 1:48f6e08a3ac2 623 }
jksoft 1:48f6e08a3ac2 624
jksoft 1:48f6e08a3ac2 625 // Prepare the node to be inserted
jksoft 1:48f6e08a3ac2 626 if (
jksoft 1:48f6e08a3ac2 627 ((p_timer->ticks_at_start - m_ticks_latest) & MAX_RTC_COUNTER_VAL)
jksoft 1:48f6e08a3ac2 628 <
jksoft 1:48f6e08a3ac2 629 (MAX_RTC_COUNTER_VAL / 2)
jksoft 1:48f6e08a3ac2 630 )
jksoft 1:48f6e08a3ac2 631 {
jksoft 1:48f6e08a3ac2 632 p_timer->ticks_to_expire = ticks_diff_get(p_timer->ticks_at_start, m_ticks_latest) +
jksoft 1:48f6e08a3ac2 633 p_timer->ticks_first_interval;
jksoft 1:48f6e08a3ac2 634 }
jksoft 1:48f6e08a3ac2 635 else
jksoft 1:48f6e08a3ac2 636 {
jksoft 1:48f6e08a3ac2 637 uint32_t delta_current_start;
jksoft 1:48f6e08a3ac2 638
jksoft 1:48f6e08a3ac2 639 delta_current_start = ticks_diff_get(m_ticks_latest, p_timer->ticks_at_start);
jksoft 1:48f6e08a3ac2 640 if (p_timer->ticks_first_interval > delta_current_start)
jksoft 1:48f6e08a3ac2 641 {
jksoft 1:48f6e08a3ac2 642 p_timer->ticks_to_expire = p_timer->ticks_first_interval - delta_current_start;
jksoft 1:48f6e08a3ac2 643 }
jksoft 1:48f6e08a3ac2 644 else
jksoft 1:48f6e08a3ac2 645 {
jksoft 1:48f6e08a3ac2 646 p_timer->ticks_to_expire = 0;
jksoft 1:48f6e08a3ac2 647 }
jksoft 1:48f6e08a3ac2 648 }
jksoft 1:48f6e08a3ac2 649
jksoft 1:48f6e08a3ac2 650 p_timer->ticks_at_start = 0;
jksoft 1:48f6e08a3ac2 651 p_timer->ticks_first_interval = 0;
jksoft 1:48f6e08a3ac2 652 p_timer->is_running = true;
jksoft 1:48f6e08a3ac2 653 p_timer->next = TIMER_NULL;
jksoft 1:48f6e08a3ac2 654
jksoft 1:48f6e08a3ac2 655 // Insert into list
jksoft 1:48f6e08a3ac2 656 timer_list_insert(id_start);
jksoft 1:48f6e08a3ac2 657 }
jksoft 1:48f6e08a3ac2 658 }
jksoft 1:48f6e08a3ac2 659
jksoft 1:48f6e08a3ac2 660 return (m_timer_id_head != timer_id_old_head);
jksoft 1:48f6e08a3ac2 661 }
jksoft 1:48f6e08a3ac2 662
jksoft 1:48f6e08a3ac2 663
jksoft 1:48f6e08a3ac2 664 /**@brief Function for updating the Capture Compare register.
jksoft 1:48f6e08a3ac2 665 */
jksoft 1:48f6e08a3ac2 666 static void compare_reg_update(app_timer_id_t timer_id_head_old)
jksoft 1:48f6e08a3ac2 667 {
jksoft 1:48f6e08a3ac2 668 // Setup the timeout for timers on the head of the list
jksoft 1:48f6e08a3ac2 669 if (m_timer_id_head != TIMER_NULL)
jksoft 1:48f6e08a3ac2 670 {
jksoft 1:48f6e08a3ac2 671 uint32_t ticks_to_expire = mp_nodes[m_timer_id_head].ticks_to_expire;
jksoft 1:48f6e08a3ac2 672 uint32_t pre_counter_val = rtc1_counter_get();
jksoft 1:48f6e08a3ac2 673 uint32_t cc = m_ticks_latest;
jksoft 1:48f6e08a3ac2 674 uint32_t ticks_elapsed = ticks_diff_get(pre_counter_val, cc) + RTC_COMPARE_OFFSET_MIN;
jksoft 1:48f6e08a3ac2 675
jksoft 1:48f6e08a3ac2 676 if (timer_id_head_old == TIMER_NULL)
jksoft 1:48f6e08a3ac2 677 {
jksoft 1:48f6e08a3ac2 678 // No timers were already running, start RTC
jksoft 1:48f6e08a3ac2 679 rtc1_start();
jksoft 1:48f6e08a3ac2 680 }
jksoft 1:48f6e08a3ac2 681
jksoft 1:48f6e08a3ac2 682 cc += (ticks_elapsed < ticks_to_expire) ? ticks_to_expire : ticks_elapsed;
jksoft 1:48f6e08a3ac2 683 cc &= MAX_RTC_COUNTER_VAL;
jksoft 1:48f6e08a3ac2 684
jksoft 1:48f6e08a3ac2 685 rtc1_compare0_set(cc);
jksoft 1:48f6e08a3ac2 686
jksoft 1:48f6e08a3ac2 687 uint32_t post_counter_val = rtc1_counter_get();
jksoft 1:48f6e08a3ac2 688
jksoft 1:48f6e08a3ac2 689 if (
jksoft 1:48f6e08a3ac2 690 (ticks_diff_get(post_counter_val, pre_counter_val) + RTC_COMPARE_OFFSET_MIN)
jksoft 1:48f6e08a3ac2 691 >
jksoft 1:48f6e08a3ac2 692 ticks_diff_get(cc, pre_counter_val)
jksoft 1:48f6e08a3ac2 693 )
jksoft 1:48f6e08a3ac2 694 {
jksoft 1:48f6e08a3ac2 695 // When this happens the COMPARE event may not be triggered by the RTC.
jksoft 1:48f6e08a3ac2 696 // The nRF51 Series User Specification states that if the COUNTER value is N
jksoft 1:48f6e08a3ac2 697 // (i.e post_counter_val = N), writing N or N+1 to a CC register may not trigger a
jksoft 1:48f6e08a3ac2 698 // COMPARE event. Hence the RTC interrupt is forcefully pended by calling the following
jksoft 1:48f6e08a3ac2 699 // function.
jksoft 1:48f6e08a3ac2 700 timer_timeouts_check_sched();
jksoft 1:48f6e08a3ac2 701 }
jksoft 1:48f6e08a3ac2 702 }
jksoft 1:48f6e08a3ac2 703 else
jksoft 1:48f6e08a3ac2 704 {
jksoft 1:48f6e08a3ac2 705 // No timers are running, stop RTC
jksoft 1:48f6e08a3ac2 706 rtc1_stop();
jksoft 1:48f6e08a3ac2 707 }
jksoft 1:48f6e08a3ac2 708 }
jksoft 1:48f6e08a3ac2 709
jksoft 1:48f6e08a3ac2 710
jksoft 1:48f6e08a3ac2 711 /**@brief Function for handling changes to the timer list.
jksoft 1:48f6e08a3ac2 712 */
jksoft 1:48f6e08a3ac2 713 static void timer_list_handler(void)
jksoft 1:48f6e08a3ac2 714 {
jksoft 1:48f6e08a3ac2 715 app_timer_id_t restart_list_head = TIMER_NULL;
jksoft 1:48f6e08a3ac2 716 uint32_t ticks_elapsed;
jksoft 1:48f6e08a3ac2 717 uint32_t ticks_previous;
jksoft 1:48f6e08a3ac2 718 bool ticks_have_elapsed;
jksoft 1:48f6e08a3ac2 719 bool compare_update;
jksoft 1:48f6e08a3ac2 720 app_timer_id_t timer_id_head_old;
jksoft 1:48f6e08a3ac2 721
jksoft 1:48f6e08a3ac2 722 // Back up the previous known tick and previous list head
jksoft 1:48f6e08a3ac2 723 ticks_previous = m_ticks_latest;
jksoft 1:48f6e08a3ac2 724 timer_id_head_old = m_timer_id_head;
jksoft 1:48f6e08a3ac2 725
jksoft 1:48f6e08a3ac2 726 // Get number of elapsed ticks
jksoft 1:48f6e08a3ac2 727 ticks_have_elapsed = elapsed_ticks_acquire(&ticks_elapsed);
jksoft 1:48f6e08a3ac2 728
jksoft 1:48f6e08a3ac2 729 // Handle list deletions
jksoft 1:48f6e08a3ac2 730 compare_update = list_deletions_handler();
jksoft 1:48f6e08a3ac2 731
jksoft 1:48f6e08a3ac2 732 // Handle expired timers
jksoft 1:48f6e08a3ac2 733 if (ticks_have_elapsed)
jksoft 1:48f6e08a3ac2 734 {
jksoft 1:48f6e08a3ac2 735 expired_timers_handler(ticks_elapsed, ticks_previous, &restart_list_head);
jksoft 1:48f6e08a3ac2 736 compare_update = true;
jksoft 1:48f6e08a3ac2 737 }
jksoft 1:48f6e08a3ac2 738
jksoft 1:48f6e08a3ac2 739 // Handle list insertions
jksoft 1:48f6e08a3ac2 740 if (list_insertions_handler(restart_list_head))
jksoft 1:48f6e08a3ac2 741 {
jksoft 1:48f6e08a3ac2 742 compare_update = true;
jksoft 1:48f6e08a3ac2 743 }
jksoft 1:48f6e08a3ac2 744
jksoft 1:48f6e08a3ac2 745 // Update compare register if necessary
jksoft 1:48f6e08a3ac2 746 if (compare_update)
jksoft 1:48f6e08a3ac2 747 {
jksoft 1:48f6e08a3ac2 748 compare_reg_update(timer_id_head_old);
jksoft 1:48f6e08a3ac2 749 }
jksoft 1:48f6e08a3ac2 750 }
jksoft 1:48f6e08a3ac2 751
jksoft 1:48f6e08a3ac2 752
jksoft 1:48f6e08a3ac2 753 /**@brief Function for enqueueing a new operations queue entry.
jksoft 1:48f6e08a3ac2 754 *
jksoft 1:48f6e08a3ac2 755 * @param[in] p_user User that the entry is to be enqueued for.
jksoft 1:48f6e08a3ac2 756 * @param[in] last_index Index of the next last index to be enqueued.
jksoft 1:48f6e08a3ac2 757 */
jksoft 1:48f6e08a3ac2 758 static void user_op_enque(timer_user_t * p_user, app_timer_id_t last_index)
jksoft 1:48f6e08a3ac2 759 {
jksoft 1:48f6e08a3ac2 760 p_user->last = last_index;
jksoft 1:48f6e08a3ac2 761 }
jksoft 1:48f6e08a3ac2 762
jksoft 1:48f6e08a3ac2 763
jksoft 1:48f6e08a3ac2 764 /**@brief Function for allocating a new operations queue entry.
jksoft 1:48f6e08a3ac2 765 *
jksoft 1:48f6e08a3ac2 766 * @param[in] p_user User that the entry is to be allocated for.
jksoft 1:48f6e08a3ac2 767 * @param[out] p_last_index Index of the next last index to be enqueued.
jksoft 1:48f6e08a3ac2 768 *
jksoft 1:48f6e08a3ac2 769 * @return Pointer to allocated queue entry, or NULL if queue is full.
jksoft 1:48f6e08a3ac2 770 */
jksoft 1:48f6e08a3ac2 771 static timer_user_op_t * user_op_alloc(timer_user_t * p_user, app_timer_id_t * p_last_index)
jksoft 1:48f6e08a3ac2 772 {
jksoft 1:48f6e08a3ac2 773 app_timer_id_t last;
jksoft 1:48f6e08a3ac2 774 timer_user_op_t * p_user_op;
jksoft 1:48f6e08a3ac2 775
jksoft 1:48f6e08a3ac2 776 last = p_user->last + 1;
jksoft 1:48f6e08a3ac2 777 if (last == p_user->user_op_queue_size)
jksoft 1:48f6e08a3ac2 778 {
jksoft 1:48f6e08a3ac2 779 // Overflow case.
jksoft 1:48f6e08a3ac2 780 last = 0;
jksoft 1:48f6e08a3ac2 781 }
jksoft 1:48f6e08a3ac2 782 if (last == p_user->first)
jksoft 1:48f6e08a3ac2 783 {
jksoft 1:48f6e08a3ac2 784 // Queue is full.
jksoft 1:48f6e08a3ac2 785 return NULL;
jksoft 1:48f6e08a3ac2 786 }
jksoft 1:48f6e08a3ac2 787
jksoft 1:48f6e08a3ac2 788 *p_last_index = last;
jksoft 1:48f6e08a3ac2 789 p_user_op = &p_user->p_user_op_queue[p_user->last];
jksoft 1:48f6e08a3ac2 790
jksoft 1:48f6e08a3ac2 791 return p_user_op;
jksoft 1:48f6e08a3ac2 792 }
jksoft 1:48f6e08a3ac2 793
jksoft 1:48f6e08a3ac2 794
jksoft 1:48f6e08a3ac2 795 /**@brief Function for scheduling a Timer Start operation.
jksoft 1:48f6e08a3ac2 796 *
jksoft 1:48f6e08a3ac2 797 * @param[in] user_id Id of user calling this function.
jksoft 1:48f6e08a3ac2 798 * @param[in] timer_id Id of timer to start.
jksoft 1:48f6e08a3ac2 799 * @param[in] timeout_initial Time (in ticks) to first timer expiry.
jksoft 1:48f6e08a3ac2 800 * @param[in] timeout_periodic Time (in ticks) between periodic expiries.
jksoft 1:48f6e08a3ac2 801 * @param[in] p_context General purpose pointer. Will be passed to the timeout handler when
jksoft 1:48f6e08a3ac2 802 * the timer expires.
jksoft 1:48f6e08a3ac2 803 * @return NRF_SUCCESS on success, otherwise an error code.
jksoft 1:48f6e08a3ac2 804 */
jksoft 1:48f6e08a3ac2 805 static uint32_t timer_start_op_schedule(timer_user_id_t user_id,
jksoft 1:48f6e08a3ac2 806 app_timer_id_t timer_id,
jksoft 1:48f6e08a3ac2 807 uint32_t timeout_initial,
jksoft 1:48f6e08a3ac2 808 uint32_t timeout_periodic,
jksoft 1:48f6e08a3ac2 809 void * p_context)
jksoft 1:48f6e08a3ac2 810 {
jksoft 1:48f6e08a3ac2 811 app_timer_id_t last_index;
jksoft 1:48f6e08a3ac2 812
jksoft 1:48f6e08a3ac2 813 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index);
jksoft 1:48f6e08a3ac2 814 if (p_user_op == NULL)
jksoft 1:48f6e08a3ac2 815 {
jksoft 1:48f6e08a3ac2 816 return NRF_ERROR_NO_MEM;
jksoft 1:48f6e08a3ac2 817 }
jksoft 1:48f6e08a3ac2 818
jksoft 1:48f6e08a3ac2 819 p_user_op->op_type = TIMER_USER_OP_TYPE_START;
jksoft 1:48f6e08a3ac2 820 p_user_op->timer_id = timer_id;
jksoft 1:48f6e08a3ac2 821 p_user_op->params.start.ticks_at_start = rtc1_counter_get();
jksoft 1:48f6e08a3ac2 822 p_user_op->params.start.ticks_first_interval = timeout_initial;
jksoft 1:48f6e08a3ac2 823 p_user_op->params.start.ticks_periodic_interval = timeout_periodic;
jksoft 1:48f6e08a3ac2 824 p_user_op->params.start.p_context = p_context;
jksoft 1:48f6e08a3ac2 825
jksoft 1:48f6e08a3ac2 826 user_op_enque(&mp_users[user_id], last_index);
jksoft 1:48f6e08a3ac2 827
jksoft 1:48f6e08a3ac2 828 timer_list_handler_sched();
jksoft 1:48f6e08a3ac2 829
jksoft 1:48f6e08a3ac2 830 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 831 }
jksoft 1:48f6e08a3ac2 832
jksoft 1:48f6e08a3ac2 833
jksoft 1:48f6e08a3ac2 834 /**@brief Function for scheduling a Timer Stop operation.
jksoft 1:48f6e08a3ac2 835 *
jksoft 1:48f6e08a3ac2 836 * @param[in] user_id Id of user calling this function.
jksoft 1:48f6e08a3ac2 837 * @param[in] timer_id Id of timer to stop.
jksoft 1:48f6e08a3ac2 838 *
jksoft 1:48f6e08a3ac2 839 * @return NRF_SUCCESS on successful scheduling a timer stop operation. NRF_ERROR_NO_MEM when there
jksoft 1:48f6e08a3ac2 840 * is no memory left to schedule the timer stop operation.
jksoft 1:48f6e08a3ac2 841 */
jksoft 1:48f6e08a3ac2 842 static uint32_t timer_stop_op_schedule(timer_user_id_t user_id, app_timer_id_t timer_id)
jksoft 1:48f6e08a3ac2 843 {
jksoft 1:48f6e08a3ac2 844 app_timer_id_t last_index;
jksoft 1:48f6e08a3ac2 845
jksoft 1:48f6e08a3ac2 846 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index);
jksoft 1:48f6e08a3ac2 847 if (p_user_op == NULL)
jksoft 1:48f6e08a3ac2 848 {
jksoft 1:48f6e08a3ac2 849 return NRF_ERROR_NO_MEM;
jksoft 1:48f6e08a3ac2 850 }
jksoft 1:48f6e08a3ac2 851
jksoft 1:48f6e08a3ac2 852 p_user_op->op_type = TIMER_USER_OP_TYPE_STOP;
jksoft 1:48f6e08a3ac2 853 p_user_op->timer_id = timer_id;
jksoft 1:48f6e08a3ac2 854
jksoft 1:48f6e08a3ac2 855 user_op_enque(&mp_users[user_id], last_index);
jksoft 1:48f6e08a3ac2 856
jksoft 1:48f6e08a3ac2 857 timer_list_handler_sched();
jksoft 1:48f6e08a3ac2 858
jksoft 1:48f6e08a3ac2 859 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 860 }
jksoft 1:48f6e08a3ac2 861
jksoft 1:48f6e08a3ac2 862
jksoft 1:48f6e08a3ac2 863 /**@brief Function for scheduling a Timer Stop All operation.
jksoft 1:48f6e08a3ac2 864 *
jksoft 1:48f6e08a3ac2 865 * @param[in] user_id Id of user calling this function.
jksoft 1:48f6e08a3ac2 866 */
jksoft 1:48f6e08a3ac2 867 static uint32_t timer_stop_all_op_schedule(timer_user_id_t user_id)
jksoft 1:48f6e08a3ac2 868 {
jksoft 1:48f6e08a3ac2 869 app_timer_id_t last_index;
jksoft 1:48f6e08a3ac2 870
jksoft 1:48f6e08a3ac2 871 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index);
jksoft 1:48f6e08a3ac2 872 if (p_user_op == NULL)
jksoft 1:48f6e08a3ac2 873 {
jksoft 1:48f6e08a3ac2 874 return NRF_ERROR_NO_MEM;
jksoft 1:48f6e08a3ac2 875 }
jksoft 1:48f6e08a3ac2 876
jksoft 1:48f6e08a3ac2 877 p_user_op->op_type = TIMER_USER_OP_TYPE_STOP_ALL;
jksoft 1:48f6e08a3ac2 878 p_user_op->timer_id = TIMER_NULL;
jksoft 1:48f6e08a3ac2 879
jksoft 1:48f6e08a3ac2 880 user_op_enque(&mp_users[user_id], last_index);
jksoft 1:48f6e08a3ac2 881
jksoft 1:48f6e08a3ac2 882 timer_list_handler_sched();
jksoft 1:48f6e08a3ac2 883
jksoft 1:48f6e08a3ac2 884 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 885 }
jksoft 1:48f6e08a3ac2 886
jksoft 1:48f6e08a3ac2 887
jksoft 1:48f6e08a3ac2 888 /**@brief Function for handling the RTC1 interrupt.
jksoft 1:48f6e08a3ac2 889 *
jksoft 1:48f6e08a3ac2 890 * @details Checks for timeouts, and executes timeout handlers for expired timers.
jksoft 1:48f6e08a3ac2 891 */
jksoft 1:48f6e08a3ac2 892 extern "C" void RTC1_IRQHandler(void)
jksoft 1:48f6e08a3ac2 893 {
jksoft 1:48f6e08a3ac2 894 // Clear all events (also unexpected ones)
jksoft 1:48f6e08a3ac2 895 NRF_RTC1->EVENTS_COMPARE[0] = 0;
jksoft 1:48f6e08a3ac2 896 NRF_RTC1->EVENTS_COMPARE[1] = 0;
jksoft 1:48f6e08a3ac2 897 NRF_RTC1->EVENTS_COMPARE[2] = 0;
jksoft 1:48f6e08a3ac2 898 NRF_RTC1->EVENTS_COMPARE[3] = 0;
jksoft 1:48f6e08a3ac2 899 NRF_RTC1->EVENTS_TICK = 0;
jksoft 1:48f6e08a3ac2 900 NRF_RTC1->EVENTS_OVRFLW = 0;
jksoft 1:48f6e08a3ac2 901
jksoft 1:48f6e08a3ac2 902 // Check for expired timers
jksoft 1:48f6e08a3ac2 903 timer_timeouts_check();
jksoft 1:48f6e08a3ac2 904 }
jksoft 1:48f6e08a3ac2 905
jksoft 1:48f6e08a3ac2 906 /**@brief Function for handling the SWI0 interrupt.
jksoft 1:48f6e08a3ac2 907 *
jksoft 1:48f6e08a3ac2 908 * @details Performs all updates to the timer list.
jksoft 1:48f6e08a3ac2 909 */
jksoft 1:48f6e08a3ac2 910 extern "C" void SWI0_IRQHandler(void)
jksoft 1:48f6e08a3ac2 911 {
jksoft 1:48f6e08a3ac2 912 timer_list_handler();
jksoft 1:48f6e08a3ac2 913 }
jksoft 1:48f6e08a3ac2 914
jksoft 1:48f6e08a3ac2 915 uint32_t app_timer_init(uint32_t prescaler,
jksoft 1:48f6e08a3ac2 916 uint8_t max_timers,
jksoft 1:48f6e08a3ac2 917 uint8_t op_queues_size,
jksoft 1:48f6e08a3ac2 918 void * p_buffer,
jksoft 1:48f6e08a3ac2 919 app_timer_evt_schedule_func_t evt_schedule_func)
jksoft 1:48f6e08a3ac2 920 {
jksoft 1:48f6e08a3ac2 921 int i;
jksoft 1:48f6e08a3ac2 922
jksoft 1:48f6e08a3ac2 923 // Check that buffer is correctly aligned
jksoft 1:48f6e08a3ac2 924 if (!is_word_aligned(p_buffer))
jksoft 1:48f6e08a3ac2 925 {
jksoft 1:48f6e08a3ac2 926 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 927 }
jksoft 1:48f6e08a3ac2 928 // Check for NULL buffer
jksoft 1:48f6e08a3ac2 929 if (p_buffer == NULL)
jksoft 1:48f6e08a3ac2 930 {
jksoft 1:48f6e08a3ac2 931 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 932 }
jksoft 1:48f6e08a3ac2 933
jksoft 1:48f6e08a3ac2 934 // Stop RTC to prevent any running timers from expiring (in case of reinitialization)
jksoft 1:48f6e08a3ac2 935 rtc1_stop();
jksoft 1:48f6e08a3ac2 936
jksoft 1:48f6e08a3ac2 937 m_evt_schedule_func = evt_schedule_func;
jksoft 1:48f6e08a3ac2 938
jksoft 1:48f6e08a3ac2 939 // Initialize timer node array
jksoft 1:48f6e08a3ac2 940 m_node_array_size = max_timers;
jksoft 1:48f6e08a3ac2 941 mp_nodes = (timer_node_t *) p_buffer;
jksoft 1:48f6e08a3ac2 942
jksoft 1:48f6e08a3ac2 943 for (i = 0; i < max_timers; i++)
jksoft 1:48f6e08a3ac2 944 {
jksoft 1:48f6e08a3ac2 945 mp_nodes[i].state = STATE_FREE;
jksoft 1:48f6e08a3ac2 946 mp_nodes[i].is_running = false;
jksoft 1:48f6e08a3ac2 947 }
jksoft 1:48f6e08a3ac2 948
jksoft 1:48f6e08a3ac2 949 // Skip timer node array
jksoft 1:48f6e08a3ac2 950 p_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)];
jksoft 1:48f6e08a3ac2 951
jksoft 1:48f6e08a3ac2 952 // Initialize users array
jksoft 1:48f6e08a3ac2 953 m_user_array_size = APP_TIMER_INT_LEVELS;
jksoft 1:48f6e08a3ac2 954 mp_users = (timer_user_t *) p_buffer;
jksoft 1:48f6e08a3ac2 955
jksoft 1:48f6e08a3ac2 956 // Skip user array
jksoft 1:48f6e08a3ac2 957 p_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)];
jksoft 1:48f6e08a3ac2 958
jksoft 1:48f6e08a3ac2 959 // Initialize operation queues
jksoft 1:48f6e08a3ac2 960 for (i = 0; i < APP_TIMER_INT_LEVELS; i++)
jksoft 1:48f6e08a3ac2 961 {
jksoft 1:48f6e08a3ac2 962 timer_user_t * p_user = &mp_users[i];
jksoft 1:48f6e08a3ac2 963
jksoft 1:48f6e08a3ac2 964 p_user->first = 0;
jksoft 1:48f6e08a3ac2 965 p_user->last = 0;
jksoft 1:48f6e08a3ac2 966 p_user->user_op_queue_size = op_queues_size;
jksoft 1:48f6e08a3ac2 967 p_user->p_user_op_queue = (timer_user_op_t *) p_buffer;
jksoft 1:48f6e08a3ac2 968
jksoft 1:48f6e08a3ac2 969 // Skip operation queue
jksoft 1:48f6e08a3ac2 970 p_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)];
jksoft 1:48f6e08a3ac2 971 }
jksoft 1:48f6e08a3ac2 972
jksoft 1:48f6e08a3ac2 973 m_timer_id_head = TIMER_NULL;
jksoft 1:48f6e08a3ac2 974 m_ticks_elapsed_q_read_ind = 0;
jksoft 1:48f6e08a3ac2 975 m_ticks_elapsed_q_write_ind = 0;
jksoft 1:48f6e08a3ac2 976
jksoft 1:48f6e08a3ac2 977 NVIC_ClearPendingIRQ(SWI0_IRQn);
jksoft 1:48f6e08a3ac2 978 NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI);
jksoft 1:48f6e08a3ac2 979 NVIC_EnableIRQ(SWI0_IRQn);
jksoft 1:48f6e08a3ac2 980
jksoft 1:48f6e08a3ac2 981 rtc1_init(prescaler);
jksoft 1:48f6e08a3ac2 982
jksoft 1:48f6e08a3ac2 983 m_ticks_latest = rtc1_counter_get();
jksoft 1:48f6e08a3ac2 984
jksoft 1:48f6e08a3ac2 985 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 986 }
jksoft 1:48f6e08a3ac2 987
jksoft 1:48f6e08a3ac2 988
jksoft 1:48f6e08a3ac2 989 uint32_t app_timer_create(app_timer_id_t * p_timer_id,
jksoft 1:48f6e08a3ac2 990 app_timer_mode_t mode,
jksoft 1:48f6e08a3ac2 991 app_timer_timeout_handler_t timeout_handler)
jksoft 1:48f6e08a3ac2 992 {
jksoft 1:48f6e08a3ac2 993 int i;
jksoft 1:48f6e08a3ac2 994
jksoft 1:48f6e08a3ac2 995 // Check state and parameters
jksoft 1:48f6e08a3ac2 996 if (mp_nodes == NULL)
jksoft 1:48f6e08a3ac2 997 {
jksoft 1:48f6e08a3ac2 998 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 999 }
jksoft 1:48f6e08a3ac2 1000 if (timeout_handler == NULL)
jksoft 1:48f6e08a3ac2 1001 {
jksoft 1:48f6e08a3ac2 1002 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 1003 }
jksoft 1:48f6e08a3ac2 1004 if (p_timer_id == NULL)
jksoft 1:48f6e08a3ac2 1005 {
jksoft 1:48f6e08a3ac2 1006 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 1007 }
jksoft 1:48f6e08a3ac2 1008
jksoft 1:48f6e08a3ac2 1009 // Find free timer
jksoft 1:48f6e08a3ac2 1010 for (i = 0; i < m_node_array_size; i++)
jksoft 1:48f6e08a3ac2 1011 {
jksoft 1:48f6e08a3ac2 1012 if (mp_nodes[i].state == STATE_FREE)
jksoft 1:48f6e08a3ac2 1013 {
jksoft 1:48f6e08a3ac2 1014 mp_nodes[i].state = STATE_ALLOCATED;
jksoft 1:48f6e08a3ac2 1015 mp_nodes[i].mode = mode;
jksoft 1:48f6e08a3ac2 1016 mp_nodes[i].p_timeout_handler = timeout_handler;
jksoft 1:48f6e08a3ac2 1017
jksoft 1:48f6e08a3ac2 1018 *p_timer_id = i;
jksoft 1:48f6e08a3ac2 1019 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 1020 }
jksoft 1:48f6e08a3ac2 1021 }
jksoft 1:48f6e08a3ac2 1022
jksoft 1:48f6e08a3ac2 1023 return NRF_ERROR_NO_MEM;
jksoft 1:48f6e08a3ac2 1024 }
jksoft 1:48f6e08a3ac2 1025
jksoft 1:48f6e08a3ac2 1026
jksoft 1:48f6e08a3ac2 1027 /**@brief Function for creating a timer user id from the current interrupt level.
jksoft 1:48f6e08a3ac2 1028 *
jksoft 1:48f6e08a3ac2 1029 * @return Timer user id.
jksoft 1:48f6e08a3ac2 1030 */
jksoft 1:48f6e08a3ac2 1031 static timer_user_id_t user_id_get(void)
jksoft 1:48f6e08a3ac2 1032 {
jksoft 1:48f6e08a3ac2 1033 timer_user_id_t ret;
jksoft 1:48f6e08a3ac2 1034
jksoft 1:48f6e08a3ac2 1035 STATIC_ASSERT(APP_TIMER_INT_LEVELS == 3);
jksoft 1:48f6e08a3ac2 1036
jksoft 1:48f6e08a3ac2 1037 switch (current_int_priority_get())
jksoft 1:48f6e08a3ac2 1038 {
jksoft 1:48f6e08a3ac2 1039 case APP_IRQ_PRIORITY_HIGH:
jksoft 1:48f6e08a3ac2 1040 ret = APP_HIGH_USER_ID;
jksoft 1:48f6e08a3ac2 1041 break;
jksoft 1:48f6e08a3ac2 1042
jksoft 1:48f6e08a3ac2 1043 case APP_IRQ_PRIORITY_LOW:
jksoft 1:48f6e08a3ac2 1044 ret = APP_LOW_USER_ID;
jksoft 1:48f6e08a3ac2 1045 break;
jksoft 1:48f6e08a3ac2 1046
jksoft 1:48f6e08a3ac2 1047 default:
jksoft 1:48f6e08a3ac2 1048 ret = THREAD_MODE_USER_ID;
jksoft 1:48f6e08a3ac2 1049 break;
jksoft 1:48f6e08a3ac2 1050 }
jksoft 1:48f6e08a3ac2 1051
jksoft 1:48f6e08a3ac2 1052 return ret;
jksoft 1:48f6e08a3ac2 1053 }
jksoft 1:48f6e08a3ac2 1054
jksoft 1:48f6e08a3ac2 1055
jksoft 1:48f6e08a3ac2 1056 uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context)
jksoft 1:48f6e08a3ac2 1057 {
jksoft 1:48f6e08a3ac2 1058 uint32_t timeout_periodic;
jksoft 1:48f6e08a3ac2 1059
jksoft 1:48f6e08a3ac2 1060 // Check state and parameters
jksoft 1:48f6e08a3ac2 1061 if (mp_nodes == NULL)
jksoft 1:48f6e08a3ac2 1062 {
jksoft 1:48f6e08a3ac2 1063 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 1064 }
jksoft 1:48f6e08a3ac2 1065 if ((timer_id >= m_node_array_size) || (timeout_ticks < APP_TIMER_MIN_TIMEOUT_TICKS))
jksoft 1:48f6e08a3ac2 1066 {
jksoft 1:48f6e08a3ac2 1067 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 1068 }
jksoft 1:48f6e08a3ac2 1069 if (mp_nodes[timer_id].state != STATE_ALLOCATED)
jksoft 1:48f6e08a3ac2 1070 {
jksoft 1:48f6e08a3ac2 1071 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 1072 }
jksoft 1:48f6e08a3ac2 1073
jksoft 1:48f6e08a3ac2 1074 // Schedule timer start operation
jksoft 1:48f6e08a3ac2 1075 timeout_periodic = (mp_nodes[timer_id].mode == APP_TIMER_MODE_REPEATED) ? timeout_ticks : 0;
jksoft 1:48f6e08a3ac2 1076
jksoft 1:48f6e08a3ac2 1077 return timer_start_op_schedule(user_id_get(),
jksoft 1:48f6e08a3ac2 1078 timer_id,
jksoft 1:48f6e08a3ac2 1079 timeout_ticks,
jksoft 1:48f6e08a3ac2 1080 timeout_periodic,
jksoft 1:48f6e08a3ac2 1081 p_context);
jksoft 1:48f6e08a3ac2 1082 }
jksoft 1:48f6e08a3ac2 1083
jksoft 1:48f6e08a3ac2 1084
jksoft 1:48f6e08a3ac2 1085 uint32_t app_timer_stop(app_timer_id_t timer_id)
jksoft 1:48f6e08a3ac2 1086 {
jksoft 1:48f6e08a3ac2 1087 // Check state and parameters
jksoft 1:48f6e08a3ac2 1088 if (mp_nodes == NULL)
jksoft 1:48f6e08a3ac2 1089 {
jksoft 1:48f6e08a3ac2 1090 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 1091 }
jksoft 1:48f6e08a3ac2 1092 if (timer_id >= m_node_array_size)
jksoft 1:48f6e08a3ac2 1093 {
jksoft 1:48f6e08a3ac2 1094 return NRF_ERROR_INVALID_PARAM;
jksoft 1:48f6e08a3ac2 1095 }
jksoft 1:48f6e08a3ac2 1096 if (mp_nodes[timer_id].state != STATE_ALLOCATED)
jksoft 1:48f6e08a3ac2 1097 {
jksoft 1:48f6e08a3ac2 1098 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 1099 }
jksoft 1:48f6e08a3ac2 1100
jksoft 1:48f6e08a3ac2 1101 // Schedule timer stop operation
jksoft 1:48f6e08a3ac2 1102 return timer_stop_op_schedule(user_id_get(), timer_id);
jksoft 1:48f6e08a3ac2 1103 }
jksoft 1:48f6e08a3ac2 1104
jksoft 1:48f6e08a3ac2 1105
jksoft 1:48f6e08a3ac2 1106 uint32_t app_timer_stop_all(void)
jksoft 1:48f6e08a3ac2 1107 {
jksoft 1:48f6e08a3ac2 1108 // Check state
jksoft 1:48f6e08a3ac2 1109 if (mp_nodes == NULL)
jksoft 1:48f6e08a3ac2 1110 {
jksoft 1:48f6e08a3ac2 1111 return NRF_ERROR_INVALID_STATE;
jksoft 1:48f6e08a3ac2 1112 }
jksoft 1:48f6e08a3ac2 1113
jksoft 1:48f6e08a3ac2 1114 return timer_stop_all_op_schedule(user_id_get());
jksoft 1:48f6e08a3ac2 1115 }
jksoft 1:48f6e08a3ac2 1116
jksoft 1:48f6e08a3ac2 1117
jksoft 1:48f6e08a3ac2 1118 uint32_t app_timer_cnt_get(uint32_t * p_ticks)
jksoft 1:48f6e08a3ac2 1119 {
jksoft 1:48f6e08a3ac2 1120 *p_ticks = rtc1_counter_get();
jksoft 1:48f6e08a3ac2 1121 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 1122 }
jksoft 1:48f6e08a3ac2 1123
jksoft 1:48f6e08a3ac2 1124
jksoft 1:48f6e08a3ac2 1125 uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to,
jksoft 1:48f6e08a3ac2 1126 uint32_t ticks_from,
jksoft 1:48f6e08a3ac2 1127 uint32_t * p_ticks_diff)
jksoft 1:48f6e08a3ac2 1128 {
jksoft 1:48f6e08a3ac2 1129 *p_ticks_diff = ticks_diff_get(ticks_to, ticks_from);
jksoft 1:48f6e08a3ac2 1130 return NRF_SUCCESS;
jksoft 1:48f6e08a3ac2 1131 }