cancel
Showing results for 
Search instead for 
Did you mean: 

LPTIM compare interruption sometimes is triggered when it should not

JNava.1
Associate II

Hi. I am working in a project which involves the use of an STM32L443 microcontroller. I am using the IAR for ARM v 9.30.1 and the CMSIS and ST drivers package (STM32CubeL4 Firmware Package V1.17.1). I am using LL drivers (HAL is not suitable for our needs).

I have configured several timers to interrupt when the counter reaches the compare registers (basically, to have different timeouts to do different tasks). The problem comes when I use a LPTIMx timer. It works as expected, triggering an interruption when the counter register value matches the compare register value programmed. But sometimes, it triggers an interrupt in moments in which the counter register are not close to the compare value.

In order to simplify, I create a simple project to show the problem clearer. It consists in an infinite loop which just have two timers (TIM1 and LPTIM1) running in sleep mode with compare interruptions active. The idea is to initialize the LPTIM1 compare register CMP to make LPTIM1 to interrupt 400ms after that initialization. Immediately after, I initialize the compare register CCR1 in order to make TIM1 to interrupt 350ms after that initialization. The correct behavior should be that TIM1 will interrupt 350ms after those initializations, waking up the microcontroller from sleep mode, toogle the PIN PA7, initialize the timers counters and go to sleep again. So, LPTIM1 should never interrupt, because TIM1 should always "win". The signal in the oscilloscope should be a square signal 350ms in high and 350ms in low. But, the reality is that the oscilloscope shows that square signal but with quick "toggles" in some places. These "quick toggles" are produced because in some occasions, LPTIM1 interrupts immediately the enable_irq() is called. I define two arrays to store the values of the registers inside the LPTIM1_IRQHandler routine, showing that LPTIM1 interrupt occurs having the counter register far from the compare register. I want to say that before enabling the interruptions and entering sleep mode, I clear the LPTIM1 CMP flag and the NVIC pending flag too in order to avoid any pending interruption to occur.

The code of my main function is the following:

int main( void )
{
	/* Initialize microcontroller peripherals */
	InitPWR();
	InitClocks();
	InitTIM1( TIM1_FREQ );
	InitLPTIM1();
	InitPorts();
	__disable_irq();
 
	for ( ;; ) {
		//Initialize LPTIM Compare register 400ms ahead from the current value of the counter
		LL_LPTIM_ClearFlag_CMPOK(LPTIM1);
		LL_LPTIM_SetCompare(LPTIM1, static_cast<uint16_t>(LL_LPTIM_GetCounter( LPTIM1 ) + MS_TO_TICKS_LPTIM1( 400 ) + 1));
		while ( !LL_LPTIM_IsActiveFlag_CMPOK(LPTIM1) ) {
		}
		LL_LPTIM_ClearFLAG_CMPM(LPTIM1);
		NVIC_ClearPendingIRQ( LPTIM1_IRQn );
 
		//Initialize TIM1 Compare channel 1 register 350ms ahead from the current value of the counter
		LL_TIM_OC_SetCompareCH1(TIM1, static_cast<uint16_t>(LL_TIM_GetCounter( TIM1 ) + MS_TO_TICKS_TIM1( 350 ) + 1));
		LL_TIM_ClearFlag_CC1(TIM1);
		NVIC_ClearPendingIRQ( TIM1_CC_IRQn );
 
		//Enable TIM1 Compare channel 1 interrupt, LPTIM1 Compare interrupt and their corresponding IRQs
		LL_TIM_EnableIT_CC1(TIM1);
		NVIC_EnableIRQ( TIM1_CC_IRQn );
		LL_LPTIM_EnableIT_CMPM( LPTIM1 );
		NVIC_EnableIRQ( LPTIM1_IRQn );
 
		/* Enter in sleep mode */
		LL_LPM_EnableSleep();
		LL_LPM_EnableSleepOnExit();
		__enable_irq();
		__WFI();
 
		//Disable TIM1 Compare channel 1 interrupt, LPTIM1 Compare interrupt and their corresponding IRQs
		LL_TIM_DisableIT_CC1(TIM1);
		NVIC_DisableIRQ( TIM1_CC_IRQn );
		LL_LPTIM_DisableIT_CMPM( LPTIM1 );
		NVIC_DisableIRQ( LPTIM1_IRQn );
 
		LL_GPIO_TogglePin( GPIOA, LL_GPIO_PIN_7 );				/* Trace to see timings in the oscilloscope */
	}
}

 The oscilloscope capture is the following:

0693W00000aHXNLQA4.png 

I will also attach the project, if anyone want to see the init routines and other stuff.

Thanks in advance for your comments to help me understand if there is something that I am doing wrong or to see if it is a non declared errata of these microcontrollers.

1 ACCEPTED SOLUTION

Accepted Solutions
JNava.1
Associate II

After doing some tests I realize the problem is because the CMPM (compare match flag) doesn't work as is defined in the reference manual. In the manual, we can read the following:

"The CMPM bit is set by hardware to inform application that LPTIM_CNT register value reached the

LPTIM_CMP register’s value". It looks like the flag and the corresponding interrupt is only triggered when CNT = CMP as happens with other timers. But for LPTIM, the reality is that the flag and their interrupt is triggered when CNT >= CMP. Then, if you use the timer as a free running timer, with ARR = 0xFFFF, and perform a timeout adding the time you want to the current value of the CNT and writing the result in CMP, when CMP is lower than CNT, the flag will rise. For example, if you have CNT = 0xFFF0 and you set a timeout of 0x20, then you have to write CMP = 0x0010. That immediately will trigger the flag and the interrupt.

So, the LPTIM timers could not be used as free running timers. If you want to set a timeout, you have to set the TIMOUT bit in the Control register and always restart the timer before starting the timeout.

