2024-10-01 03:57 AM
Hello everyone,
I'm using Board NUCLEO-U031R8 for testing now, and I started with cubeMX and initial component below:
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.
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.
Solved! Go to Solution.
2024-10-28 06:35 PM
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.
2024-10-04 12:06 AM
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:
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.
2024-10-07 02:34 AM
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.
2024-10-09 02:57 AM
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?
2024-10-28 06:35 PM
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.