keil/include/drivers/mk_power.c
@@ -77,19 +77,26 @@
#include "mk_i2c.h"
#endif
#ifndef LOW_POWER_CLOCK_PPM
#define LOW_POWER_CLOCK_PPM (50)
#endif
#ifndef SYS_CLK_SOURCE
#define SYS_CLK_SOURCE CLOCK_62P4M_XTAL38P4M_TO_SYS_CLK
#endif
// Including 38.4MHz clock PLL ready time and application recovery time, unit: us
#define RECOVERY_TIME_FROM_LOW_POWER (1000)
/** XTAL32K clock source ppm */
#ifndef LOW_POWER_CLOCK_PPM
#define LOW_POWER_CLOCK_PPM (100)
#endif
// Considering 32KHz clock ppm and sleep time, x = sleep time (us), the result should < PHY_SLEEP_TIME_US_MIN
#define WAKEUP_IN_ADVANCE_TIME(x) (RECOVERY_TIME_FROM_LOW_POWER + (x)*LOW_POWER_CLOCK_PPM / 1000000)
/** RCO32K clock source ppm */
#ifndef LOW_POWER_CLOCK_PPM_RCO
#define LOW_POWER_CLOCK_PPM_RCO (1000)
#endif
// Including 38.4MHz clock PLL ready time and application recovery time, unit: 32768Hz tick. 32tick ~= 1ms
#define RECOVERY_TIME_FROM_LOW_POWER (32)
// Considering 32KHz clock ppm and power down time, x = low power ticks to power down ( unit: 32768 tick)
#define WAKEUP_IN_ADVANCE_TIME(x) \
    (x - (RECOVERY_TIME_FROM_LOW_POWER + (x) * ((SYSCON->SYS_CMU & SYSCON_SYS_CMU_32K_CLK_SEL_MSK) ? LOW_POWER_CLOCK_PPM : LOW_POWER_CLOCK_PPM_RCO) / 1000000))
#ifdef UWB_EN
extern uint32_t slp_cnt;
@@ -130,8 +137,8 @@
    }
    else
    {
        // enable PMU, disable Xtal32k
        SYSCON->PMU_CTRL0 |= (1U << 31) | (1 << 7);
        // enable PMU
        SYSCON->PMU_CTRL0 |= (1U << 31);
    }
    // DG REF from LP_BG
@@ -262,12 +269,11 @@
    NVIC_ClearPendingIRQ(WAKEUP_IRQn);
    NVIC_EnableIRQ(WAKEUP_IRQn);
    // flash power down  - by flash_close()
    flash_power_down(FLASH_ID0);
    uint8_t val = REG_READ_BYTE(EFUSE_SHADOW_BASE + 0x67);
    if ((val & 0x80) == 0)
    {
        // flash power down  - by flash_close()
        flash_power_down(FLASH_ID0);
        // switch to external flash io
        REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val | 0x80);
    }
@@ -287,6 +293,7 @@
    SYSCON->IO_SLP_PUP[0] = SYSCON->IO_PUP[0];
    SYSCON->IO_SLP_PUP[1] = SYSCON->IO_PUP[1];
    SYSCON->IO_SLP_PUP[2] = SYSCON->IO_PUP[2];
    // SYSCON->IO_SLP_AEN |= (1U << 31);
    // reduce DG current 60%
    SYSCON->CAP_DIV_CFG = 0x0;
@@ -304,26 +311,27 @@
    // Clock on - CALIB
    SYSCON->SYS_CMU |= (1U << CLOCK_CALIB);
    SYSCON->CAP_DIV_CFG = 0x7;
    if ((val & 0x80) == 0)
    {
        // switch to internal flash io
        REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val & 0x7f);
        // flash power up
        flash_power_up(FLASH_ID0);
    }
    SYSCON->CAP_DIV_CFG = 0x7;
    // flash power up
    flash_power_up(FLASH_ID0);
#ifdef XIP_EN
    flash_open_for_xip(FLASH_ID0);
#endif
    NVIC_DisableIRQ(WAKEUP_IRQn);
    NVIC_ClearPendingIRQ(WAKEUP_IRQn);
    // Restore IO
    GPIO->DATAOUT = SYSCON->IO_SLP_OUT;
    GPIO->OUTENSET = SYSCON->IO_SLP_OE;
    // SYSCON->IO_SLP_AEN &= ~(1U << 31);
#ifdef SWD_WAKEUP_EN
    // power_swd_restore();
@@ -402,7 +410,7 @@
        SYSCON->PMU_CTRL1 &= ~((1U << 7) | (1U << 5));
    }
    // switch system clock from XTAL to RO
    // switch system clock from XTAL_38M4 to RO_48M
    clock_attach(CLOCK_48M_RO_TO_SYS_CLK);
    // for XIP
@@ -422,7 +430,7 @@
    // REFPLL - ON
    SYSCON->PMU_CTRL0 &= ~(1U << 10);
    // switch system clock from RO to XTAL
    // switch system clock from RO_48M to XTAL_38M4
    clock_attach(SYS_CLK_SOURCE);
