CH582 CH583 FreeRTOS之Tickless低功耗管理实现

    前两天在研究FreeRTOS的Tickless低功耗管理,使用的是CH582的板子,一直卡在portSUPPRESS_TICKS_AND_SLEEP()宏的实现上面。具体问题如下:

    使用LowPower_Halt休眠没有问题,使用LowPower_Sleep休眠时,系统在切换任务时就直接卡B了。

    非常郁闷,尝试了各种方法都不行,直到我看到这个:

    1667100237641.jpg

    也就是说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配置。

1


参考最新的代码,把SysTick计数值也更新了,同时修复了休眠时间太短的时候异常的问题:

/* Define the function that is called by portSUPPRESS_TICKS_AND_SLEEP(). */
__HIGH_CODE
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;
    uint64_t sysTick_cnt = 0;
    /* Stop the timer that is generating the tick interrupt. */
    PFIC_DisableIRQ(RTC_IRQn);
    SYS_DisableAllIrq(&irq_status);
    sysTick_cnt = SysTick->CNT;
    ulLowPowerTimeBeforeSleep = RTC_GetCycle32k();
    time = xExpectedIdleTime * (32768.0 / configTICK_RATE_HZ) + ulLowPowerTimeBeforeSleep;
    if (time >= RTC_TIMER_MAX_VALUE) {
        time -= RTC_TIMER_MAX_VALUE;
    }
    if (time < WAKE_UP_RTC_MAX_TIME + 1) {
        SYS_RecoverIrq(irq_status);
        return;
    }
    time -= (WAKE_UP_RTC_MAX_TIME + 1);
    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)) || (time_sleep < SLEEP_RTC_MIN_TIME)) {
        SYS_RecoverIrq(irq_status);
        return;
    }
    /* Ensure it is still ok to enter the sleep mode. */
    eSleepStatus = eTaskConfirmSleepModeStatus();
    if( eSleepStatus == eAbortSleep ) {
        SYS_RecoverIrq(irq_status);
    }
    else {
        if( eSleepStatus == eNoTasksWaitingTimeout ) {
            LowPower_Shutdown(0);
        }
        else {
            R8_RTC_FLAG_CTRL = RB_RTC_TRIG_CLR;
            RTC_SetTignTime(time);
#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_EXTEND);
                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偏置电流)
                SysTick->CNT = sysTick_cnt;   // 恢复SysTick计数
                // 恢复SysTick配置
                PFIC_SetPriority(SWI_IRQn, 0xf0);
                PFIC_SetPriority(SysTick_IRQn, 0xf0);
                SysTick->CMP = (configCPU_CLOCK_HZ / configTICK_RATE_HZ) - 1; /* set reload register */
                SysTick->CTLR = SysTick_CTLR_INIT |
                                SysTick_CTLR_STRE |
                                SysTick_CTLR_STCLK |
                                SysTick_CTLR_STIE |
                                SysTick_CTLR_STE;
            }
            else {
                R8_RTC_FLAG_CTRL = RB_RTC_TRIG_CLR;
            }
            ulLowPowerTimeAfterSleep = RTC_GetCycle32k();
            if (ulLowPowerTimeAfterSleep < ulLowPowerTimeBeforeSleep) {
                ulLowPowerTimeTotalSleep = ulLowPowerTimeAfterSleep + (RTC_TIMER_MAX_VALUE - ulLowPowerTimeBeforeSleep);
            }
            else {
                ulLowPowerTimeTotalSleep = ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep;
            }
            ulLowPowerTimeTotalSleep = ulLowPowerTimeTotalSleep * (configTICK_RATE_HZ / 32768.0);
            vTaskStepTick( ulLowPowerTimeTotalSleep );
        }
        SYS_RecoverIrq(irq_status);
    }
#endif
}



只有登录才能回复,可以选择微信账号登录