Interrupt latency, schedule a recurring task, repeat a task
- Microcontroller: STM32L051R8Tx (MSI used as system clock, set to 4.194 MHz)
- Board: custom design (no Nucleo etc.)
- The LPTIM1 is clocked by an external LSE (32,768 kHz, +- 10ppm).
- IDE: Keil uVision V5.36.0.0
- Compiler: ARM Compiler “Use default compiler Version 5”
I scheduled a recurring task with help of LPTIM1 and it’s IRQ. The timer is set to 500ms. The task processing time is around 130ms (measured with help of TIMER21). I observed a latency which is becoming visible over time. After 4080 interrupts (after 34:00 minutes, 4080/2/60) the timer is lagging ~9 seconds (in my application, a few hundred milliseconds wouldn’t be a problem at all). In case this time is getting “lost” evenly per interrupt it would mean >2ms per interrupt. It seems to be unrealistic latency, isn’t it?
Secondly, if I replace the task by a delay function of the same time duration (or simple comment the task out), there is no considerable latency at all. 34:00 minutes on the microcontroller equal to 34:00 minutes on my stop watch.
Why is the task affecting the accuracy? I think I am missing something. Every hint is welcome – many thanks in advance.
// LPTIM1_IRQHandler
void LPTIM1_IRQHandler(void)
{
// ES0251: Flags must not be cleared outside the interrupt subroutine.
if((LPTIM1->ISR) & 0x02) // CNT value reached LPTIM_ARR register’s value?
{
LPTIM1->ICR = LPTIM_ICR_ARRMCF; // Writing 1 to this bit clears the ARRM
// flag in the LPTIM_ISR register.
g_interrupt_flag = 1;
}
} // End: void LPTIM1_IRQHandler(void)
int main(void)
{
RCC->ICSCR |= 0xC000; // Update clock to 4.194 MHz
// 0xC000 = 110 00000 00000000
// Bits 15:13 MSIRANGE[2:0]: MSI clock rang
// 110: range 6 around 4.194 MHz
SystemCoreClockUpdate(); // Only to set globale variable SystemCoreClock?
__disable_irq(); // Global disable IRQs
RCC->IOPENR |= 0x0001; // Enable GPIOA clock, USER LED (PA5)
RCC->APB2ENR |= 0x0001; // Enable SYSCFG clock (for delay_ms())
GPIOA->MODER &= ~0x00000C00; // Clear pin mode, LED PIN 21 (PA5)
GPIOA->MODER |= 0x00000400; // Set pin to output mode, LED PIN 21 (PA5)
GPIOA->BSRR = 0x00200000; // Turn off LED, PIN 21 (PA5)
tick_count_init(TIMER21_PSC); // Needed only during debug (Dev. stage)
usart2_init(); // Needed only during debug (Dev. stage)
i2c2_init(); // Used for sensor measurement (task)
// LPTIM1 is used to schedule measurement
// One measurement every 500ms
low_power_timer_init();
NVIC_SetPriority(LPTIM1_IRQn, 1);
NVIC_EnableIRQ(LPTIM1_IRQn); // Enable LPTIM1 interrupt
__enable_irq(); // Global enable IRQs
while(1)
{
printf("please enter zero (0) \r\n");
scanf("%d", &g_user_input); // Wait for user input
switch(g_user_input)
{
case 0:
{
low_power_timer_disable(); // Stop (previous) measurement
low_power_timer_enable(); // Start a new measurement,
// interrupt every 500ms
printf("new measurement cmd received\r\n");
g_datapoint_count = 0; // Restart count of measurements
while(g_datapoint_count < 4080) // 4080/2/60 = 34:00 minutes
{
// It takes 143ms needed after user entered "zero" on
// keyboard to come here
if(g_interrupt_flag) // Occurs every 500ms
{
// tick_count_start(); // Timer to measure duration
g_interrupt_flag = 0;
g_datapoint_count += 1;
print_time_on_terminal(0);
GPIOA->ODR ^= 0x20; // blink LED
task(g_iterations); // takes ~129ms
// delay_ms(129); // Needed only during debug
// tick_count_stop(TIMER21_MULTIPLIER);
// //printf("counts: %i \r\n", g_counts);
// //printf("time_ms: %.0i ms\r\n", g_time_ms);
// if(g_time_ms > max_time)
// {
// max_time = g_time_ms;
// printf("max_time: %.0i ms \r\n", max_time);
// }
} // End: if(g_interrupt_flag)
} // End: switch (stage)
break;
} // End: case 0:
} // End: switch(g_user_input)
} // End: while(1)
} // End: main()