For more explanation of that behavior and other problems of LPTIM timers, see the following post:

https://community.st.com/s/question/0D53W000004J0srSAC/stm32l0-lptim-generating-random-compare-match-interrupt-when-changing-compare-match-value

View solution in original post

3 REPLIES 3
JNava.1
Associate II

After doing some tests I realize the problem is because the CMPM (compare match flag) doesn't work as is defined in the reference manual. In the manual, we can read the following:

"The CMPM bit is set by hardware to inform application that LPTIM_CNT register value reached the

LPTIM_CMP register’s value". It looks like the flag and the corresponding interrupt is only triggered when CNT = CMP as happens with other timers. But for LPTIM, the reality is that the flag and their interrupt is triggered when CNT >= CMP. Then, if you use the timer as a free running timer, with ARR = 0xFFFF, and perform a timeout adding the time you want to the current value of the CNT and writing the result in CMP, when CMP is lower than CNT, the flag will rise. For example, if you have CNT = 0xFFF0 and you set a timeout of 0x20, then you have to write CMP = 0x0010. That immediately will trigger the flag and the interrupt.

So, the LPTIM timers could not be used as free running timers. If you want to set a timeout, you have to set the TIMOUT bit in the Control register and always restart the timer before starting the timeout.

For more explanation of that behavior and other problems of LPTIM timers, see the following post:

https://community.st.com/s/question/0D53W000004J0srSAC/stm32l0-lptim-generating-random-compare-match-interrupt-when-changing-compare-match-value

ST removed the post link..... =>Access Denied

Hello,

I do not understand, I am using a STM32L431VCT6 with STM32CubeIDE and I have not this behaviour: There is no interruption when CNT > CMP, only when CNT == CMP.

Here is the initialisation code:

    uint32_t timeout;
    
    LL_LPTIM_ClearFLAG_CMPM(LPTIM1);
    LL_LPTIM_ClearFLAG_ARRM(LPTIM1);

    /* Enable interrupt */
    LL_LPTIM_EnableIT_CMPM(LPTIM1);
    LL_LPTIM_EnableIT_ARRM(LPTIM1);

    /* Enable the LPTIM1 counter */
    LL_LPTIM_Enable(LPTIM1);

    /* Set the Autoreload value */
    LL_LPTIM_ClearFlag_ARROK(LPTIM1);
    LL_LPTIM_SetAutoReload(LPTIM1, TIMER_MAXVAL_2SEC);
    timeout = 0x1000;
    while ((!LL_LPTIM_IsActiveFlag_ARROK(LPTIM1)) && (--timeout));
    if (0 == timeout)
        DbgUart_SendString("LPTIM1 err1\r\n");

    /* Set the compare value */
    LL_LPTIM_ClearFlag_CMPOK(LPTIM1);
    LL_LPTIM_SetCompare(LPTIM1, TIMER_PERIODICAL_FREQUENCY);
    timeout = 0x1000;
    while ((!LL_LPTIM_IsActiveFlag_ARROK(LPTIM1)) && (--timeout));
    if (0 == timeout)
        DbgUart_SendString("LPTIM1 err2\r\n");
    
    /* Start the LPTIM counter in continuous mode */
    LL_LPTIM_StartCounter(LPTIM1, LL_LPTIM_OPERATING_MODE_CONTINUOUS);

With autoreload value set to maximum value 0xFFFF (2sec) and periodical value set to 7935 (around 240ms with 32768 clk)

Here is the interrupt code:

    uint32_t val, val1;
    u_int memo0 = 0, memo1 = 0;
    uint32_t timeout;

    if(LL_LPTIM_IsActiveFlag_CMPM(LPTIM1) == 1)
    {
        TESTE3_TOGGLE;
        LL_LPTIM_ClearFLAG_CMPM(LPTIM1);        /* CClear the compare match flag (CMPMCF) */
        
        val1 = 0x0000FFFF & read_LPTIM1();
        val = 0x0000FFFF & (val1 + TIMER_PERIODICAL_FREQUENCY);
        if (TIMER_MAXVAL_2SEC == val)   // This ARR[15:0]: Auto reload value must be strictly greater than the CMP[15:0] value.
        {
            val--;
            memo0 = 1;
        }
        if (val1 > val)
        {
            TESTE4_ON;           
            memo1 = 1;
        }
        LL_LPTIM_ClearFlag_CMPOK(LPTIM1);
        LL_LPTIM_SetCompare(LPTIM1, val);   // new CMP value
        timeout = 0x1000;
        while ((!LL_LPTIM_IsActiveFlag_CMPOK(LPTIM1)) && (--timeout));
        if (0 == timeout)
            DbgUart_SendString("LPTIM1 IT err\r\n");
        //PeriodEvents_Handler();
        DbgUart_SendString("cnt ");
        DbgUart_send_udec32(val1);
        DbgUart_SendString(", val ");
        DbgUart_send_udec32(val);
        if (memo0)
            DbgUart_SendString(", memo0");
        if (memo1)
            DbgUart_SendString(", memo1");
        DbgUart_SendString("\r\n");
    }

    if(LL_LPTIM_IsActiveFlag_ARRM(LPTIM1) == 1)
    {
        TESTE2_TOGGLE;
        LL_LPTIM_ClearFLAG_ARRM(LPTIM1);        /* CClear the autoreload match flag (ARRMCF)*/
    }
    TESTE4_OFF;

We can see on this log that the CNT value is above the CMP value:

cnt 58880, val 1279, memo1        => next is above the CNT overflow so CMP is less that actual CNT

But there is no interrupt before the equal condition.....

image.png

Do you have any idea?

Best regards

Mich.