cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L412 interrupt latency and jitter

b.a.
Associate III

I am running STM32L412 on 48MHz MSI, Timer 2 is configured in One-Pulse mode to output a 2ms Pulse. This is done by setting Timer 2 in PWM mode and with a prescaler of 47, ARR = 1999 and CCR = 1, Polarity=LOW

An interrupt is generated on update Event, i.e. when the pulse goes low.

In the ISR I set a GPIO to high, then clear the interrupt flag

void TIM2_IRQHandler(void)
{
	if(LL_TIM_IsActiveFlag_UPDATE(TIM2))
	{
		LL_GPIO_SetOutputPin(BTS_5V_GPIO_Port, BTS_5V_Pin);
		LL_TIM_ClearFlag_UPDATE(TIM2);
	}
}

Configuration is done with CubeIDE

Now, when observing the signals on a scope, I see that there is very unsteady interrupt latency that jitters from fairly good (750ns) to quite poor(2,6µs) and all this in quite neat 166ns intervals

0693W000005AHCxQAO.png 

I am quite new to the STM32, but this very coars jitter seem just not right to me and the total timespan would be something like over 100 clock cycles.

I have the feeling that I am missing something out on the configuration...

Up to now there is no application code running, just timers and some interrupts

Any help welcome to solve this mistery...

Cheers,

Bob

9 REPLIES 9
b.a.
Associate III

Oh my...

just realized I was still in debug mode !

silly me :\

The latency is much less, but still quite a lot of jitter:

(I left the scope on the same settings to better illustrate change)

0693W000005AHH4QAO.pngeach jitter intervall is around 62ns (which would mean about 3 clock cycles on 48MHz?)

Any idea what can cause this very irregular interrupt latency?

Other interrupt of same or higher priority (maybe systick if you use CubeMX autovomited code), combined with jumpcache and bus synchronization artefacts?

In 32-bitters, interrupts are a poor way of realtime control, use hardware whenever possible.

JW

b.a.
Associate III

no systick, as far as I know, when using LL-libraries.

apart from system interrupts, there is only on other interrupt. Used to reset the pin that is set within TIM2_ISR when a falling edge occurs on PA5 (same pin that triggers TIM2)

0693W000005AIVgQAO.pngBUT, when I set this interrupt to priority 1 it is never executed!

I fear, I still don't fully understand interrupt handling :(

any good uploading the project?

> when I set this interrupt to priority 1 it is never executed!

This definitively is not normal. Maybe the other interrupt fails to clear its interrupt source (TIM interrupt? TIMx->SR) so loops infinitely.

Toggle a (different) pin in each ISR upon entry and exit and observe.

JW

b.a.
Associate III

I am no "migrating" everything to "handmade" code, still looking at what the Cube-Thingy does and learning...

Too many strange things happened that I had to investigate, what held me away from doing what I want to do...

I now have a very stable interrupt latency of around 300ns (@80MHz PLLCLK) !

I really am convinced that this is the way to go.

I still use the LowLevel-lybraries and one issue is that when I disable peripherls in Cube, the correpsonding drivers, i.e. the .c and .h files are no longer included. small but annoying problem

> I now have a very stable interrupt latency of around 300ns (@80MHz PLLCLK) !

So, what did you do to achieve this?

JW

RAM based vectors and code might have a much more predictable execution profile.

Would also want to look at the code actually generated, and clear the interrupt flag earlier.​

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

As I wrote, I did all the configuration "by hand". Basically looking what registers were set to which value and then initialized the peripheral by using either direct register access or by using the LL-driver functions (that basically do the same thing but are a tad more readable)

example for Timer15

//*-- Timer 15  -----------------------------------------
//	enable this on in last place, since it is the one that generates the simulated trigger
	LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM15);
	TIM15->CR1	 |= TIM_CR1_ARPE;	//	enable auto-reload buffer
	TIM15->CCMR1 |= TIM_CCMR1_OC2PE;	//	enable preload register (buffered)
	TIM15->CCMR1 |= TIM_CCMR1_OC2M_1;	//	set PWM mode 1 (togehter with next instruction)
	TIM15->CCMR1 |= TIM_CCMR1_OC2M_2;	//	set PWM mode 1 (togehter with previous instruction)
	TIM15->CCER  |= TIM_CCER_CC2E;	//	output of channel 2 is enabled
	TIM15->BDTR  |= TIM_BDTR_MOE;	//	enable outputs for this Timer
	TIM15->CNT    = 0;	//	Reset Count register to 0
	TIM15->PSC    = 79;	//	set prescaler in order to get 1µs clocks
	TIM15->ARR		= 59999;	//	we want a period of 60ms
	TIM15->CCR2	 	= 14999;	//	and a pulse of 25ms
	TIM15->EGR	 |= TIM_EGR_UG;	//	generate update event to initialize all preloaded registers
	while((!LL_TIM_IsActiveFlag_UPDATE(TIM15))){};	//	wait fpr interrupt to have occured
	LL_TIM_ClearFlag_UPDATE(TIM15);	//	clear flag
	TIM15->CR1   |= TIM_CR1_CEN;	//	enable this Timer
//--------------------------------------------------*/

I am now on a point where there is only the SysClock initialization is left from Cube

The problem with LL is that it requires learning both - LL and register names, and translating back and forth. Also it often forces to do a read-modify-write for separate bits of register, where a single write could be done. And it is not a driver or library anyway, as it doesn't provide an abstraction level. HAL/CubeMX code is such a broken bloatware, that it is completely useless. The best is to develop your own sane libraries using register level code.

Clock initialization is rather simple and takes some 10-30 lines of proper register level code - the same amount of lines that an initialization of the HAL structures takes. With a difference that it doesn't generate kilobytes of flawed bloatware...