Gorgon_Meducer 发表于 2012-9-13 17:14:30

[笔记]Freescal Cortex-M4 休眠模式备忘录

本帖最后由 Gorgon_Meducer 于 2012-12-10 20:25 编辑

版权图片,引用前请注明出处



/***************************************************************************
*   Copyright(C)2009-2012 by Gorgon Meducer<Embedded_zhuoran@hotmail.com> *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify*
*   it under the terms of the GNU Lesser General Public License as      *
*   published by the Free Software Foundation; either version 2 of the    *
*   License, or (at your option) any later version.                     *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of      *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the         *
*   GNU General Public License for more details.                        *
*                                                                         *
*   You should have received a copy of the GNU Lesser General Public      *
*   License along with this program; if not, write to the               *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA02111-1307, USA.             *
***************************************************************************/

#ifndef __DRIVER_COMMON_SLEEP_H__
#define __DRIVER_COMMON_SLEEP_H__

/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"

/*============================ MACROS ========================================*/
#ifndef SLEEP_REQ_SET
#warning No defined sleep request set: SLEEP_REQ_SET
#endif

#ifndef SLEEP_REQ_COUNT
#warning No defined sleep request number: SLEEP_REQ_COUNT
#define SLEEP_REQ_COUNT         (1u)
#endif

#if   SLEEP_REQ_COUNT <= 8
#   define SLEEP_REQ_WORD_COUNT   1
#elif   SLEEP_REQ_COUNT <= 16
#   define SLEEP_REQ_WORD_COUNT   2
#elif   SLEEP_REQ_COUNT <= 24
#   define SLEEP_REQ_WORD_COUNT   3
#elif   SLEEP_REQ_COUNT <= 32
#   define SLEEP_REQ_WORD_COUNT   4
#else
#error It dose not make any sense that there are more than 32 vote candidates!
#endif

/*============================ MACROFIED FUNCTIONS ===========================*/

//! brief add a new sleep request item to SLEEP_REQ_SET
#define ADD_SLEEP_REQ(__NAME)               uint32_t __NAME : 4;

//! brief macro used to request a sleep level
#define REQ_SLEEP(__AGENT, __LEVEL)   \
    do {\
      SAFE_ATOM_CODE (\
            g_tSleepRequests.tMembers.__AGENT = (__LEVEL) \
      ) \
    } while(false)

#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
//! \brief macro used to reques a sleep level without updating run mode
#define REQ_RUN_AS(__AGENT, __LEVEL)    \
    do {\
      SAFE_ATOM_CODE (\
            g_tSleepRequests.tMembers.__AGENT = (__LEVEL) \
      ) \
      k20_core_try_to_sleep();\
    } while(false)
#else
#define REQ_RUN_AS(__AGENT, __LEVEL)    REQ_SLEEP(__AGENT, __LEVEL)
#endif

/*============================ TYPES =========================================*/

//! \name core rum mode
//! @{
typedef enum {
    NORMAL_RUN_MODE               = 1,      //!< normal run mode: support full speed
    LOW_POWER_RUN_MODE            = 2         //!< low power run mode: support 4M max clock
}run_mode_t;
//! @}

//! \name sleep mode
//! @{
typedef enum {
    SLEEP_KEEP_CURRENT_RUN_MODE   = 0,      //!< keep current run mode   
    SLEEP_RUN                     = 1,      //!< normal run mode
    SLEEP_VLP_RUN                   = 2,      //!< very low power run mode
    SLEEP_NONE                      = 3,      //!< none sleep mode

    SLEEP_WAIT                      = 4,      //!< wait mode
    SLEEP_VLP_WAIT                  = 5,      //!< very low power wait mode
    SLEEP_STOP                      = 6,      //!< stop mode
    SLEEP_VLP_STOP                  = 7,      //!< very low power stop mode
#if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_LL_STOP                   = 8,      //!< low leakage stop mode
    SLEEP_DEEPEST_RECOVERABLE_SLEEP = 8,      //!< deepest recoverable sleep mode
#else
    SLEEP_DEEPEST_RECOVERABLE_SLEEP = 7,      
#endif
#if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_VLL_STOP_LV3            = 9,      //!< very low leakage stop mode 3
    SLEEP_VLL_STOP_LV2            = 10,      //!< very low leakage stop mode 2
    SLEEP_VLL_STOP_LV1            = 11,       //!< very low leakage stop mode 1
    SLEEP_VLL_STOP_LV0            = 12,       //!< very low leakage stop mode 0
    SLEEP_DEEPEST_SLEEP             = 12,
#elif CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_DEEPEST_SLEEP             = 8,
#else
    SLEEP_DEEPEST_SLEEP             = 7,
#endif
    SLEEP_DO_NOT_CARE               = 0x0F      //!< don't care about the sleep mode
}sleep_mode_t;
//! @}

