cancel
Showing results for 
Search instead for 
Did you mean: 

LPUART interrupt occur not immediately cause overrun with FreeRTOS tickless.

kein
Associate III

Hello everyone,

I'm using Board NUCLEO-U031R8 for testing now, and I started with cubeMX and initial component below:

  • System Clock configure to HSI (16MHz)
  • FreeRTOS with USE_TICKLESS_IDLE as 1 (built in functionality enabled), and auto generated two function PreSleepProcessing() / PostSleepProcessing().
  • LPTIM1 with 32kHz LSI and clock presclaer set to Div32, every timer count alomost 1ms, using for replace the sysTick while tickless idle.
  • LPUART1 with 16MHz HSI1, interrupt RXNE/WKUP/ERROR always enabled after system initial done.

The task is startup and print information and delay 1 second, then entering an endless loop that includes a 1-second running delay and a 4-second sleep delay, and everything works fine in 1 second running delay.

But when sending bytes in sleep, it would occur overrun error when received length > 2, so i toggle an output while interrupt occur. and i saw an weird interrupt while receiving bytes, the record here.

kein_0-1727778016161.png

To I modify tickless idle part, I disable sysTick and start LPTIM then go stop1 mode in PreSleepProcessing(), and  restore everything and call vTaskStepTick() in the end of PostSleepProcessing(), and commit out the vTaskStepTick() in vPortSuppressTicksAndSleep() of port.c

 

Task function:

