Timer
4.6. Timer
4.6.1. Overview
The system timer peripheral on RP2040 provides a global microsecond timebase for the system, and generates interrupts based on this timebase. It supports the following features:
-
[A single 64-bit counter, incrementing once per microsecond]
-
[This counter can be read from a pair of latching registers, for race-free reads over a 32-bit bus.]
-
[Four alarms: match on the lower 32 bits of counter, IRQ on match.]
The timer uses a one microsecond reference that is generated in the Watchdog (see Section 4.7.2), and derived from
4.6. Timer
534
RP2040 Datasheet
the reference clock (Figure 28), which itself is usually connected directly to the crystal oscillator (Section 2.16).
The 64-bit counter effectively can not overflow (thousands of years at 1MHz), so the system timer is completely monotonic in practice.
4.6.1.1. Other Timer Resources on RP2040
The system timer is intended to provide a global timebase for software. RP2040 has a number of other programmable counter resources which can provide regular interrupts, or trigger DMA transfers.
-
[The PWM (][Section 4.5][) contains 8× 16-bit programmable counters, which run at up to system speed, can generate] interrupts, and can be continuously reprogrammed via the DMA, or trigger DMA transfers to other peripherals.
-
[8× PIO state machines (][Chapter 3][) can count 32-bit values at system speed, and generate interrupts.]
-
[The DMA (][Section 2.5][) has four internal pacing timers, which trigger transfers at regular intervals.]
-
[Each Cortex-M0+ core (][Section 2.4][) has a standard 24-bit SysTick timer, counting either the microsecond tick] (Section 4.7.2) or the system clock.
4.6.2. Counter
The timer has a 64-bit counter, but RP2040 only has a 32-bit data bus. This means that the TIME value is accessed through a pair of registers. These are:
-
[TIMEHW][ and ][TIMELW][ to write the time]
-
[TIMEHR][ and ][TIMELR][ to read the time]
These pairs are used by accessing the lower register, L, followed by the higher register, H. In the read case, reading the L register latches the value in the H register so that an accurate time can be read. Alternatively, TIMERAWH and TIMERAWL can be used to read the raw time without any latching.
CAUTION
While it is technically possible to force a new time value by writing to the TIMEHW and TIMELW registers, programmers are discouraged from doing this. This is because the timer value is expected to be monotonically increasing by the SDK which uses it for timeouts, elapsed time etc.
4.6.3. Alarms
The timer has 4 alarms, and outputs a separate interrupt for each alarm. The alarms match on the lower 32 bits of the 64-bit counter which means they can be fired at a maximum of 2[32] microseconds into the future. This is equivalent to:
-
[2][32][ ÷ 10][6][: ~4295 seconds]
-
[4295 ÷ 60: ~72 minutes]
NOTE
This timer is expected to be used for short sleeps. If you want a longer alarm see Section 4.8.
To enable an alarm:
-
[Enable the interrupt at the timer with a write to the appropriate alarm bit in ][INTE][: i.e. ][(1 << 0)][ for ][ALARM0]
-
[Enable the appropriate timer interrupt at the processor (see ][Section 2.3.2][)]
-
[Write the time you would like the interrupt to fire to ][ALARM0][ (i.e. the current value in ][TIMERAWL][ plus your desired] alarm time in microseconds). Writing the time to the ALARM register sets the ARMED bit as a side effect.
4.6. Timer
535
RP2040 Datasheet
Once the alarm has fired, the ARMED bit will be set to 0. To clear the latched interrupt, write a 1 to the appropriate bit in INTR.
4.6.4. Programmer’s Model
NOTE
The Watchdog tick (see Section 4.7.2) must be running for the timer to start counting. The SDK starts this tick as part of the platform initialisation code.
4.6.4.1. Reading the time
NOTE
Time here refers to the number of microseconds since the timer was started, it is not a clock. For that - see Section 4.8.
The simplest form of reading the 64-bit time is to read TIMELR followed by TIMEHR. However, because RP2040 has 2 cores, it is unsafe to do this if the second core is executing code that can also access the timer, or if the timer is read concurrently in an IRQ handler and in thread mode. This is because reading TIMELR latches the value in TIMEHR (i.e. stops it updating) until TIMEHR is read. If one core reads TIMELR followed by another core reading TIMELR, the value in TIMEHR isn’t necessarily accurate. The example below shows the simplest form of getting the 64-bit time.
Pico Examples: https://github.com/raspberrypi/pico-examples/blob/master/timer/timer_lowlevel/timer_lowlevel.c Lines 15 - 23
15 // Simplest form of getting 64 bit time from the timer. 16 // It isn't safe when called from 2 cores because of the latching 17 // so isn't implemented this way in the sdk 18 static uint64_t get_time(void) { 19 // Reading low latches the high value 20 uint32_t lo = timer_hw->timelr; 21 uint32_t hi = timer_hw->timehr; 22 return ((uint64_t) hi << 32u) | lo; 23 }
The SDK provides a time_us_64 function that uses a more thorough method to get the 64-bit time, which makes use of the TIMERAWH and TIMERAWL registers. The RAW registers don’t latch, and therefore make time_us_64 safe to call from multiple cores at once.
SDK: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_timer/timer.c Lines 57 - 73
57 uint64_t timer_time_us_64(timer_hw_t *timer) { 58 // Need to make sure that the upper 32 bits of the timer 59 // don't change, so read that first 60 uint32_t hi = timer->timerawh; 61 uint32_t lo; 62 do { 63 // Read the lower 32 bits 64 lo = timer->timerawl; 65 // Now read the upper 32 bits again and 66 // check that it hasn't incremented. If it has loop around 67 // and read the lower 32 bits again to get an accurate value 68 uint32_t next_hi = timer->timerawh; 69 if (hi == next_hi) break; 70 hi = next_hi;
4.6. Timer
536
RP2040 Datasheet
71 } while (true); 72 return ((uint64_t) hi << 32u) | lo; 73 }
4.6.4.2. Set an alarm
The standalone timer example, timer_lowlevel, demonstrates how to set an alarm at a hardware level, without the additional abstraction over the timer that the SDK provides. To use these abstractions see Section 4.6.4.4.
Pico Examples: https://github.com/raspberrypi/pico-examples/blob/master/timer/timer_lowlevel/timer_lowlevel.c Lines 27 - 74
27 // Use alarm 0 28 #define ALARM_NUM 0 29 #define ALARM_IRQ timer_hardware_alarm_get_irq_num(timer_hw, ALARM_NUM) 30 31 // Alarm interrupt handler 32 static volatile bool alarm_fired; 33 34 static void alarm_irq(void) { 35 // Clear the alarm irq 36 hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM); 37 38 // Assume alarm 0 has fired 39 printf("Alarm IRQ fired\n"); 40 alarm_fired = true; 41 } 42 43 static void alarm_in_us(uint32_t delay_us) { 44 // Enable the interrupt for our alarm (the timer outputs 4 alarm irqs) 45 hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM); 46 // Set irq handler for alarm irq 47 irq_set_exclusive_handler(ALARM_IRQ, alarm_irq); 48 // Enable the alarm irq 49 irq_set_enabled(ALARM_IRQ, true); 50 // Enable interrupt in block and at processor 51 52 // Alarm is only 32 bits so if trying to delay more 53 // than that need to be careful and keep track of the upper 54 // bits 55 uint64_t target = timer_hw->timerawl + delay_us; 56 57 // Write the lower 32 bits of the target time to the alarm which 58 // will arm it 59 timer_hw->alarm[ALARM_NUM] = (uint32_t) target; 60 } 61 62 int main() { 63 stdio_init_all(); 64 printf("Timer lowlevel!\n"); 65 66 // Set alarm every 2 seconds 67 while (1) { 68 alarm_fired = false; 69 alarm_in_us(1000000 * 2); 70 // Wait for alarm to fire 71 while (!alarm_fired); 72 } 73 }
4.6. Timer
537
RP2040 Datasheet
4.6.4.3. Busy wait
If you don’t want to use an alarm to wait for a period of time, instead use a while loop. The SDK provides various busy_wait_ functions to do this:
SDK: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_timer/timer.c Lines 77 - 122
77 void timer_busy_wait_us_32(timer_hw_t timer, uint32_t delay_us) { 78 if (0 <= (int32_t)delay_us) { 79 // we only allow 31 bits, otherwise we could have a race in the loop below with 80 // values very close to 2^32 81 uint32_t start = timer->timerawl; 82 while (timer->timerawl - start < delay_us) { 83 tight_loop_contents(); 84 } 85 } else { 86 busy_wait_us(delay_us); 87 } 88 } 89 90 void timer_busy_wait_us(timer_hw_t timer, uint64_t delay_us) { 91 uint64_t base = timer_time_us_64(timer); 92 uint64_t target = base + delay_us; 93 if (target < base) { 94 target = (uint64_t)-1; 95 } 96 absolute_time_t t; 97 update_us_since_boot(&t, target); 98 timer_busy_wait_until(timer, t); 99 } 100 101 void timer_busy_wait_ms(timer_hw_t timer, uint32_t delay_ms) 102 { 103 if (delay_ms <= 0x7fffffffu / 1000) { 104 timer_busy_wait_us_32(timer, delay_ms * 1000); 105 } else { 106 timer_busy_wait_us(timer, delay_ms * 1000ull); 107 } 108 } 109 110 void timer_busy_wait_until(timer_hw_t timer, absolute_time_t t) { 111 uint64_t target = to_us_since_boot(t); 112 uint32_t hi_target = (uint32_t)(target >> 32u); 113 uint32_t hi = timer->timerawh; 114 while (hi < hi_target) { 115 hi = timer->timerawh; 116 tight_loop_contents(); 117 } 118 while (hi == hi_target && timer->timerawl < (uint32_t) target) { 119 hi = timer->timerawh; 120 tight_loop_contents(); 121 } 122 }
4.6.4.4. Complete example using SDK
4.6. Timer
538
RP2040 Datasheet
Pico Examples: https://github.com/raspberrypi/pico-examples/blob/master/timer/hello_timer/hello_timer.c Lines 11 - 57
11 volatile bool timer_fired = false; 12 13 int64_t alarm_callback(alarm_id_t id, __unused void user_data) { 14 printf("Timer %d fired!\n", (int) id); 15 timer_fired = true; 16 // Can return a value here in us to fire in the future 17 return 0; 18 } 19 20 bool repeating_timer_callback(__unused struct repeating_timer t) { 21 printf("Repeat at %lld\n", time_us_64()); 22 return true; 23 } 24 25 int main() { 26 stdio_init_all(); 27 printf("Hello Timer!\n"); 28 29 // Call alarm_callback in 2 seconds 30 add_alarm_in_ms(2000, alarm_callback, NULL, false); 31 32 // Wait for alarm callback to set timer_fired 33 while (!timer_fired) { 34 tight_loop_contents(); 35 } 36 37 // Create a repeating timer that calls repeating_timer_callback. 38 // If the delay is > 0 then this is the delay between the previous callback ending and the next starting. 39 // If the delay is negative (see below) then the next call to the callback will be exactly 500ms after the 40 // start of the call to the last callback 41 struct repeating_timer timer; 42 add_repeating_timer_ms(500, repeating_timer_callback, NULL, &timer); 43 sleep_ms(3000); 44 bool cancelled = cancel_repeating_timer(&timer); 45 printf("cancelled... %d\n", cancelled); 46 sleep_ms(2000); 47 48 // Negative delay so means we will call repeating_timer_callback, and call it again 49 // 500ms later regardless of how long the callback took to execute 50 add_repeating_timer_ms(-500, repeating_timer_callback, NULL, &timer); 51 sleep_ms(3000); 52 cancelled = cancel_repeating_timer(&timer); 53 printf("cancelled... %d\n", cancelled); 54 sleep_ms(2000); 55 printf("Done\n"); 56 return 0; 57 }
4.6.5. List of Registers
The Timer registers start at a base address of 0x40054000 (defined as TIMER_BASE in SDK).
Table 527. List of TIMER registers
| Offset | Name | Info |
|---|---|---|
| 0x00 | TIMEHW | Write to bits 63:32 of time always write timelw before timehw |
4.6. Timer
539
RP2040 Datasheet
| Offset | Name | Info |
|---|---|---|
| 0x04 | TIMELW | Write to bits 31:0 of time writes do not get copied to time until timehw is written |
| 0x08 | TIMEHR | Read from bits 63:32 of time always read timelr before timehr |
| 0x0c | TIMELR | Read from bits 31:0 of time |
| 0x10 | ALARM0 | Arm alarm 0, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM0 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
| 0x14 | ALARM1 | Arm alarm 1, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM1 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
| 0x18 | ALARM2 | Arm alarm 2, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM2 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
| 0x1c | ALARM3 | Arm alarm 3, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM3 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
| 0x20 | ARMED | Indicates the armed/disarmed status of each alarm. A write to the corresponding ALARMx register arms the alarm. Alarms automatically disarm upon firing, but writing ones here will disarm immediately without waiting to fire. |
| 0x24 | TIMERAWH | Raw read from bits 63:32 of time (no side effects) |
| 0x28 | TIMERAWL | Raw read from bits 31:0 of time (no side effects) |
| 0x2c | DBGPAUSE | Set bits high to enable pause when the corresponding debug ports are active |
| 0x30 | PAUSE | Set high to pause the timer |
| 0x34 | INTR | Raw Interrupts |
| 0x38 | INTE | Interrupt Enable |
| 0x3c | INTF | Interrupt Force |
| 0x40 | INTS | Interrupt status after masking & forcing |
TIMER: TIMEHW Register
Offset : 0x00
4.6. Timer
540
RP2040 Datasheet
| Table 528. TIMEHW Register Table 529. TIMELW Register Table 530. TIMEHR Register Table 531. TIMELR Register Table 532. ALARM0 Register Table 533. ALARM1 Register |
Bits | Description | Type | Reset |
|---|---|---|---|---|
| 31:0 | Write to bits 63:32 of time always write timelw before timehw |
WF | 0x00000000 | |
| TIMER: TIMELW Register Offset: 0x04 |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Write to bits 31:0 of time writes do not get copied to time until timehw is written |
WF | 0x00000000 | |
| TIMER: TIMEHR Register Offset: 0x08 |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Read from bits 63:32 of time always read timelr before timehr |
RO | 0x00000000 | |
| TIMER: TIMELR Register Offset: 0x0c |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Read from bits 31:0 of time | RO | 0x00000000 | |
| TIMER: ALARM0 Register Offset: 0x10 |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Arm alarm 0, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM0 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
RW | 0x00000000 | |
| TIMER: ALARM1 Register Offset: 0x14 |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Arm alarm 1, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM1 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
RW | 0x00000000 |
TIMER: ALARM2 Register
Offset : 0x18
Table 534. ALARM2 Register
4.6. Timer
541
RP2040 Datasheet
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:0 | Arm alarm 2, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM2 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
RW | 0x00000000 |
TIMER: ALARM3 Register
Offset : 0x1c
| Table 535. ALARM3 Register Table 536. ARMED Register |
Bits | Description | Type | Reset |
|---|---|---|---|---|
| 31:0 | Arm alarm 3, and configure the time it will fire. Once armed, the alarm fires when TIMER_ALARM3 == TIMELR. The alarm will disarm itself once it fires, and can be disarmed early using the ARMED status register. |
RW | 0x00000000 | |
| TIMER: ARMED Register Offset: 0x20 |
||||
| Bits | Description | Type | Reset | |
| 31:4 | Reserved. | - | - | |
| 3:0 | Indicates the armed/disarmed status of each alarm. A write to the corresponding ALARMx register arms the alarm. Alarms automatically disarm upon firing, but writing ones here will disarm immediately without waiting to fire. |
WC | 0x0 |
TIMER: TIMERAWH Register
Offset : 0x24
| Table 537. TIMERAWH Register Table 538. TIMERAWL Register |
Bits | Description | Type | Reset |
|---|---|---|---|---|
| 31:0 | Raw read from bits 63:32 of time (no side effects) | RO | 0x00000000 | |
| TIMER: TIMERAWL Register Offset: 0x28 |
||||
| Bits | Description | Type | Reset | |
| 31:0 | Raw read from bits 31:0 of time (no side effects) | RO | 0x00000000 | |
| TIMER: DBGPAUSE Register Offset: 0x2c |
Description
Set bits high to enable pause when the corresponding debug ports are active
| Table 539. DBGPAUSE Register |
Bits | Description | Type | Reset |
|---|---|---|---|---|
| 31:3 | Reserved. | - | - | |
| 2 | DBG1: Pause when processor 1 is in debug mode | RW | 0x1 | |
| 1 | DBG0: Pause when processor 0 is in debug mode | RW | 0x1 |
4.6. Timer
542
RP2040 Datasheet
| Bits | Description | Type | Reset |
|---|---|---|---|
| 0 | Reserved. | - | - |
TIMER: PAUSE Register
Offset : 0x30
Table 540. PAUSE Register
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:1 | Reserved. | - | - |
| 0 | Set high to pause the timer | RW | 0x0 |
TIMER: INTR Register
Offset : 0x34
Description
Raw Interrupts
Table 541. INTR Register
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:4 | Reserved. | - | - |
| 3 | ALARM_3 | WC | 0x0 |
| 2 | ALARM_2 | WC | 0x0 |
| 1 | ALARM_1 | WC | 0x0 |
| 0 | ALARM_0 | WC | 0x0 |
TIMER: INTE Register
Offset : 0x38
Description
Interrupt Enable
Table 542. INTE Register
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:4 | Reserved. | - | - |
| 3 | ALARM_3 | RW | 0x0 |
| 2 | ALARM_2 | RW | 0x0 |
| 1 | ALARM_1 | RW | 0x0 |
| 0 | ALARM_0 | RW | 0x0 |
TIMER: INTF Register
Offset : 0x3c
Description
Interrupt Force
Table 543. INTF Register
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:4 | Reserved. | - | - |
| 3 | ALARM_3 | RW | 0x0 |
4.6. Timer
543
RP2040 Datasheet
| Bits | Description | Type | Reset |
|---|---|---|---|
| 2 | ALARM_2 | RW | 0x0 |
| 1 | ALARM_1 | RW | 0x0 |
| 0 | ALARM_0 | RW | 0x0 |
TIMER: INTS Register
Offset : 0x40
Description
Interrupt status after masking & forcing
Table 544. INTS Register
| Bits | Description | Type | Reset |
|---|---|---|---|
| 31:4 | Reserved. | - | - |
| 3 | ALARM_3 | RO | 0x0 |
| 2 | ALARM_2 | RO | 0x0 |
| 1 | ALARM_1 | RO | 0x0 |
| 0 | ALARM_0 | RO | 0x0 |