#ifdef UWB_EN
@@ -594,61 +602,68 @@
}
#ifdef UWB_EN
/*
 *  nextExpiration can also be zero for an active timer, as said in WsfTimerNextExpiration's brief.
 *  But we are calling this WsfTimerNextExpiration with
 *    1. disabling global interrupt at first, (at the beginning of power_manage())
 *    2. then checking WSF_TIMER_EVENT is not set (POWER_UNIT_OS is cleared for all power modes).
 *  So here nextExpiration can not be zero for an active wsf timer.
 */
static uint32_t os_timer_tick_left(void)
{
    uint32_t left = UINT32_MAX;
#ifdef WSF_EN
    uint32_t nextExpiration = WsfTimerNextExpiration();
    if (nextExpiration != UINT32_MAX)
    {
        uint32_t elapsed_ticks = sys_tick_elapse_ticks();
        if (nextExpiration > elapsed_ticks)
            left = nextExpiration - elapsed_ticks;
        else
            left = 0;
    }
#endif
    return left;
}
#ifdef WSF_EN
static wsfTimer_t phy_pd_ctrl_timer = {
    .msg =
        {
            .event = PHY_PD_TIMER_EVENT,
        },
};
#endif
static void power_check_uwb_power_mode(void)
{
    enum POWER_MODE_T pm;
    // check os timer
    uint32_t os_time_ms = PHY_SLEEP_TIME_MS_MAX;
    uint32_t phy_time_us = PHY_SLEEP_TIME_MS_MAX * 1000;
    enum POWER_MODE_T pm = POWER_MODE_SLEEP;
#ifdef WSF_EN
    if (wsfOsReadyToSleep())
    // power_check_if_power_mode() may request active or sleep mode, then abort here
    if ((false == mac_is_busy()) && (power_env.power_request[POWER_MODE_SLEEP] == 0))
    {
        pm = WsfTimerSleepCheck(&os_time_ms);
        if (pm)
        uint32_t pd_tick = phy_timer_lp_tick_left();
        if (pd_tick != UINT32_MAX)
        {
#endif
            uint8_t busy = mac_is_busy();
            if (busy)
            {
                pm = POWER_MODE_SLEEP;
            }
            else
            {
                if (phy_timer_is_programmed())
                {
                    // check phy timer
                    uint32_t phy_time_tick = phy_timer_count_left();
                    phy_time_us = PHY_TIMER_COUNT_TO_US(phy_time_tick);
                }
            pd_tick += sys_tick_elapse_ticks();
            WsfTimerStartTick(&phy_pd_ctrl_timer, pd_tick, WSF_TIMER_ONE_SHOT);
        }
                // sleep time
                uint32_t sleep_time_us = MIN((uint32_t)(os_time_ms * 1000), (uint32_t)phy_time_us);
                if (sleep_time_us > PHY_SLEEP_TIME_US_MIN)
                {
                    pm = POWER_MODE_POWER_DOWN;
                    // make sure wakeup in advance, need to correct by 32K ppm
                    sleep_time_us -= WAKEUP_IN_ADVANCE_TIME(sleep_time_us);
                    uint32_t sleep_time_tick = __US_TO_32K_CNT(sleep_time_us);
                    sleep_timer_start(sleep_time_tick);
                }
                else
                {
                    pm = POWER_MODE_SLEEP;
                }
            }
#ifdef WSF_EN
        pd_tick = os_timer_tick_left();
        if (pd_tick > POWER_DOWN_TIME_TICK_MIN)
        {
            pm = POWER_MODE_POWER_DOWN;
            // make sure wakeup in advance, need to correct by 32K ppm
            sleep_timer_start(WAKEUP_IN_ADVANCE_TIME(pd_tick));
        }
        else if (pd_tick == UINT32_MAX)
        {
            pm = POWER_MODE_DEEP_POWER_DOWN;
        }
    }
    else
    {
        pm = POWER_MODE_ACTIVE;
    }
#endif
    power_mode_request(POWER_UNIT_UWB, pm);
}
#endif
@@ -660,6 +675,8 @@
    // check GPIO wakeup source
    power_check_io_power_mode();
    // check interface wakeup source
    power_check_if_power_mode();
    if (power_env.power_request[POWER_MODE_ACTIVE])
    {
@@ -670,13 +687,7 @@
#ifdef UWB_EN
        power_check_uwb_power_mode();
#endif
        power_check_if_power_mode();
        if (power_env.power_request[POWER_MODE_ACTIVE])
        {
            // stay in active mode
        }
        else if (power_env.power_request[POWER_MODE_SLEEP])
        if (power_env.power_request[POWER_MODE_SLEEP])
        {
            // enter sleep mode
            power_enter_sleep_mode();
@@ -702,13 +713,13 @@
            power_enter_shelf_mode();
        }
        power_clear_if_power_mode();
#ifdef UWB_EN
        power_mode_clear(POWER_UNIT_UWB);
#endif
    }
    // uint32_t int_pending = REG_READ(0xE000E200);
    power_clear_if_power_mode();
    int_unlock(lock);
    if (wakeup_from_power_down)