Hi,
I discovered a weired behaviour when using timer alarms on RP2040. I used it with low level functions of the SDK and could even reproduce it with the low level timer example of the SDK.
The first call of the ISR after initialising the timer alarm needs more than 10 times execution time than all consecutive calls.
Did anyone already have the same experience? I tried on pi pico 1 and 2 and on Nano RP2040 connect.
It's the same everywhere.
I used the core of Earle Philhower, which includes the standard RP2040 SDK.
This is the example code from the SDK. It's slightly changed to Arduino needs and an added GPIO output to measure the execution time via an oscilloscope
The first alarm match needs 1.2 µs within the ISR, while all subsequent alarms need less than 100ns. With a more complex ISR it is the same - the first call needs about 10 times as much execution time as all subsequent calls.
/**
Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/stdlib.h"
#include "hardware/timer.h"
#include "hardware/irq.h"
constexpr uint8_t TP1 = 25; // timing measurement
// Note on RP2350 timer_hw is the default timer instance (as per PICO_DEFAULT_TIMER)
/// \tag::get_time[]
// Simplest form of getting 64 bit time from the timer.
// It isn't safe when called from 2 cores because of the latching
// so isn't implemented this way in the sdk
static uint64_t get_time(void) {
// Reading low latches the high value
uint32_t lo = timer_hw->timelr;
uint32_t hi = timer_hw->timehr;
return ((uint64_t) hi << 32u) | lo;
}
/// \end::get_time[]
/// \tag::alarm_standalone[]
// Use alarm 0
#define ALARM_NUM 0
#define ALARM_IRQ timer_hardware_alarm_get_irq_num(timer_hw, ALARM_NUM)
// Alarm interrupt handler
static volatile bool alarm_fired;
static void alarm_irq(void) {
// Clear the alarm irq
gpio_set_mask( 1<<TP1 );
hw_clear_bits(&timer_hw->intr, 1u << ALARM_NUM);
// Assume alarm 0 has fired
//Serial.printf("Alarm IRQ fired\n");
alarm_fired = true;
gpio_clr_mask( 1<<TP1 );
}
static void alarm_in_us(uint32_t delay_us) {
// Enable the interrupt for our alarm (the timer outputs 4 alarm irqs)
hw_set_bits(&timer_hw->inte, 1u << ALARM_NUM);
// Set irq handler for alarm irq
irq_set_exclusive_handler(ALARM_IRQ, alarm_irq);
// Enable the alarm irq
irq_set_enabled(ALARM_IRQ, true);
// Enable interrupt in block and at processor
// Alarm is only 32 bits so if trying to delay more
// than that need to be careful and keep track of the upper
// bits
uint64_t target = timer_hw->timerawl + delay_us;
// Write the lower 32 bits of the target time to the alarm which
// will arm it
timer_hw->alarm[ALARM_NUM] = (uint32_t) target;
}
void setup() {
//Serial.begin(115200);
//while (!Serial);
delay(3000);
//Serial.printf("Timer lowlevel!\n");
pinMode( TP1, OUTPUT );
}
// Set alarm every 2 seconds
void loop() {
alarm_fired = false;
alarm_in_us(1000000 * 2);
// Wait for alarm to fire
while (!alarm_fired);
}
/// \end::alarm_standalone[]