前两天在研究FreeRTOS的Tickless低功耗管理,使用的是CH582的板子,一直卡在portSUPPRESS_TICKS_AND_SLEEP()宏的实现上面。具体问题如下:
使用LowPower_Halt休眠没有问题,使用LowPower_Sleep休眠时,系统在切换任务时就直接卡B了。
非常郁闷,尝试了各种方法都不行,直到我看到这个:
也就是说CH58X系列的systick在休眠后是会复位配置和计数的,这不坑爹嘛!
废话不多少,上代码。以下是portSUPPRESS_TICKS_AND_SLEEP()宏的具体实现:
/* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */ void vApplicationSleep( uint32_t xExpectedIdleTime ) { #if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE) unsigned long ulLowPowerTimeBeforeSleep, ulLowPowerTimeAfterSleep, ulLowPowerTimeTotalSleep, irq_status; eSleepModeStatus eSleepStatus; uint32_t time_sleep, time_curr, time; /* Stop the timer that is generating the tick interrupt. */ PFIC_DisableIRQ(RTC_IRQn); PFIC_DisableIRQ(SysTick_IRQn); SYS_DisableAllIrq(&irq_status); ulLowPowerTimeBeforeSleep = RTC_GetCycle32k(); time = xExpectedIdleTime * 32.768 + ulLowPowerTimeBeforeSleep; if (time >= RTC_TIMER_MAX_VALUE) { time -= RTC_TIMER_MAX_VALUE; } if (time < WAKE_UP_RTC_MAX_TIME + 32) { PFIC_EnableIRQ(SysTick_IRQn); SYS_RecoverIrq(irq_status); return; } time -= (WAKE_UP_RTC_MAX_TIME + 32); time_curr = RTC_GetCycle32k(); // 检测睡眠时间 if (time < time_curr) { time_sleep = time + (RTC_TIMER_MAX_VALUE - time_curr); } else { time_sleep = time - time_curr; } if (time_sleep > (RTC_TIMER_MAX_VALUE - TMOS_TIME_VALID)) { PFIC_EnableIRQ(SysTick_IRQn); SYS_RecoverIrq(irq_status); return; } /* Ensure it is still ok to enter the sleep mode. */ eSleepStatus = eTaskConfirmSleepModeStatus(); if( eSleepStatus == eAbortSleep ) { PFIC_EnableIRQ(SysTick_IRQn); SYS_RecoverIrq(irq_status); } else { if( eSleepStatus == eNoTasksWaitingTimeout ) { LowPower_Shutdown(0); } else { RTC_SetTignTime(time); //PFIC_EnableIRQ(RTC_IRQn); #if(DEBUG == Debug_UART1) // 使用其他串口输出打印信息需要修改这行代码 while((R8_UART1_LSR & RB_LSR_TX_ALL_EMP) == 0) { __nop(); } #endif // LOW POWER-sleep模式 if(!(R8_RTC_FLAG_CTRL & RB_RTC_TRIG_FLAG)) { LowPower_Sleep_Event(RB_PWR_RAM2K | RB_PWR_RAM30K | RB_PWR_XROM | RB_PWR_EXTEND | RB_PWR_CORE); if(R8_RTC_FLAG_CTRL & RB_RTC_TRIG_FLAG) { // 注意如果使用了RTC以外的唤醒方式,需要注意此时32M晶振未稳定 R8_RTC_FLAG_CTRL = RB_RTC_TRIG_CLR; time += WAKE_UP_RTC_MAX_TIME; if(time > 0xA8C00000) { time -= 0xA8C00000; } RTC_SetTignTime(time); LowPower_Idle_Event(); R8_RTC_FLAG_CTRL = RB_RTC_TRIG_CLR; } HSECFG_Current(HSE_RCur_100); // 降为额定电流(低功耗函数中提升了HSE偏置电流) vPortSetupTimerInterrupt(); // 恢复SysTick配置 } ulLowPowerTimeAfterSleep = RTC_GetCycle32k(); if (ulLowPowerTimeAfterSleep < ulLowPowerTimeBeforeSleep) { ulLowPowerTimeTotalSleep = ulLowPowerTimeAfterSleep + (RTC_TIMER_MAX_VALUE - ulLowPowerTimeBeforeSleep); } else { ulLowPowerTimeTotalSleep = ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep; } ulLowPowerTimeTotalSleep = ulLowPowerTimeTotalSleep * 1000 / 32768; vTaskStepTick( ulLowPowerTimeTotalSleep ); //PRINT("ulLowPowerTimeTotalSleep = %d xExpectedIdleTime=%d\n", ulLowPowerTimeTotalSleep, xExpectedIdleTime); //PRINT("xTickCount = %d\n", xTaskGetTickCount()); } SYS_RecoverIrq(irq_status); PFIC_EnableIRQ(SysTick_IRQn); } #endif }
重点是休眠唤醒后,需要调用vPortSetupTimerInterrupt()恢复SysTick配置。
热门产品 :
CH641: PD及无线充电专用MCU