1 /*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #ifndef _HARDWARE_TIMER_H
8 #define _HARDWARE_TIMER_H
9
10 #include "pico.h"
11 #include "hardware/structs/timer.h"
12
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16
17 /** \file hardware/timer.h
18 * \defgroup hardware_timer hardware_timer
19 *
20 * Low-level hardware timer API
21 *
22 * This API provides medium level access to the timer HW.
23 * See also \ref pico_time which provides higher levels functionality using the hardware timer.
24 *
25 * The timer peripheral on RP2040 supports the following features:
26 * - single 64-bit counter, incrementing once per microsecond
27 * - Latching two-stage read of counter, for race-free read over 32 bit bus
28 * - Four alarms: match on the lower 32 bits of counter, IRQ on match.
29 *
30 * By default the timer uses a one microsecond reference that is generated in the Watchdog (see Section 4.8.2) which is derived
31 * from the clk_ref.
32 *
33 * The timer has 4 alarms, and can output a separate interrupt for each alarm. The alarms match on the lower 32 bits of the 64
34 * bit counter which means they can be fired a maximum of 2^32 microseconds into the future. This is equivalent to:
35 * - 2^32 ÷ 10^6: ~4295 seconds
36 * - 4295 ÷ 60: ~72 minutes
37 *
38 * The timer is expected to be used for short sleeps, if you want a longer alarm see the \ref hardware_rtc functions.
39 *
40 * \subsection timer_example Example
41 * \addtogroup hardware_timer
42 *
43 * \include hello_timer.c
44 *
45 * \see pico_time
46 */
47
48 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_TIMER, Enable/disable assertions in the timer module, type=bool, default=0, group=hardware_timer
49 #ifndef PARAM_ASSERTIONS_ENABLED_TIMER
50 #define PARAM_ASSERTIONS_ENABLED_TIMER 0
51 #endif
52
check_hardware_alarm_num_param(uint alarm_num)53 static inline void check_hardware_alarm_num_param(uint alarm_num) {
54 invalid_params_if(TIMER, alarm_num >= NUM_TIMERS);
55 }
56
57 /*! \brief Return a 32 bit timestamp value in microseconds
58 * \ingroup hardware_timer
59 *
60 * Returns the low 32 bits of the hardware timer.
61 * \note This value wraps roughly every 1 hour 11 minutes and 35 seconds.
62 *
63 * \return the 32 bit timestamp
64 */
time_us_32(void)65 static inline uint32_t time_us_32(void) {
66 return timer_hw->timerawl;
67 }
68
69 /*! \brief Return the current 64 bit timestamp value in microseconds
70 * \ingroup hardware_timer
71 *
72 * Returns the full 64 bits of the hardware timer. The \ref pico_time and other functions rely on the fact that this
73 * value monotonically increases from power up. As such it is expected that this value counts upwards and never wraps
74 * (we apologize for introducing a potential year 5851444 bug).
75 *
76 * \return the 64 bit timestamp
77 */
78 uint64_t time_us_64(void);
79
80 /*! \brief Busy wait wasting cycles for the given (32 bit) number of microseconds
81 * \ingroup hardware_timer
82 *
83 * \param delay_us delay amount
84 */
85 void busy_wait_us_32(uint32_t delay_us);
86
87 /*! \brief Busy wait wasting cycles for the given (64 bit) number of microseconds
88 * \ingroup hardware_timer
89 *
90 * \param delay_us delay amount
91 */
92 void busy_wait_us(uint64_t delay_us);
93
94 /*! \brief Busy wait wasting cycles until after the specified timestamp
95 * \ingroup hardware_timer
96 *
97 * \param t Absolute time to wait until
98 */
99 void busy_wait_until(absolute_time_t t);
100
101 /*! \brief Check if the specified timestamp has been reached
102 * \ingroup hardware_timer
103 *
104 * \param t Absolute time to compare against current time
105 * \return true if it is now after the specified timestamp
106 */
time_reached(absolute_time_t t)107 static inline bool time_reached(absolute_time_t t) {
108 uint64_t target = to_us_since_boot(t);
109 uint32_t hi_target = target >> 32u;
110 uint32_t hi = timer_hw->timerawh;
111 return (hi >= hi_target && (timer_hw->timerawl >= (uint32_t) target || hi != hi_target));
112 }
113
114 /*! Callback function type for hardware alarms
115 * \ingroup hardware_timer
116 *
117 * \param alarm_num the hardware alarm number
118 * \sa hardware_alarm_set_callback
119 */
120 typedef void (*hardware_alarm_callback_t)(uint alarm_num);
121
122 /*! \brief cooperatively claim the use of this hardware alarm_num
123 * \ingroup hardware_timer
124 *
125 * This method hard asserts if the hardware alarm is currently claimed.
126 *
127 * \param alarm_num the hardware alarm to claim
128 * \sa hardware_claiming
129 */
130 void hardware_alarm_claim(uint alarm_num);
131
132 /*! \brief cooperatively release the claim on use of this hardware alarm_num
133 * \ingroup hardware_timer
134 *
135 * \param alarm_num the hardware alarm to unclaim
136 * \sa hardware_claiming
137 */
138 void hardware_alarm_unclaim(uint alarm_num);
139
140 /*! \brief Enable/Disable a callback for a hardware timer on this core
141 * \ingroup hardware_timer
142 *
143 * This method enables/disables the alarm IRQ for the specified hardware alarm on the
144 * calling core, and set the specified callback to be associated with that alarm.
145 *
146 * This callback will be used for the timeout set via hardware_alarm_set_target
147 *
148 * \note This will install the handler on the current core if the IRQ handler isn't already set.
149 * Therefore the user has the opportunity to call this up from the core of their choice
150 *
151 * \param alarm_num the hardware alarm number
152 * \param callback the callback to install, or NULL to unset
153 *
154 * \sa hardware_alarm_set_target
155 */
156 void hardware_alarm_set_callback(uint alarm_num, hardware_alarm_callback_t callback);
157
158 /**
159 * \brief Set the current target for the specified hardware alarm
160 *
161 * This will replace any existing target
162 *
163 * @param alarm_num the hardware alarm number
164 * @param t the target timestamp
165 * @return true if the target was "missed"; i.e. it was in the past, or occurred before a future hardware timeout could be set
166 */
167 bool hardware_alarm_set_target(uint alarm_num, absolute_time_t t);
168
169 /**
170 * \brief Cancel an existing target (if any) for a given hardware_alarm
171 *
172 * @param alarm_num
173 */
174
175 void hardware_alarm_cancel(uint alarm_num);
176
177 #ifdef __cplusplus
178 }
179 #endif
180 #endif
181