/*============================ GLOBAL VARIABLES ==============================*/
//! \brief sleep request set
extern volatile union {
    uint32_t wRequests;    //!< sleep requests
#ifdef SLEEP_REQ_SET
    struct {
      SLEEP_REQ_SET
    }tMembers;
#endif
} g_tSleepRequests;

/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/

/*! \brief initialize sleep service
*! \param none
*! \return none
*/
extern void sleep_init(void);

/*! \brief enter specified sleep mode directly
*! \param tMode target sleep mode
*! \return access result
*/
extern vsf_err_t k20_core_sleep(sleep_mode_t tMode);
   
/*! \brief try to enter a voted sleep mode
*! \param none
*! \return access result
*/
extern vsf_err_t k20_core_try_to_sleep(void);


#endif
/* EOF */
sleep.c

/***************************************************************************
*   Copyright(C)2009-2012 by Gorgon Meducer<Embedded_zhuoran@hotmail.com> *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify*
*   it under the terms of the GNU Lesser General Public License as      *
*   published by the Free Software Foundation; either version 2 of the    *
*   License, or (at your option) any later version.                     *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of      *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the         *
*   GNU General Public License for more details.                        *
*                                                                         *
*   You should have received a copy of the GNU Lesser General Public      *
*   License along with this program; if not, write to the               *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA02111-1307, USA.             *
***************************************************************************/

/*============================ INCLUDES ======================================*/
#include ".\app_cfg.h"

/*============================ MACROS ========================================*/
#ifndef SLEEP_REQ_SET
#warning No defined sleep request set: SLEEP_REQ_SET
#endif

#ifndef SLEEP_REQ_COUNT
#warning No defined sleep request number: SLEEP_REQ_COUNT
#define SLEEP_REQ_COUNT         (1u)
#endif

#if   SLEEP_REQ_COUNT <= 8
#   define SLEEP_REQ_WORD_COUNT   1
#elif   SLEEP_REQ_COUNT <= 16
#   define SLEEP_REQ_WORD_COUNT   2
#elif   SLEEP_REQ_COUNT <= 24
#   define SLEEP_REQ_WORD_COUNT   3
#elif   SLEEP_REQ_COUNT <= 32
#   define SLEEP_REQ_WORD_COUNT   4
#else
#error It dose not make any sense that there are more than 32 vote candidates!
#endif


/*============================ MACROFIED FUNCTIONS ===========================*/
//! brief add a new sleep request item to SLEEP_REQ_SET
#define ADD_SLEEP_REQ(__NAME)               uint32_t __NAME : 4;

#define __REQ_INIT(__COUNT, __VALUE)      (__VALUE),

/*============================ TYPES =========================================*/
//! \name core rum mode
//! @{
typedef enum {
    NORMAL_RUN_MODE               = 1,      //!< normal run mode: support full speed
    LOW_POWER_RUN_MODE            = 2         //!< low power run mode: support 4M max clock
}run_mode_t;
//! @}

//! \name sleep mode
//! @{
typedef enum {
    SLEEP_KEEP_CURRENT_RUN_MODE   = 0,      //!< keep current run mode   
    SLEEP_RUN                     = 1,      //!< normal run mode
    SLEEP_VLP_RUN                   = 2,      //!< very low power run mode
    SLEEP_NONE                      = 3,      //!< none sleep mode

    SLEEP_WAIT                      = 4,      //!< wait mode
    SLEEP_VLP_WAIT                  = 5,      //!< very low power wait mode
    SLEEP_STOP                      = 6,      //!< stop mode
    SLEEP_VLP_STOP                  = 7,      //!< very low power stop mode
#if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_LL_STOP                   = 8,      //!< low leakage stop mode
    SLEEP_DEEPEST_RECOVERABLE_SLEEP = 8,      //!< deepest recoverable sleep mode
#else
    SLEEP_DEEPEST_RECOVERABLE_SLEEP = 7,      
#endif
#if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_VLL_STOP_LV3            = 9,      //!< very low leakage stop mode 3
    SLEEP_VLL_STOP_LV2            = 10,      //!< very low leakage stop mode 2
    SLEEP_VLL_STOP_LV1            = 11,       //!< very low leakage stop mode 1
    SLEEP_VLL_STOP_LV0            = 12,       //!< very low leakage stop mode 0
    SLEEP_DEEPEST_SLEEP             = 12,
#elif CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
    SLEEP_DEEPEST_SLEEP             = 8,
#else
    SLEEP_DEEPEST_SLEEP             = 7,
#endif
    SLEEP_DO_NOT_CARE               = 0x0F      //!< don't care about the sleep mode
}sleep_mode_t;
//! @}

