Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of nRF51822 by
app_timer.cpp
00001 /* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved. 00002 * 00003 * The information contained herein is property of Nordic Semiconductor ASA. 00004 * Terms and conditions of usage are described in detail in NORDIC 00005 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. 00006 * 00007 * Licensees are granted free, non-transferable use of the information. NO 00008 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from 00009 * the file. 00010 * 00011 */ 00012 00013 #include "app_timer.h " 00014 #include <stdlib.h> 00015 #include "nrf51.h" 00016 #include "nrf51_bitfields.h" 00017 #include "nrf_soc.h" 00018 #include "app_error.h " 00019 //#include "nrf_delay.h" 00020 #include "mbed.h" 00021 #include "app_util.h " 00022 00023 00024 #define RTC1_IRQ_PRI APP_IRQ_PRIORITY_LOW /**< Priority of the RTC1 interrupt (used for checking for timeouts and executing timeout handlers). */ 00025 #define SWI0_IRQ_PRI APP_IRQ_PRIORITY_LOW /**< Priority of the SWI0 interrupt (used for updating the timer list). */ 00026 00027 // The current design assumes that both interrupt handlers run at the same interrupt level. 00028 // If this is to be changed, protection must be added to prevent them from interrupting each other 00029 // (e.g. by using guard/trigger flags). 00030 STATIC_ASSERT(RTC1_IRQ_PRI == SWI0_IRQ_PRI); 00031 00032 #define MAX_RTC_COUNTER_VAL 0x00FFFFFF /**< Maximum value of the RTC counter. */ 00033 00034 #define APP_HIGH_USER_ID 0 /**< User Id for the Application High "user". */ 00035 #define APP_LOW_USER_ID 1 /**< User Id for the Application Low "user". */ 00036 #define THREAD_MODE_USER_ID 2 /**< User Id for the Thread Mode "user". */ 00037 00038 #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.*/ 00039 00040 #define MAX_RTC_TASKS_DELAY 47 /**< Maximum delay until an RTC task is executed. */ 00041 00042 /**@brief Timer allocation state type. */ 00043 typedef enum 00044 { 00045 STATE_FREE, /**< The timer node is available. */ 00046 STATE_ALLOCATED /**< The timer node has been allocated. */ 00047 } timer_alloc_state_t; 00048 00049 /**@brief Timer node type. The nodes will be used form a linked list of running timers. */ 00050 typedef struct 00051 { 00052 timer_alloc_state_t state; /**< Timer allocation state. */ 00053 app_timer_mode_t mode; /**< Timer mode. */ 00054 uint32_t ticks_to_expire; /**< Number of ticks from previous timer interrupt to timer expiry. */ 00055 uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */ 00056 uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */ 00057 uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */ 00058 bool is_running; /**< True if timer is running, False otherwise. */ 00059 app_timer_timeout_handler_t p_timeout_handler; /**< Pointer to function to be executed when the timer expires. */ 00060 void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */ 00061 app_timer_id_t next; /**< Id of next timer in list of running timers. */ 00062 } timer_node_t; 00063 00064 STATIC_ASSERT(sizeof(timer_node_t) <= APP_TIMER_NODE_SIZE); 00065 STATIC_ASSERT(sizeof(timer_node_t) % 4 == 0); 00066 00067 /**@brief Set of available timer operation types. */ 00068 typedef enum 00069 { 00070 TIMER_USER_OP_TYPE_NONE, /**< Invalid timer operation type. */ 00071 TIMER_USER_OP_TYPE_START, /**< Timer operation type Start. */ 00072 TIMER_USER_OP_TYPE_STOP, /**< Timer operation type Stop. */ 00073 TIMER_USER_OP_TYPE_STOP_ALL /**< Timer operation type Stop All. */ 00074 } timer_user_op_type_t; 00075 00076 /**@brief Structure describing a timer start operation. */ 00077 typedef struct 00078 { 00079 uint32_t ticks_at_start; /**< Current RTC counter value when the timer was started. */ 00080 uint32_t ticks_first_interval; /**< Number of ticks in the first timer interval. */ 00081 uint32_t ticks_periodic_interval; /**< Timer period (for repeating timers). */ 00082 void * p_context; /**< General purpose pointer. Will be passed to the timeout handler when the timer expires. */ 00083 } timer_user_op_start_t; 00084 00085 /**@brief Structure describing a timer operation. */ 00086 typedef struct 00087 { 00088 timer_user_op_type_t op_type; /**< Timer operation type. */ 00089 app_timer_id_t timer_id; /**< Id of timer on which the operation is to be performed. */ 00090 union 00091 { 00092 timer_user_op_start_t start; /**< Structure describing a timer start operation. */ 00093 } params; 00094 } timer_user_op_t; 00095 00096 STATIC_ASSERT(sizeof(timer_user_op_t) <= APP_TIMER_USER_OP_SIZE); 00097 STATIC_ASSERT(sizeof(timer_user_op_t) % 4 == 0); 00098 00099 /**@brief Structure describing a timer user. 00100 * 00101 * @details For each user of the timer module, there will be a timer operations queue. This queue 00102 * will hold timer operations issued by this user until the timer interrupt handler 00103 * processes these operations. For the current implementation, there will be one user for 00104 * each interrupt level available to the application (APP_HIGH, APP_LOW and THREAD_MODE), 00105 * but the module can easily be modified to e.g. have one queue per process when using an 00106 * RTOS. The purpose of the queues is to be able to have a completely lockless timer 00107 * implementation. 00108 */ 00109 typedef struct 00110 { 00111 uint8_t first; /**< Index of first entry to have been inserted in the queue (i.e. the next entry to be executed). */ 00112 uint8_t last; /**< Index of last entry to have been inserted in the queue. */ 00113 uint8_t user_op_queue_size; /**< Queue size. */ 00114 timer_user_op_t * p_user_op_queue; /**< Queue buffer. */ 00115 } timer_user_t; 00116 00117 STATIC_ASSERT(sizeof(timer_user_t) == APP_TIMER_USER_SIZE); 00118 STATIC_ASSERT(sizeof(timer_user_t) % 4 == 0); 00119 00120 /**@brief User id type. 00121 * 00122 * @details In the current implementation, this will automatically be generated from the current 00123 * interrupt level. 00124 */ 00125 typedef uint32_t timer_user_id_t; 00126 00127 #define TIMER_NULL ((app_timer_id_t)(0 - 1)) /**< Invalid timer id. */ 00128 #define CONTEXT_QUEUE_SIZE_MAX (2) /**< Timer internal elapsed ticks queue size. */ 00129 00130 static uint8_t m_node_array_size; /**< Size of timer node array. */ 00131 static timer_node_t * mp_nodes = NULL; /**< Array of timer nodes. */ 00132 static uint8_t m_user_array_size; /**< Size of timer user array. */ 00133 static timer_user_t * mp_users; /**< Array of timer users. */ 00134 static app_timer_id_t m_timer_id_head; /**< First timer in list of running timers. */ 00135 static uint32_t m_ticks_latest; /**< Last known RTC counter value. */ 00136 static uint32_t m_ticks_elapsed[CONTEXT_QUEUE_SIZE_MAX]; /**< Timer internal elapsed ticks queue. */ 00137 static uint8_t m_ticks_elapsed_q_read_ind; /**< Timer internal elapsed ticks queue read index. */ 00138 static uint8_t m_ticks_elapsed_q_write_ind; /**< Timer internal elapsed ticks queue write index. */ 00139 static app_timer_evt_schedule_func_t m_evt_schedule_func; /**< Pointer to function for propagating timeout events to the scheduler. */ 00140 00141 00142 /**@brief Function for initializing the RTC1 counter. 00143 * 00144 * @param[in] prescaler Value of the RTC1 PRESCALER register. Set to 0 for no prescaling. 00145 */ 00146 static void rtc1_init(uint32_t prescaler) 00147 { 00148 NRF_RTC1->PRESCALER = prescaler; 00149 NVIC_SetPriority(RTC1_IRQn, RTC1_IRQ_PRI); 00150 } 00151 00152 00153 /**@brief Function for starting the RTC1 timer. 00154 */ 00155 static void rtc1_start(void) 00156 { 00157 NRF_RTC1->EVTENSET = RTC_EVTEN_COMPARE0_Msk; 00158 NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk; 00159 00160 NVIC_ClearPendingIRQ(RTC1_IRQn); 00161 NVIC_EnableIRQ(RTC1_IRQn); 00162 00163 NRF_RTC1->TASKS_START = 1; 00164 wait(0.0000001 * MAX_RTC_TASKS_DELAY); 00165 } 00166 00167 00168 /**@brief Function for stopping the RTC1 timer. 00169 */ 00170 static void rtc1_stop(void) 00171 { 00172 NVIC_DisableIRQ(RTC1_IRQn); 00173 00174 NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk; 00175 NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk; 00176 00177 NRF_RTC1->TASKS_STOP = 1; 00178 wait(0.0000001 * MAX_RTC_TASKS_DELAY); 00179 } 00180 00181 00182 /**@brief Function for returning the current value of the RTC1 counter. 00183 * 00184 * @return Current value of the RTC1 counter. 00185 */ 00186 static __INLINE uint32_t rtc1_counter_get(void) 00187 { 00188 return NRF_RTC1->COUNTER; 00189 } 00190 00191 00192 /**@brief Function for computing the difference between two RTC1 counter values. 00193 * 00194 * @return Number of ticks elapsed from ticks_old to ticks_now. 00195 */ 00196 static __INLINE uint32_t ticks_diff_get(uint32_t ticks_now, uint32_t ticks_old) 00197 { 00198 return ((ticks_now - ticks_old) & MAX_RTC_COUNTER_VAL); 00199 } 00200 00201 00202 /**@brief Function for setting the RTC1 Capture Compare register 0, and enabling the corresponding 00203 * event. 00204 * 00205 * @param[in] value New value of Capture Compare register 0. 00206 */ 00207 static __INLINE void rtc1_compare0_set(uint32_t value) 00208 { 00209 NRF_RTC1->CC[0] = value; 00210 } 00211 00212 00213 /**@brief Function for inserting a timer in the timer list. 00214 * 00215 * @param[in] timer_id Id of timer to insert. 00216 */ 00217 static void timer_list_insert(app_timer_id_t timer_id) 00218 { 00219 timer_node_t * p_timer = &mp_nodes[timer_id]; 00220 00221 if (m_timer_id_head == TIMER_NULL) 00222 { 00223 m_timer_id_head = timer_id; 00224 } 00225 else 00226 { 00227 if (p_timer->ticks_to_expire <= mp_nodes[m_timer_id_head].ticks_to_expire) 00228 { 00229 mp_nodes[m_timer_id_head].ticks_to_expire -= p_timer->ticks_to_expire; 00230 00231 p_timer->next = m_timer_id_head; 00232 m_timer_id_head = timer_id; 00233 } 00234 else 00235 { 00236 app_timer_id_t previous; 00237 app_timer_id_t current; 00238 uint32_t ticks_to_expire; 00239 00240 ticks_to_expire = p_timer->ticks_to_expire; 00241 previous = m_timer_id_head; 00242 current = m_timer_id_head; 00243 00244 while ((current != TIMER_NULL) && (ticks_to_expire > mp_nodes[current].ticks_to_expire)) 00245 { 00246 ticks_to_expire -= mp_nodes[current].ticks_to_expire; 00247 previous = current; 00248 current = mp_nodes[current].next; 00249 } 00250 00251 if (current != TIMER_NULL) 00252 { 00253 mp_nodes[current].ticks_to_expire -= ticks_to_expire; 00254 } 00255 00256 p_timer->ticks_to_expire = ticks_to_expire; 00257 p_timer->next = current; 00258 mp_nodes[previous].next = timer_id; 00259 } 00260 } 00261 } 00262 00263 00264 /**@brief Function for removing a timer from the timer queue. 00265 * 00266 * @param[in] timer_id Id of timer to remove. 00267 */ 00268 static void timer_list_remove(app_timer_id_t timer_id) 00269 { 00270 app_timer_id_t previous; 00271 app_timer_id_t current; 00272 uint32_t timeout; 00273 00274 // Find the timer's position in timer list 00275 previous = m_timer_id_head; 00276 current = previous; 00277 00278 while (current != TIMER_NULL) 00279 { 00280 if (current == timer_id) 00281 { 00282 break; 00283 } 00284 previous = current; 00285 current = mp_nodes[current].next; 00286 } 00287 00288 // Timer not in active list 00289 if (current == TIMER_NULL) 00290 { 00291 return; 00292 } 00293 00294 // Timer is the first in the list 00295 if (previous == current) 00296 { 00297 m_timer_id_head = mp_nodes[m_timer_id_head].next; 00298 } 00299 00300 // Remaining timeout between next timeout 00301 timeout = mp_nodes[current].ticks_to_expire; 00302 00303 // Link previous timer with next of this timer, i.e. removing the timer from list 00304 mp_nodes[previous].next = mp_nodes[current].next; 00305 00306 // If this is not the last timer, increment the next timer by this timer timeout 00307 current = mp_nodes[previous].next; 00308 if (current != TIMER_NULL) 00309 { 00310 mp_nodes[current].ticks_to_expire += timeout; 00311 } 00312 } 00313 00314 00315 /**@brief Function for scheduling a check for timeouts by generating a RTC1 interrupt. 00316 */ 00317 static void timer_timeouts_check_sched(void) 00318 { 00319 NVIC_SetPendingIRQ(RTC1_IRQn); 00320 } 00321 00322 00323 /**@brief Function for scheduling a timer list update by generating a SWI0 interrupt. 00324 */ 00325 static void timer_list_handler_sched(void) 00326 { 00327 NVIC_SetPendingIRQ(SWI0_IRQn); 00328 } 00329 00330 00331 /**@brief Function for executing an application timeout handler, either by calling it directly, or 00332 * by passing an event to the @ref app_scheduler. 00333 * 00334 * @param[in] p_timer Pointer to expired timer. 00335 */ 00336 static void timeout_handler_exec(timer_node_t * p_timer) 00337 { 00338 if (m_evt_schedule_func != NULL) 00339 { 00340 uint32_t err_code = m_evt_schedule_func(p_timer->p_timeout_handler, p_timer->p_context); 00341 APP_ERROR_CHECK(err_code); 00342 } 00343 else 00344 { 00345 p_timer->p_timeout_handler(p_timer->p_context); 00346 } 00347 } 00348 00349 00350 /**@brief Function for checking for expired timers. 00351 */ 00352 static void timer_timeouts_check(void) 00353 { 00354 // Handle expired of timer 00355 if (m_timer_id_head != TIMER_NULL) 00356 { 00357 app_timer_id_t timer_id; 00358 uint32_t ticks_elapsed; 00359 uint32_t ticks_expired; 00360 00361 // Initialize actual elapsed ticks being consumed to 0 00362 ticks_expired = 0; 00363 00364 // ticks_elapsed is collected here, job will use it 00365 ticks_elapsed = ticks_diff_get(rtc1_counter_get(), m_ticks_latest); 00366 00367 // Auto variable containing the head of timers expiring 00368 timer_id = m_timer_id_head; 00369 00370 // Expire all timers within ticks_elapsed and collect ticks_expired 00371 while (timer_id != TIMER_NULL) 00372 { 00373 timer_node_t * p_timer; 00374 00375 // Auto variable for current timer node 00376 p_timer = &mp_nodes[timer_id]; 00377 00378 // Do nothing if timer did not expire 00379 if (ticks_elapsed < p_timer->ticks_to_expire) 00380 { 00381 break; 00382 } 00383 00384 // Decrement ticks_elapsed and collect expired ticks 00385 ticks_elapsed -= p_timer->ticks_to_expire; 00386 ticks_expired += p_timer->ticks_to_expire; 00387 00388 // Move to next timer 00389 timer_id = p_timer->next; 00390 00391 // Execute Task 00392 timeout_handler_exec(p_timer); 00393 } 00394 00395 // Prepare to queue the ticks expired in the m_ticks_elapsed queue. 00396 if (m_ticks_elapsed_q_read_ind == m_ticks_elapsed_q_write_ind) 00397 { 00398 // The read index of the queue is equal to the write index. This means the new 00399 // value of ticks_expired should be stored at a new location in the m_ticks_elapsed 00400 // queue (which is implemented as a double buffer). 00401 00402 // Check if there will be a queue overflow. 00403 if (++m_ticks_elapsed_q_write_ind == CONTEXT_QUEUE_SIZE_MAX) 00404 { 00405 // There will be a queue overflow. Hence the write index should point to the start 00406 // of the queue. 00407 m_ticks_elapsed_q_write_ind = 0; 00408 } 00409 } 00410 00411 // Queue the ticks expired. 00412 m_ticks_elapsed[m_ticks_elapsed_q_write_ind] = ticks_expired; 00413 00414 timer_list_handler_sched(); 00415 } 00416 } 00417 00418 00419 /**@brief Function for acquiring the number of ticks elapsed. 00420 * 00421 * @param[out] p_ticks_elapsed Number of ticks elapsed. 00422 * 00423 * @return TRUE if elapsed ticks was read from queue, FALSE otherwise. 00424 */ 00425 static bool elapsed_ticks_acquire(uint32_t * p_ticks_elapsed) 00426 { 00427 // Pick the elapsed value from queue 00428 if (m_ticks_elapsed_q_read_ind != m_ticks_elapsed_q_write_ind) 00429 { 00430 // Dequeue elapsed value 00431 m_ticks_elapsed_q_read_ind++; 00432 if (m_ticks_elapsed_q_read_ind == CONTEXT_QUEUE_SIZE_MAX) 00433 { 00434 m_ticks_elapsed_q_read_ind = 0; 00435 } 00436 00437 *p_ticks_elapsed = m_ticks_elapsed[m_ticks_elapsed_q_read_ind]; 00438 00439 m_ticks_latest += *p_ticks_elapsed; 00440 m_ticks_latest &= MAX_RTC_COUNTER_VAL; 00441 00442 return true; 00443 } 00444 else 00445 { 00446 // No elapsed value in queue 00447 *p_ticks_elapsed = 0; 00448 return false; 00449 } 00450 } 00451 00452 00453 /**@brief Function for handling the timer list deletions. 00454 * 00455 * @return TRUE if Capture Compare register must be updated, FALSE otherwise. 00456 */ 00457 static bool list_deletions_handler(void) 00458 { 00459 app_timer_id_t timer_id_old_head; 00460 uint8_t user_id; 00461 00462 // Remember the old head, so as to decide if new compare needs to be set 00463 timer_id_old_head = m_timer_id_head; 00464 00465 user_id = m_user_array_size; 00466 while (user_id--) 00467 { 00468 timer_user_t * p_user = &mp_users[user_id]; 00469 uint8_t user_ops_first = p_user->first; 00470 00471 while (user_ops_first != p_user->last) 00472 { 00473 timer_node_t * p_timer; 00474 timer_user_op_t * p_user_op = &p_user->p_user_op_queue[user_ops_first]; 00475 00476 // Traverse to next operation in queue 00477 user_ops_first++; 00478 if (user_ops_first == p_user->user_op_queue_size) 00479 { 00480 user_ops_first = 0; 00481 } 00482 00483 switch (p_user_op->op_type) 00484 { 00485 case TIMER_USER_OP_TYPE_STOP: 00486 // Delete node if timer is running 00487 p_timer = &mp_nodes[p_user_op->timer_id]; 00488 if (p_timer->is_running) 00489 { 00490 timer_list_remove(p_user_op->timer_id); 00491 p_timer->is_running = false; 00492 } 00493 break; 00494 00495 case TIMER_USER_OP_TYPE_STOP_ALL: 00496 // Delete list of running timers, and mark all timers as not running 00497 while (m_timer_id_head != TIMER_NULL) 00498 { 00499 timer_node_t * p_head = &mp_nodes[m_timer_id_head]; 00500 00501 p_head->is_running = false; 00502 m_timer_id_head = p_head->next; 00503 } 00504 break; 00505 00506 default: 00507 // No implementation needed. 00508 break; 00509 } 00510 } 00511 } 00512 00513 // Detect change in head of the list 00514 return (m_timer_id_head != timer_id_old_head); 00515 } 00516 00517 00518 /**@brief Function for updating the timer list for expired timers. 00519 * 00520 * @param[in] ticks_elapsed Number of elapsed ticks. 00521 * @param[in] ticks_previous Previous known value of the RTC counter. 00522 * @param[out] p_restart_list_head List of repeating timers to be restarted. 00523 */ 00524 static void expired_timers_handler(uint32_t ticks_elapsed, 00525 uint32_t ticks_previous, 00526 app_timer_id_t * p_restart_list_head) 00527 { 00528 uint32_t ticks_expired = 0; 00529 00530 while (m_timer_id_head != TIMER_NULL) 00531 { 00532 timer_node_t * p_timer; 00533 app_timer_id_t id_expired; 00534 00535 // Auto variable for current timer node 00536 p_timer = &mp_nodes[m_timer_id_head]; 00537 00538 // Do nothing if timer did not expire 00539 if (ticks_elapsed < p_timer->ticks_to_expire) 00540 { 00541 p_timer->ticks_to_expire -= ticks_elapsed; 00542 break; 00543 } 00544 00545 // Decrement ticks_elapsed and collect expired ticks 00546 ticks_elapsed -= p_timer->ticks_to_expire; 00547 ticks_expired += p_timer->ticks_to_expire; 00548 00549 // Timer expired, set ticks_to_expire zero 00550 p_timer->ticks_to_expire = 0; 00551 p_timer->is_running = false; 00552 00553 // Remove the expired timer from head 00554 id_expired = m_timer_id_head; 00555 m_timer_id_head = p_timer->next; 00556 00557 // Timer will be restarted if periodic 00558 if (p_timer->ticks_periodic_interval != 0) 00559 { 00560 p_timer->ticks_at_start = (ticks_previous + ticks_expired) & MAX_RTC_COUNTER_VAL; 00561 p_timer->ticks_first_interval = p_timer->ticks_periodic_interval; 00562 p_timer->next = *p_restart_list_head; 00563 *p_restart_list_head = id_expired; 00564 } 00565 } 00566 } 00567 00568 00569 /**@brief Function for handling timer list insertions. 00570 * 00571 * @param[in] p_restart_list_head List of repeating timers to be restarted. 00572 * 00573 * @return TRUE if Capture Compare register must be updated, FALSE otherwise. 00574 */ 00575 static bool list_insertions_handler(app_timer_id_t restart_list_head) 00576 { 00577 app_timer_id_t timer_id_old_head; 00578 uint8_t user_id; 00579 00580 // Remember the old head, so as to decide if new compare needs to be set 00581 timer_id_old_head = m_timer_id_head; 00582 00583 user_id = m_user_array_size; 00584 while (user_id--) 00585 { 00586 timer_user_t * p_user = &mp_users[user_id]; 00587 00588 // Handle insertions of timers 00589 while ((restart_list_head != TIMER_NULL) || (p_user->first != p_user->last)) 00590 { 00591 app_timer_id_t id_start; 00592 timer_node_t * p_timer; 00593 00594 if (restart_list_head != TIMER_NULL) 00595 { 00596 id_start = restart_list_head; 00597 p_timer = &mp_nodes[id_start]; 00598 restart_list_head = p_timer->next; 00599 } 00600 else 00601 { 00602 timer_user_op_t * p_user_op = &p_user->p_user_op_queue[p_user->first]; 00603 00604 p_user->first++; 00605 if (p_user->first == p_user->user_op_queue_size) 00606 { 00607 p_user->first = 0; 00608 } 00609 00610 id_start = p_user_op->timer_id; 00611 p_timer = &mp_nodes[id_start]; 00612 00613 if ((p_user_op->op_type != TIMER_USER_OP_TYPE_START) || p_timer->is_running) 00614 { 00615 continue; 00616 } 00617 00618 p_timer->ticks_at_start = p_user_op->params.start.ticks_at_start; 00619 p_timer->ticks_first_interval = p_user_op->params.start.ticks_first_interval; 00620 p_timer->ticks_periodic_interval = p_user_op->params.start.ticks_periodic_interval; 00621 p_timer->p_context = p_user_op->params.start.p_context; 00622 } 00623 00624 // Prepare the node to be inserted 00625 if ( 00626 ((p_timer->ticks_at_start - m_ticks_latest) & MAX_RTC_COUNTER_VAL) 00627 < 00628 (MAX_RTC_COUNTER_VAL / 2) 00629 ) 00630 { 00631 p_timer->ticks_to_expire = ticks_diff_get(p_timer->ticks_at_start, m_ticks_latest) + 00632 p_timer->ticks_first_interval; 00633 } 00634 else 00635 { 00636 uint32_t delta_current_start; 00637 00638 delta_current_start = ticks_diff_get(m_ticks_latest, p_timer->ticks_at_start); 00639 if (p_timer->ticks_first_interval > delta_current_start) 00640 { 00641 p_timer->ticks_to_expire = p_timer->ticks_first_interval - delta_current_start; 00642 } 00643 else 00644 { 00645 p_timer->ticks_to_expire = 0; 00646 } 00647 } 00648 00649 p_timer->ticks_at_start = 0; 00650 p_timer->ticks_first_interval = 0; 00651 p_timer->is_running = true; 00652 p_timer->next = TIMER_NULL; 00653 00654 // Insert into list 00655 timer_list_insert(id_start); 00656 } 00657 } 00658 00659 return (m_timer_id_head != timer_id_old_head); 00660 } 00661 00662 00663 /**@brief Function for updating the Capture Compare register. 00664 */ 00665 static void compare_reg_update(app_timer_id_t timer_id_head_old) 00666 { 00667 // Setup the timeout for timers on the head of the list 00668 if (m_timer_id_head != TIMER_NULL) 00669 { 00670 uint32_t ticks_to_expire = mp_nodes[m_timer_id_head].ticks_to_expire; 00671 uint32_t pre_counter_val = rtc1_counter_get(); 00672 uint32_t cc = m_ticks_latest; 00673 uint32_t ticks_elapsed = ticks_diff_get(pre_counter_val, cc) + RTC_COMPARE_OFFSET_MIN; 00674 00675 if (timer_id_head_old == TIMER_NULL) 00676 { 00677 // No timers were already running, start RTC 00678 rtc1_start(); 00679 } 00680 00681 cc += (ticks_elapsed < ticks_to_expire) ? ticks_to_expire : ticks_elapsed; 00682 cc &= MAX_RTC_COUNTER_VAL; 00683 00684 rtc1_compare0_set(cc); 00685 00686 uint32_t post_counter_val = rtc1_counter_get(); 00687 00688 if ( 00689 (ticks_diff_get(post_counter_val, pre_counter_val) + RTC_COMPARE_OFFSET_MIN) 00690 > 00691 ticks_diff_get(cc, pre_counter_val) 00692 ) 00693 { 00694 // When this happens the COMPARE event may not be triggered by the RTC. 00695 // The nRF51 Series User Specification states that if the COUNTER value is N 00696 // (i.e post_counter_val = N), writing N or N+1 to a CC register may not trigger a 00697 // COMPARE event. Hence the RTC interrupt is forcefully pended by calling the following 00698 // function. 00699 timer_timeouts_check_sched(); 00700 } 00701 } 00702 else 00703 { 00704 // No timers are running, stop RTC 00705 rtc1_stop(); 00706 } 00707 } 00708 00709 00710 /**@brief Function for handling changes to the timer list. 00711 */ 00712 static void timer_list_handler(void) 00713 { 00714 app_timer_id_t restart_list_head = TIMER_NULL; 00715 uint32_t ticks_elapsed; 00716 uint32_t ticks_previous; 00717 bool ticks_have_elapsed; 00718 bool compare_update; 00719 app_timer_id_t timer_id_head_old; 00720 00721 // Back up the previous known tick and previous list head 00722 ticks_previous = m_ticks_latest; 00723 timer_id_head_old = m_timer_id_head; 00724 00725 // Get number of elapsed ticks 00726 ticks_have_elapsed = elapsed_ticks_acquire(&ticks_elapsed); 00727 00728 // Handle list deletions 00729 compare_update = list_deletions_handler(); 00730 00731 // Handle expired timers 00732 if (ticks_have_elapsed) 00733 { 00734 expired_timers_handler(ticks_elapsed, ticks_previous, &restart_list_head); 00735 compare_update = true; 00736 } 00737 00738 // Handle list insertions 00739 if (list_insertions_handler(restart_list_head)) 00740 { 00741 compare_update = true; 00742 } 00743 00744 // Update compare register if necessary 00745 if (compare_update) 00746 { 00747 compare_reg_update(timer_id_head_old); 00748 } 00749 } 00750 00751 00752 /**@brief Function for enqueueing a new operations queue entry. 00753 * 00754 * @param[in] p_user User that the entry is to be enqueued for. 00755 * @param[in] last_index Index of the next last index to be enqueued. 00756 */ 00757 static void user_op_enque(timer_user_t * p_user, app_timer_id_t last_index) 00758 { 00759 p_user->last = last_index; 00760 } 00761 00762 00763 /**@brief Function for allocating a new operations queue entry. 00764 * 00765 * @param[in] p_user User that the entry is to be allocated for. 00766 * @param[out] p_last_index Index of the next last index to be enqueued. 00767 * 00768 * @return Pointer to allocated queue entry, or NULL if queue is full. 00769 */ 00770 static timer_user_op_t * user_op_alloc(timer_user_t * p_user, app_timer_id_t * p_last_index) 00771 { 00772 app_timer_id_t last; 00773 timer_user_op_t * p_user_op; 00774 00775 last = p_user->last + 1; 00776 if (last == p_user->user_op_queue_size) 00777 { 00778 // Overflow case. 00779 last = 0; 00780 } 00781 if (last == p_user->first) 00782 { 00783 // Queue is full. 00784 return NULL; 00785 } 00786 00787 *p_last_index = last; 00788 p_user_op = &p_user->p_user_op_queue[p_user->last]; 00789 00790 return p_user_op; 00791 } 00792 00793 00794 /**@brief Function for scheduling a Timer Start operation. 00795 * 00796 * @param[in] user_id Id of user calling this function. 00797 * @param[in] timer_id Id of timer to start. 00798 * @param[in] timeout_initial Time (in ticks) to first timer expiry. 00799 * @param[in] timeout_periodic Time (in ticks) between periodic expiries. 00800 * @param[in] p_context General purpose pointer. Will be passed to the timeout handler when 00801 * the timer expires. 00802 * @return NRF_SUCCESS on success, otherwise an error code. 00803 */ 00804 static uint32_t timer_start_op_schedule(timer_user_id_t user_id, 00805 app_timer_id_t timer_id, 00806 uint32_t timeout_initial, 00807 uint32_t timeout_periodic, 00808 void * p_context) 00809 { 00810 app_timer_id_t last_index; 00811 00812 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index); 00813 if (p_user_op == NULL) 00814 { 00815 return NRF_ERROR_NO_MEM; 00816 } 00817 00818 p_user_op->op_type = TIMER_USER_OP_TYPE_START; 00819 p_user_op->timer_id = timer_id; 00820 p_user_op->params.start.ticks_at_start = rtc1_counter_get(); 00821 p_user_op->params.start.ticks_first_interval = timeout_initial; 00822 p_user_op->params.start.ticks_periodic_interval = timeout_periodic; 00823 p_user_op->params.start.p_context = p_context; 00824 00825 user_op_enque(&mp_users[user_id], last_index); 00826 00827 timer_list_handler_sched(); 00828 00829 return NRF_SUCCESS; 00830 } 00831 00832 00833 /**@brief Function for scheduling a Timer Stop operation. 00834 * 00835 * @param[in] user_id Id of user calling this function. 00836 * @param[in] timer_id Id of timer to stop. 00837 * 00838 * @return NRF_SUCCESS on successful scheduling a timer stop operation. NRF_ERROR_NO_MEM when there 00839 * is no memory left to schedule the timer stop operation. 00840 */ 00841 static uint32_t timer_stop_op_schedule(timer_user_id_t user_id, app_timer_id_t timer_id) 00842 { 00843 app_timer_id_t last_index; 00844 00845 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index); 00846 if (p_user_op == NULL) 00847 { 00848 return NRF_ERROR_NO_MEM; 00849 } 00850 00851 p_user_op->op_type = TIMER_USER_OP_TYPE_STOP; 00852 p_user_op->timer_id = timer_id; 00853 00854 user_op_enque(&mp_users[user_id], last_index); 00855 00856 timer_list_handler_sched(); 00857 00858 return NRF_SUCCESS; 00859 } 00860 00861 00862 /**@brief Function for scheduling a Timer Stop All operation. 00863 * 00864 * @param[in] user_id Id of user calling this function. 00865 */ 00866 static uint32_t timer_stop_all_op_schedule(timer_user_id_t user_id) 00867 { 00868 app_timer_id_t last_index; 00869 00870 timer_user_op_t * p_user_op = user_op_alloc(&mp_users[user_id], &last_index); 00871 if (p_user_op == NULL) 00872 { 00873 return NRF_ERROR_NO_MEM; 00874 } 00875 00876 p_user_op->op_type = TIMER_USER_OP_TYPE_STOP_ALL; 00877 p_user_op->timer_id = TIMER_NULL; 00878 00879 user_op_enque(&mp_users[user_id], last_index); 00880 00881 timer_list_handler_sched(); 00882 00883 return NRF_SUCCESS; 00884 } 00885 00886 00887 /**@brief Function for handling the RTC1 interrupt. 00888 * 00889 * @details Checks for timeouts, and executes timeout handlers for expired timers. 00890 */ 00891 extern "C" void RTC1_IRQHandler(void) 00892 { 00893 // Clear all events (also unexpected ones) 00894 NRF_RTC1->EVENTS_COMPARE[0] = 0; 00895 NRF_RTC1->EVENTS_COMPARE[1] = 0; 00896 NRF_RTC1->EVENTS_COMPARE[2] = 0; 00897 NRF_RTC1->EVENTS_COMPARE[3] = 0; 00898 NRF_RTC1->EVENTS_TICK = 0; 00899 NRF_RTC1->EVENTS_OVRFLW = 0; 00900 00901 // Check for expired timers 00902 timer_timeouts_check(); 00903 } 00904 00905 /**@brief Function for handling the SWI0 interrupt. 00906 * 00907 * @details Performs all updates to the timer list. 00908 */ 00909 extern "C" void SWI0_IRQHandler(void) 00910 { 00911 timer_list_handler(); 00912 } 00913 00914 uint32_t app_timer_init(uint32_t prescaler, 00915 uint8_t max_timers, 00916 uint8_t op_queues_size, 00917 void * p_buffer, 00918 app_timer_evt_schedule_func_t evt_schedule_func) 00919 { 00920 int i; 00921 00922 // Check that buffer is correctly aligned 00923 if (!is_word_aligned(p_buffer)) 00924 { 00925 return NRF_ERROR_INVALID_PARAM; 00926 } 00927 // Check for NULL buffer 00928 if (p_buffer == NULL) 00929 { 00930 return NRF_ERROR_INVALID_PARAM; 00931 } 00932 00933 // Stop RTC to prevent any running timers from expiring (in case of reinitialization) 00934 rtc1_stop(); 00935 00936 m_evt_schedule_func = evt_schedule_func; 00937 00938 // Initialize timer node array 00939 m_node_array_size = max_timers; 00940 mp_nodes = (timer_node_t *) p_buffer; 00941 00942 for (i = 0; i < max_timers; i++) 00943 { 00944 mp_nodes[i].state = STATE_FREE; 00945 mp_nodes[i].is_running = false; 00946 } 00947 00948 // Skip timer node array 00949 p_buffer = &((uint8_t *)p_buffer)[max_timers * sizeof(timer_node_t)]; 00950 00951 // Initialize users array 00952 m_user_array_size = APP_TIMER_INT_LEVELS; 00953 mp_users = (timer_user_t *) p_buffer; 00954 00955 // Skip user array 00956 p_buffer = &((uint8_t *)p_buffer)[APP_TIMER_INT_LEVELS * sizeof(timer_user_t)]; 00957 00958 // Initialize operation queues 00959 for (i = 0; i < APP_TIMER_INT_LEVELS; i++) 00960 { 00961 timer_user_t * p_user = &mp_users[i]; 00962 00963 p_user->first = 0; 00964 p_user->last = 0; 00965 p_user->user_op_queue_size = op_queues_size; 00966 p_user->p_user_op_queue = (timer_user_op_t *) p_buffer; 00967 00968 // Skip operation queue 00969 p_buffer = &((uint8_t *)p_buffer)[op_queues_size * sizeof(timer_user_op_t)]; 00970 } 00971 00972 m_timer_id_head = TIMER_NULL; 00973 m_ticks_elapsed_q_read_ind = 0; 00974 m_ticks_elapsed_q_write_ind = 0; 00975 00976 NVIC_ClearPendingIRQ(SWI0_IRQn); 00977 NVIC_SetPriority(SWI0_IRQn, SWI0_IRQ_PRI); 00978 NVIC_EnableIRQ(SWI0_IRQn); 00979 00980 rtc1_init(prescaler); 00981 00982 m_ticks_latest = rtc1_counter_get(); 00983 00984 return NRF_SUCCESS; 00985 } 00986 00987 00988 uint32_t app_timer_create(app_timer_id_t * p_timer_id, 00989 app_timer_mode_t mode, 00990 app_timer_timeout_handler_t timeout_handler) 00991 { 00992 int i; 00993 00994 // Check state and parameters 00995 if (mp_nodes == NULL) 00996 { 00997 return NRF_ERROR_INVALID_STATE; 00998 } 00999 if (timeout_handler == NULL) 01000 { 01001 return NRF_ERROR_INVALID_PARAM; 01002 } 01003 if (p_timer_id == NULL) 01004 { 01005 return NRF_ERROR_INVALID_PARAM; 01006 } 01007 01008 // Find free timer 01009 for (i = 0; i < m_node_array_size; i++) 01010 { 01011 if (mp_nodes[i].state == STATE_FREE) 01012 { 01013 mp_nodes[i].state = STATE_ALLOCATED; 01014 mp_nodes[i].mode = mode; 01015 mp_nodes[i].p_timeout_handler = timeout_handler; 01016 01017 *p_timer_id = i; 01018 return NRF_SUCCESS; 01019 } 01020 } 01021 01022 return NRF_ERROR_NO_MEM; 01023 } 01024 01025 01026 /**@brief Function for creating a timer user id from the current interrupt level. 01027 * 01028 * @return Timer user id. 01029 */ 01030 static timer_user_id_t user_id_get(void) 01031 { 01032 timer_user_id_t ret; 01033 01034 STATIC_ASSERT(APP_TIMER_INT_LEVELS == 3); 01035 01036 switch (current_int_priority_get()) 01037 { 01038 case APP_IRQ_PRIORITY_HIGH: 01039 ret = APP_HIGH_USER_ID; 01040 break; 01041 01042 case APP_IRQ_PRIORITY_LOW: 01043 ret = APP_LOW_USER_ID; 01044 break; 01045 01046 default: 01047 ret = THREAD_MODE_USER_ID; 01048 break; 01049 } 01050 01051 return ret; 01052 } 01053 01054 01055 uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context) 01056 { 01057 uint32_t timeout_periodic; 01058 01059 // Check state and parameters 01060 if (mp_nodes == NULL) 01061 { 01062 return NRF_ERROR_INVALID_STATE; 01063 } 01064 if ((timer_id >= m_node_array_size) || (timeout_ticks < APP_TIMER_MIN_TIMEOUT_TICKS)) 01065 { 01066 return NRF_ERROR_INVALID_PARAM; 01067 } 01068 if (mp_nodes[timer_id].state != STATE_ALLOCATED) 01069 { 01070 return NRF_ERROR_INVALID_STATE; 01071 } 01072 01073 // Schedule timer start operation 01074 timeout_periodic = (mp_nodes[timer_id].mode == APP_TIMER_MODE_REPEATED) ? timeout_ticks : 0; 01075 01076 return timer_start_op_schedule(user_id_get(), 01077 timer_id, 01078 timeout_ticks, 01079 timeout_periodic, 01080 p_context); 01081 } 01082 01083 01084 uint32_t app_timer_stop(app_timer_id_t timer_id) 01085 { 01086 // Check state and parameters 01087 if (mp_nodes == NULL) 01088 { 01089 return NRF_ERROR_INVALID_STATE; 01090 } 01091 if (timer_id >= m_node_array_size) 01092 { 01093 return NRF_ERROR_INVALID_PARAM; 01094 } 01095 if (mp_nodes[timer_id].state != STATE_ALLOCATED) 01096 { 01097 return NRF_ERROR_INVALID_STATE; 01098 } 01099 01100 // Schedule timer stop operation 01101 return timer_stop_op_schedule(user_id_get(), timer_id); 01102 } 01103 01104 01105 uint32_t app_timer_stop_all(void) 01106 { 01107 // Check state 01108 if (mp_nodes == NULL) 01109 { 01110 return NRF_ERROR_INVALID_STATE; 01111 } 01112 01113 return timer_stop_all_op_schedule(user_id_get()); 01114 } 01115 01116 01117 uint32_t app_timer_cnt_get(uint32_t * p_ticks) 01118 { 01119 *p_ticks = rtc1_counter_get(); 01120 return NRF_SUCCESS; 01121 } 01122 01123 01124 uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to, 01125 uint32_t ticks_from, 01126 uint32_t * p_ticks_diff) 01127 { 01128 *p_ticks_diff = ticks_diff_get(ticks_to, ticks_from); 01129 return NRF_SUCCESS; 01130 }
Generated on Tue Jul 12 2022 18:44:25 by