void StartDefaultTask(void *argument) {
  // initial GPIO
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
  LL_GPIO_SetPinMode(LED4_GPIO, LED4_PIN, LL_GPIO_MODE_OUTPUT);     // LED4 = PA5 = D13
  LL_GPIO_SetPinMode(OUT1_GPIO, OUT1_PIN, LL_GPIO_MODE_OUTPUT);     // OUT1 = PA8 = D7
  LL_GPIO_SetPinMode(OUT2_GPIO, OUT2_PIN, LL_GPIO_MODE_OUTPUT);     // OUT2 = PB5 = D4
  LL_GPIO_SetPinOutputType(LED4_GPIO, LED4_PIN, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinOutputType(OUT1_GPIO, OUT1_PIN, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinOutputType(OUT2_GPIO, OUT2_PIN, LL_GPIO_OUTPUT_PUSHPULL);

  LPUART_SendStr("BOOT\n");
  LL_mDelay(1000);
  LPUART_SendStr("Start!\n");
  /* Infinite loop */
  for(;;)
  {
    LL_GPIO_SetOutputPin(LED4_GPIO, LED4_PIN);
    LL_mDelay(1000);    // delay with no sleep

    LPUART_SendStr("SLEEP\n");
    LL_GPIO_ResetOutputPin(LED4_GPIO, LED4_PIN);
    osDelay(pdMS_TO_TICKS(4000));    // delay to entry sleep (tickless idle)
    LPUART_SendStr("WAKE\n");

    // print out if any UART error.
    if (_ParErrCnt > 0)      { LPUART_SendStr(",ParityErr");  LPUART_SendNum(_ParErrCnt, 10);      _ParErrCnt = 0; }
    if (_FrameErrCnt > 0)    { LPUART_SendStr(",FrameErr");   LPUART_SendNum(_FrameErrCnt, 10);    _FrameErrCnt = 0; }
    if (_NoiseErrCnt > 0)    { LPUART_SendStr(",NoiseErr");   LPUART_SendNum(_NoiseErrCnt, 10);    _NoiseErrCnt = 0; }
    if (_OverRunErrCnt > 0)  { LPUART_SendStr(",OverRunErr"); LPUART_SendNum(_OverRunErrCnt, 10);  _OverRunErrCnt = 0; }
  }

Tickless functions:

void PreSleepProcessing(uint32_t ulExpectedIdleTime) {
  LL_SYSTICK_DisableIT();
  LPTIM_Start(ulExpectedIdleTime);
  timeBeforeSleep = 0;

  LL_LPUART_EnableInStopMode(LPUART1);                      // Set   LPUART1->CR1.UESM
  
  // ----------------------------- Sleep ------------------------------
  LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);                   // Set   PWR->CR1.LPMS
  LL_LPM_EnableDeepSleep();                                 // Set   SCB->SCR
  __WFI();
  LL_LPM_EnableSleep();                                     // Clear SCB->SCR
  LL_LPUART_DisableInStopMode(LPUART1);                     // Clear LPUART1->CR1.UESM
  // ------------------------------------------------------------------
  // Wake-up 
}

void PostSleepProcessing(uint32_t ulExpectedIdleTime) {
  sleepTime = LL_LPTIM_GetCounter(LPTIM1) - timeBeforeSleep;

  LPTIM_Stop();
  LL_SYSTICK_EnableIT();                                    // HAL_ResumeTick();

  // NOTE: call vTaskStepTick() here, commit out the vTaskStepTick() in function vPortSetupTimerInterrupt() which in file port.c
  vTaskStepTick( sleepTime );
}

LPUART IRQHandler:

void USART3_LPUART1_IRQHandler(void)
{
  LL_GPIO_TogglePin(OUT1_GPIO, OUT1_PIN);

  if (LL_LPUART_IsActiveFlag_WKUP(LPUART1) && LL_LPUART_IsEnabledIT_WKUP(LPUART1)) {
    LL_LPUART_ClearFlag_WKUP(LPUART1);
  }

  // Check UART error and clear
  uint32_t uart_isr = LPUART1->ISR;
  if (uart_isr & 0x0F) {
    if(uart_isr & LL_LPUART_ISR_PE) {
      LPUART1->ICR = LL_LPUART_ICR_PECF;
      _ParErrCnt++;
    }
    if(uart_isr & LL_LPUART_ISR_FE) {
      LL_LPUART_RequestRxDataFlush(LPUART1);
      LPUART1->ICR = LL_LPUART_ICR_FECF;
      _FrameErrCnt++;
    }
    if(uart_isr & LL_LPUART_ISR_NE) {
      LL_LPUART_RequestRxDataFlush(LPUART1);
      LPUART1->ICR = LL_LPUART_ICR_NCF;
      _NoiseErrCnt++;
    }
    if(uart_isr & LL_LPUART_ISR_ORE) {
      LPUART1->ICR = LL_LPUART_ICR_ORECF;
      _OverRunErrCnt++;
    }
  }

  if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
    uint8_t ch = LL_LPUART_ReceiveData8(LPUART1);
    rxBuf[rxIdx++] = ch;
    if (ch == '\n') rxFlag = rxIdx;
  }

  LL_GPIO_TogglePin(OUT1_GPIO, OUT1_PIN);
}

 

The test project is upload if anyone needed.

If there are any mistakes or oversights, please correct me.

Regards

Kein.

1 ACCEPTED SOLUTION

Accepted Solutions
kein
Associate III

Reply to myself with final solution:

using RTC to replace LPTIM because LPTIM take too long time 2~3ms to set CCR1 wait CMP1OK, in my case if other peripherals need interrupt like uart receive at same time would occur overrun and lost received data.

The RTC using 32k LSI as clock source, and configure to Free running Binary mode and start wakeup timer before MCU go into sleep, wakeup and get counter of RTC could know the real time in sleeping and call vTaskStepTick(), and make sure vTaskStepTick() need to commit out in port.c

hope this can help someone.

View solution in original post

4 REPLIES 4
kein
Associate III

i think i found the problem, the time to set CCR1 and wait CMP1OK of LPTIM is too long (2~3ms), it makes in the waiting time not trigger the interrupt until whole function finish, just like this:

kein_0-1728025341447.png

my next step is to found out if there is any peripherals which can replace LPTIM.

 

and here is the timer code

void LPTIM_Start(uint32_t timeout) {
  static bool initialed = false;
  if (initialed == false) {
    LL_LPTIM_EnableTimeout(LPTIM1);
    LL_LPTIM_Enable(LPTIM1);
    LL_LPTIM_ClearFlag_DIEROK(LPTIM1);
    LL_LPTIM_EnableIT_CC1(LPTIM1);
    initialed = true;
  }

  LL_LPTIM_ClearFlag_CMP1OK(LPTIM1);
  LL_LPTIM_OC_SetCompareCH1(LPTIM1, timeout - 1);

  LL_GPIO_SetOutputPin(OUT2_GPIO, OUT2_PIN);     // OUT2 pin using for debugging
  // NOTE: wait here until the APB bus write operation to the LPTIM1 CCR1 register has been successfully completed, it takes around 2~3ms and the interrupt would not triggered  immediatly.
  while (!LL_LPTIM_IsActiveFlag_CMP1OK(LPTIM1));
  LL_GPIO_ResetOutputPin(OUT2_GPIO, OUT2_PIN);

  LL_LPTIM_ResetCounter(LPTIM1);
  LL_LPTIM_StartCounter(LPTIM1, LL_LPTIM_OPERATING_MODE_ONESHOT);
}
void LPTIM_Stop(void) {
  // only clear the flag, let LPTIM keep enable.
  LL_LPTIM_ClearFlag_CC1(LPTIM1);
}

If there are any mistakes, please correct me, and welcom any suggtion and advice.

Sarra.S
ST Employee

Hello @kein

The delay you're experiencing when setting the CCR1 register and waiting for the CMP1OK flag is due to the synchronization between the APB clock and the LPTIM clock, especially if the LPTIM clock is much slower than the APB clock.

have you tried with a general purpose timer, as they have higher clock speeds therefore lower latency for setting compare values and generating interrupts

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hi Sarra:

Now I'm using RTC to repleace LPTIM, but there's a little problem, when i start wakeUp timer of RTC and reset RTC time to 00:00:00, it would occur HardFault_Handler() when i call LL_RTC_EnableWriteProtection().

And here is the RTC code which is modify from HAL library:

void RTC_StartWakeUpTimer(uint32_t wakeup_ms)
{
  wakeup_ms = (wakeup_ms * 2) -1;     // RTC using 32k LSI with DIV16, 0.5ms in every count
  uint32_t wakeUpAutoClr = 0;

  /* Check RTC WUTWF flag is reset only when wake up timer enabled*/
  if (READ_BIT(RTC_INSTANCE->CR, RTC_CR_WUTE) != 0U)
  {
    /* Wait till RTC WUTWF flag is reset and if Time out is reached exit */
    while (READ_BIT(RTC_INSTANCE->ICSR, RTC_ICSR_WUTWF) != 0U) {}
  }

  LL_RTC_WAKEUP_Disable(RTC_INSTANCE);           /* Disable the Wake-Up timer */
  LL_RTC_ClearFlag_WUT(RTC_INSTANCE);            /* Clear flag Wake-Up */

  /* Wait till RTC WUTWF flag is set and if Time out is reached exit */
  while (READ_BIT(RTC_INSTANCE->ICSR, RTC_ICSR_WUTWF) == 0U) {}

  /* Configure the Wakeup Timer counter and auto clear value */
  WRITE_REG(RTC_INSTANCE->WUTR, (uint32_t)(wakeup_ms | (wakeUpAutoClr << RTC_WUTR_WUTOCLR_Pos)));

  /* Configure the clock source */
  LL_RTC_WAKEUP_SetClock(RTC_INSTANCE, LL_RTC_WAKEUPCLOCK_DIV_16);

  /* RTC WakeUpTimer EXTI Configuration: Interrupt configuration */
  LL_EXTI_EnableIT_0_31(RTC_EXTI_LINE_WAKEUPTIMER_EVENT);

  /* Configure the Interrupt in the RTC_CR register and Enable the Wakeup Timer*/
  SET_BIT(RTC->CR, (RTC_CR_WUTIE | RTC_CR_WUTE));
}
void RTC_ResetTime(void)
{
  /* Disable the write protection for RTC registers */
  LL_RTC_DisableWriteProtection(RTC_INSTANCE);

  /* Set Initialization mode */
  RTC_EnterInitMode();

  /* Check Binary mode ((32-bit free-running counter) */
  if (READ_BIT(RTC_INSTANCE->ICSR, RTC_ICSR_BIN) != RTC_BINARY_ONLY)
  {
    /* Set the RTC_TR register */
    uint32_t RTC_TR_Reg = 0;          // AM,0hr,0min,0sec
    WRITE_REG(RTC->TR, RTC_TR_Reg);

    /* Clear the bits to be configured (Deprecated. Use HAL_RTC_DST_xxx functions instead) */
    CLEAR_BIT(RTC->CR, RTC_CR_BKP);
  }

  /* Exit Initialization mode */
  CLEAR_BIT(RTC_INSTANCE->ICSR, RTC_ICSR_INIT);

  /* If  CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */
  if (READ_BIT(RTC_INSTANCE->CR, RTC_CR_BYPSHAD) == 0U)
  {
    RTC_WaitForSynchro();
  }

  /* Enable the write protection for RTC registers */
  // TODO: it would entry HardFault_Handler()
  LL_RTC_EnableWriteProtection(RTC_INSTANCE);
}

 

 

BTW, Is there's any RTC example can using for calculating elapsed time and wakeup mcu itself?

kein
Associate III

Reply to myself with final solution:

using RTC to replace LPTIM because LPTIM take too long time 2~3ms to set CCR1 wait CMP1OK, in my case if other peripherals need interrupt like uart receive at same time would occur overrun and lost received data.

The RTC using 32k LSI as clock source, and configure to Free running Binary mode and start wakeup timer before MCU go into sleep, wakeup and get counter of RTC could know the real time in sleeping and call vTaskStepTick(), and make sure vTaskStepTick() need to commit out in port.c

hope this can help someone.