#define SLEEP_MODE_RUN          _BV(0)
#define SLEEP_MODE_STOP         _BV(1)
#define SLEEP_MODE_VLPR         _BV(2)
#define SLEEP_MODE_VLPW         _BV(3)
#define SLEEP_MODE_VLPS         _BV(4)
#define SLEEP_MODE_LLS          _BV(5)
#define SLEEP_MODE_VLLS         _BV(6)

/*============================ GLOBAL VARIABLES ==============================*/
//! \brief sleep request set
volatile union {
    uint32_t wRequests;    //!< sleep requests
#ifdef SLEEP_REQ_SET
    struct {
      SLEEP_REQ_SET
    }tMembers;
#endif
} g_tSleepRequests = { MREPEAT(SLEEP_REQ_WORD_COUNT, __REQ_INIT, 0xFFFFFFFF) };

/*============================ LOCAL VARIABLES ===============================*/

/*============================ PROTOTYPES ====================================*/
#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
static vsf_err_t try_to_run_as(sleep_mode_t tVoteResult);
#endif
static sleep_mode_t sleep_vote(sleep_mode_t tMode);

/*============================ IMPLEMENTATION ================================*/

/*! \brief initialize sleep service
*! \param none
*! \return none
*/
void sleep_init(void)
{
    /*Enable all operation modes because this is a write once register*/
    SMC_PMPROT = (
                #if CORE_SLEEP_VERY_LOW_LEAKAGE_STOP == ENABLED
                  SMC_PMPROT_AVLLS_MASK |
                #endif
                #if CORE_SLEEP_LOW_LEAKAGE_STOP == ENABLED
                  SMC_PMPROT_ALLS_MASK|   
                #endif
                  SMC_PMPROT_AVLP_MASK);
}


#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
/*! \brief enter very low power run mode
*! \param none
*! \retval true succeed
*! \retval false failed
*/
static bool enter_very_low_power_run_mode(void)
{
    /*! you need not check current run mode here before doing anything */

#ifdef ON_ENTERING_LOW_POWER_RUN_MODE
    /*! \note raise entering low power run mode event, you can change system
            clock setting in the event handler*/
    ON_ENTERING_LOW_POWER_RUN_MODE
#endif

    /*! try to enter very low power run mode */
    SMC_PMCTRL = (SMC_PMCTRL & ~SMC_PMCTRL_RUNM_MASK) | SMC_PMCTRL_RUNM(2);
   
    /*! wait REGONS cleared */
    while((PMC_REGSC & PMC_REGSC_REGONS_MASK));

#ifdef ON_CONFIRM_LOW_POWER_RUN_MODE
    /*! \note raise confirm low power run mode event, you can do necessary work
            here to handle the possible failure or initialize peripherals.*/
    ON_CONFIRM_LOW_POWER_RUN_MODE(
      (SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) );
#endif

    return true;
}


/*! \brief enter normal run mode
*! \param none
*! \return none
*/
static void enter_normal_run_mode(void)
{
    /*! you need not check current run mode here before doing anything */
#ifdef ON_LEAVING_LOW_POWER_RUN
    /*! \note raise leaving low power run mode event, you can change system
            clock setting here in the event handler */
    ON_LEAVING_LOW_POWER_RUN
#endif

    /*! enter normal run mode */
    SMC_PMCTRL &= ~SMC_PMCTRL_RUNM_MASK;

    /*! wait REGONS cleared */
    while((PMC_REGSC & PMC_REGSC_REGONS_MASK));

#ifdef ON_ENTER_NORMAL_RUN_MODE
    /*! \note raise enter normal run mode event, you can initialize peripherals
            here. */
    ON_ENTER_NORMAL_RUN_MODE
#endif
   
}
#endif

/*! \brief enter specified sleep mode
*! \param chMode sleep mode
*! \return none
*/
static void enter_sleep_mode(vsf_uint8_t chMode)
{
    const static uint8_t c_chMode[] = {
            {SMC_PMCTRL_STOPM(0x00), 0},                  //!< 3: wait
            {SMC_PMCTRL_STOPM(0x00), 0},                  //!< 4: vlpw
            {SMC_PMCTRL_STOPM(0x00), 0},                  //!< 5: stop
            {SMC_PMCTRL_STOPM(0x02), 0},                  //!< 6: vlps
            {SMC_PMCTRL_STOPM(0x03), 0},                  //!< 7: lls
            {SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(3)},//!< 8: vlls3
            {SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(2)},//!< 9: vlls2
            {SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(1)},//!< 8: vlls1
            {SMC_PMCTRL_STOPM(0x04), SMC_VLLSCTRL_VLLSM(0)} //!< 9: vlls0
      };

    SAFE_ATOM_CODE(
      //!< read SMC_PMCTRL
      uint32_t tTempPMCTRL = SMC_PMCTRL & ~(SMC_PMCTRL_STOPM_MASK |
                                                SMC_PMCTRL_LPWUI_MASK
                                          );

      //! check current run mode
      if (SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) {
            tTempPMCTRL &= ~SMC_PMCTRL_LPWUI_MASK;

            //! very low power run mode
            if ((SLEEP_STOP - SLEEP_WAIT) == chMode) {
            #if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
                /*! \note can not transfer from vlpr mode to stop mode directly
               *!       so enter normal run mode first
               */
                enter_normal_run_mode();

                //! update tempPMCTRL
                tTempPMCTRL &= ~SMC_PMCTRL_RUNM_MASK;
                tTempPMCTRL |= (SMC_PMCTRL & SMC_PMCTRL_RUNM_MASK);
                tTempPMCTRL |= SMC_PMCTRL_LPWUI_MASK;
            #else
                /*! \note can not transfer from vlpr mode to stop mode directly
               *!       so enter very low power stop instead
               */
                chMode = (SLEEP_VLP_STOP - SLEEP_WAIT);
            #endif
            }

      } else {
            //! normal run mode
            tTempPMCTRL |= SMC_PMCTRL_LPWUI_MASK;

            if ((SLEEP_VLP_WAIT - SLEEP_WAIT) == chMode) {
            #if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
                /*! \note normal run -> very low power wait
               *!       enter very low power run mode first. if it is failed
               *!       in entering very low power run mode, normal wait mode
               *!       will be entered.
               */
                enter_very_low_power_run_mode();

                //! update tempPMCTRL
                tTempPMCTRL &= ~SMC_PMCTRL_RUNM_MASK;
                tTempPMCTRL |= (SMC_PMCTRL & SMC_PMCTRL_RUNM_MASK);
            #else
               /*! \note normal run-> very low power wait was configured that
                *!       it's not supported here, so use normal wait instead.
                */
                chMode = (SLEEP_WAIT - SLEEP_WAIT);
            #endif
            }
      }

      //! set PMC CONTROL register
      tTempPMCTRL |= c_chMode;

      //! update SMC registers
      SMC_PMCTRL = tTempPMCTRL;
      SMC_VLLSCTRL = c_chMode;

      if (chMode < (SLEEP_VLP_STOP - SLEEP_WAIT)) {
            /*! \note Clear the SLEEPDEEP bit to make sure we go into WAIT (sleep) mode
             *      insteadof deep sleep.
             */
            SCB_SCR &= ~SCB_SCR_SLEEPDEEP_MASK;       
      } else {
            /*! Set the SLEEPDEEP bit to enable deep sleep mode (STOP) */
            SCB_SCR |= SCB_SCR_SLEEPDEEP_MASK;
      }
      ENABLE_GLOBAL_INTERRUPT();

    //! raise entering_sleep_mode event
    #ifdef ON_ENTERING_SLEEP_MODE
      ON_ENTERING_SLEEP_MODE((chMode + SLEEP_WAIT))
    #endif
      
    #if __IS_COMPILER_IAR__
      __WFI();
    #else
      __asm__ __volatile__ ("WFI");
    #endif

    //! raise wake_up_from_sleep_mode event
    #ifdef ON_WAKE_UP_FROM_SLEEP_MODE
      ON_WAKE_UP_FROM_SLEEP_MODE((chMode + SLEEP_WAIT))
    #endif

    )
}


/*! \brief enter specified sleep mode directly
*! \param tMode target sleep mode
*! \return access result
*/
vsf_err_t k20_core_sleep(sleep_mode_t tMode)
{
    if (tMode < SLEEP_WAIT) {
      return VSFERR_INVALID_RANGE;
    } else if ( tMode > SLEEP_DEEPEST_SLEEP) {
      tMode = SLEEP_DEEPEST_SLEEP;
    }

    //! enter sleep mode
    enter_sleep_mode(tMode - SLEEP_WAIT);

        return VSFERR_NONE;
}

/*! \brief sleep voting algorithm
*! \param tMode maximum allowed sleep mode
*! \return voting result
*/
static sleep_mode_t sleep_vote(sleep_mode_t tMode)
{
    vsf_uint8_t n = SLEEP_REQ_COUNT;
    vsf_uint8_t chIndex = 0;
    volatile uint32_t *pwRequest = g_tSleepRequests.wRequests;
    uint32_t wRequestWord;

    //! sleep volte algorithm
    do {
      vsf_uint8_t chTemp;
      if (!(chIndex & 0x03)) {
            SAFE_ATOM_CODE(
                wRequestWord = *pwRequest++;
            )
      }
      chTemp = (wRequestWord & 0x0F);
      if (chTemp < tMode) {
            tMode = (sleep_mode_t)chTemp;
      #if VSF_CORE_ALTERNATE_RUN_MODE != ENABLED
            if (tMode < SLEEP_WAIT) {
                break;
            }
      #endif
      }
      chIndex++;
    } while (--n);
    return tMode;
}


/*! \brief try to enter a voted sleep mode
*! \param none
*! \return access result
*/
vsf_err_t k20_core_try_to_sleep(void)
{
    sleep_mode_t tMode = sleep_vote(SLEEP_DEEPEST_SLEEP);

    if (tMode < SLEEP_WAIT) {
    #if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
      //! no sleep mode allowed
      try_to_run_as(tMode);
    #endif
      return VSFERR_NOT_AVAILABLE;
    }

    //! enter voted sleep mode
        return k20_core_sleep(tMode);
}

#if VSF_CORE_ALTERNATE_RUN_MODE == ENABLED
static vsf_err_t try_to_run_as(sleep_mode_t tVoteResult)
{
    SAFE_ATOM_CODE(
      do {
            if (tVoteResult >= SLEEP_NONE) {
                break;
            }

            
            //! get voting result
            sleep_mode_t tCurrentMode =
                            (SLEEP_MODE_VLPR == (SMC_PMSTAT & SMC_PMSTAT_PMSTAT_MASK)) ?
                            SLEEP_VLP_RUN : SLEEP_RUN;

            if (    (tVoteResult == tCurrentMode)
                /*||(SLEEP_NONE == tVoteResult)*/) {
                //! already run in the target mode, need to do nothing at all
                break;
            } else if (SLEEP_KEEP_CURRENT_RUN_MODE == tVoteResult) {
                //! no run mode changing is allowed
                EXIT_SAFE_ATOM_CODE();
                return VSFERR_NOT_AVAILABLE;
            }

            if (    (SLEEP_VLP_RUN == tVoteResult)
                &&(SLEEP_RUN ==tCurrentMode )) {
                //! enter normal run mode
                enter_normal_run_mode();
            } elseif (/*(SLEEP_RUN == (sleep_mode_t)tMode) ||*/
                  (SLEEP_VLP_RUN ==tCurrentMode )){
                //! enter very low power run mode
                enter_very_low_power_run_mode();
            }
            
      } while (false);
    )

    return VSFERR_NONE;
}

#endif


/* EOF */

Gorgon_Meducer 发表于 2012-9-13 17:15:13

占个楼~

Rocky_Zou 发表于 2012-10-23 22:29:04

顶一下      

xtx8962 发表于 2012-10-31 11:54:51

{:biggrin:}{:biggrin:}{:biggrin:}顶

sanliuyaoling 发表于 2012-12-7 14:42:04

顶!!!!!!!!!!!!!!!!!

Gorgon_Meducer 发表于 2012-12-10 20:17:04

本帖最后由 Gorgon_Meducer 于 2012-12-10 20:20 编辑

更新图片,和范例代码(未来得及严格测试,仅编译通过)

Gorgon_Meducer 发表于 2013-11-5 16:49:30

挖个古墓。送给Freescale板块

tyqhaha 发表于 2013-11-12 22:29:45

M4也这么多模式,还没注意,和M0+好像

jinyi7016 发表于 2015-2-23 21:34:12

学习好多,感谢分享
页: [1]
查看完整版本: [笔记]Freescal Cortex-M4 休眠模式备忘录